C经典笔记
C语言博大精深,很多看似简单的却暗含杀机
如果你感到你的C语言学的差不多了,那说明你的C一无所知:
下面就看过的C做一些总结,文中都是以图片的形式给出,也花费了我很多宝贵的时间
之所以用图片就是为了迫使大家自己在编译器上敲一遍代码
由于编译器和系统的差异,可能结果有点不一样,而且很多都没有解释,自己看,如有问题,下面评论留言::
<本文出自www.lfsblack.com>
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,在字符串中找出连续最长的数字串,返回长度,最长数字串放在outputstr
80,
81,
82,
windows下的结果:
83,
但是这个地方和系统和编译器有很大关系:在windows下结果如下:我们姑且这样认为(linux下默认4字节对齐)
84,
windows下的结果:
85,
windows下结果:
86,
windows下的结果:
87,
windows下结果:
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
C++程序中调用被C编译器编译后的函数,为什么要加extern "C"?
C++语言支持重载,C不支持。函数被C++编译后在库中的名字与C的不同。
C++提供了C链接交换指定符号extern "C"解决名字匹配问题
99,
头文件ifndef/define/endif有什么作用?
防止该头文件被重复引用
100,
编程去掉代码中的注释:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
void remove_comment(char *buf,size_t size) {
char *p,*end,c;
char *sq_start,*dq_start;
char *lc_start,*bc_start;
size_t len;
p = buf;
end = p + size;
sq_start = dq_start = lc_start = bc_start = NULL;
while(p < end) {
c = *p;
switch(c) {
case '\'':
if(dq_start || lc_start || bc_start) {
p ++;
continue;
}
if(sq_start == NULL)
sq_start = p ++;
else {
len = p ++ - sq_start;
if(len == 2 && *(sq_start+1) == '\\') {
continue;
}
sq_start = NULL;
}
break;
case '\"':
if(sq_start || lc_start || bc_start ) {
p ++ ;
continue;
}
if(sq_start == NULL)
dq_start = p ++;
else {
if(*(p ++ -1) == '\\')
continue;
dq_start = NULL;
}
break;
case '/':
if(sq_start || dq_start || lc_start || bc_start) {
p ++;
continue;
}
c = *(p+1);
if(c == '/') {
lc_start = p;
p += 2;
}else if(c == '*') {
bc_start = p ;
p += 2;
} else
p ++;
break;
case '*':
if(sq_start || dq_start || lc_start || bc_start == NULL) {
p ++;
continue;
}
if(*(p+1) != '/') {
p ++;
continue;
}
p += 2;
memset(bc_start, ' ',p - bc_start);
bc_start = NULL;
break;
case '\n':
if(lc_start == NULL) {
p ++;
continue;
}
c = *(p-1);
memset(lc_start,' ',(c == '\r' ? (p ++ -1) : p++) - lc_start);
lc_start = NULL;
break;
default:
p ++;
break;
}
}
if(lc_start)
memset(lc_start,' ' ,p - lc_start);
}
int main(int argc, char *argv[]) {
int fd,n;
char buf[102400];
fd = open("1.txt",O_RDONLY,0);
if(fd == -1)
return -1;
n = read(fd,buf,sizeof(buf));
if( n == -1 || n == 0) {
close(fd);
return -1;
}
remove_comment(buf,n);
*(buf + n) = '\0';
printf(buf);
close(fd);
return 0;
}
测试文件1.txt:
测试结果:
101,
用一个宏,求一个结构体里某个变量相对于struct的偏移量
#define FIND(struc,e) (size_t)((struct *)0)->e)
102,
用预处理定义一个常数,用以表明1年中有多少秒
要考虑有可能溢出,因此用UL
#define SECOND_PER_YEAE (60*60 * 24 * 365)UL
103,
用宏返回两个数中较小的一个
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
104,
const在C中有什么用途?
1,可以定义const常量
2,const可以修饰函数的参数和返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性
105,
const和#define区别?
const常量有数据类型,宏没有。编译器可以对前者进行 类型安全检查,而后者只能进行字符替换,并且替换中还有可能发生意想不到的错误
有些集成化的调试工具对const常量进行调试,但是不能对宏常量进行调试。在C++中const完全取代了宏常量
106,
常量引进在早期的C++版本,当时标准C规范正在制定。那时,常量被看做一个好的思想而被包含在C中。但是,C中的const的意思是“一个不能被改变的普通变量”。在C中,它总占用
内存,而且名字的全局符。C编译器不能把const看成一个编译期间的常量。在C中如果写:
但是可以这样写: const bufsize;
而这种写法在C++中是不允许的。C编译器则把它作为一个声明,这个声明指明在别的地方内存分配。因为C默认const是外部连接的,C++默认const是内部连接的,这样,如果C++
中想完成与C中同样的事,必须用extern把内部连接改成外部连接:
extern const buffer;
107,
108,
109,
110,
111,
#include <iostream>
using namespace std;
class A1 {
public:
int a;
static int b;
A1();
~A1();
};
class A2 {
public:
int a;
char c;
A2();
~A2();
};
class A3 {
public:
float a;
char c;
A3();
~A3();
};
class A4 {
public:
float a;
int b;
char c;
A4();
~A4();
};
class A5 {
public:
double d;
float a;
int b;
char c;
A5();
~A5();
};
void main() {
cout << sizeof(A1) << endl << sizeof(A2) << endl << sizeof(A3)
<< endl << sizeof(A4) << endl << sizeof(A5) << endl;
}
112,
sizeof和strlen的区别?
1>sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小
2>sizeof是运算符,strlen是函数
3>sizeof可用类型做参数,strlen只能用char *做参数,且必须是以'\0'结尾,sizeof还可以用函数做参数
4>数组作为sizeof参数不退化,传递给strlen就退化为指针
5>大部分编译程序编译的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组的尾数原因
6>strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小
7>sizeof后如果是类型必须加括号,如果是变量名可以不加括号。这是因为sizeof是个操作符而不是个函数
8>当使用一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof返回全部数组的尺寸。sizeof操作符不能返回动态
分配的数组或外部的数组的尺寸
9>数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址
10>sizeof操作符不能用于函数类型、不完全类型或字段。不完全类型指具有未知存储大小数据的数据类型,如:未知存储大小的数组类型,未知内容的结构或联合类型,void类型
113,
sizeof的使用场合:
1>sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信
2>用它可以看看某种类型的对象在内存中所占的单元字节
3>在动态分配一个对象时,可以让系统知道分配多少内存
4>便于一些类型的扩充,在windows中有很多结构类型就有一个专用的字段用来存放该类型的字节大小
5>由于操作数的字节数在实现时可能会出现变化,在涉及操作数字节大小时也用sizeof代替常量计算
6>如果操作数是函数中的数组形参或函数类型的参数,sizeof给出其指针的大小
114,
115,
116,
117,
118,
119,
120,
121,
交换:
#include <iostream>
using namespace std;
void swap1(int p,int q) {
int temp ;
temp = p;
p = q;
q = temp ;
}
void swap2(int *p,int *q) {
int *temp ;
*temp = *p;
*p = *q;
*q = *temp;
}
void swap3(int *p,int *q) {
int *temp ;
temp = p;
p = q;
q = temp;
}
void swap4(int *p,int *q) {
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swap5(int &p,int &q) {
int temp;
temp = p;
p = q;
q = temp;
}
int main() {
int a = 1,b = 2;
swap1(a,b);
cout << a << '\t' << b << endl;
// swap2(&a,&b); //有错误
// cout << a << '\t' << b << endl;
swap3(&a,&b);
cout << a << '\t' << b << endl;
swap4(&a,&b);
cout << a << '\t' << b << endl;
swap5(a,b);
cout << a << '\t' << b << endl;
return 0;
}
结果:只有后两个可以实现
122,
上面程序崩溃。因为GetMemory并不能传递动态申请的内存,str一直都是NULL
123,
124,
125,
126,
127,
在main函数中可以不写return语句,因为编译器会隐式返回0
删除一个指针后,把它设置为空指针(0)
128,
int (*(*f)(int,int))(int)
f是一个函数指针,指向的函数类型是有两个int参数并且返回一个函数指针的函数,返回的函数指向一个int参数且返回int的函数
129,
130,
131,
132,
133,
134,
auto_ptr指针
135,
136,
137,
138,
139,
140,
141,
142,
143,
144,
统计字符串中,各个字符出现的次数:
145,
146,
C++代码实现,输入n输出nXn矩阵,规定沿45度线递增,形成一个zigzag数组(JPEG编码里取像素数据的排列顺序):
147,
两个等长数组A、B,所含元素相同,但顺序不同,只能取A数组某值和B数组某值进行比较,比较结果为大于,小于或等于。但是不能取同一数组两个值进行比较,也不能取得某数组中的某个值。写一个匹配算法(即A数组中某值与B数组中某值等值)
148,
标准模板库是一个基于模板的容器类库
容器是包容其他对象的对象
149,
从最上面输出结果看,两次调用析构函数
150,
C++的空类默认产生4个函数,默认构造函数,析构函数,拷贝构造汉斯,赋值函数
C++中struct和class默认访问权限是唯一区别,可以有构造和析构函数
类静态成员必须初始化赋初值!!!
151,
可见 上面第一个例子之所以错,是因为编译器把Test b();当作一个函数声明了
152,
153,
从上例子中我们可以看到析构函数可以是内敛的
154,
155,
封装可以隐藏实现细节,使得代码块化
继承可以扩展已存在的代码模块,目的是为了代码重用
多态是为了实现 接口重用
156,
结果:
结果:
157,
结果:
158,
159,
如果一个圆角矩形有直边和圆角,那么它也就多重继承了圆形和矩形,而圆形和矩形又都是从shape类里继承。问:当创建一个圆角矩形使,共创建多少shape?
答曰:如果圆形类和矩形类都不是virtual继承shape类,那么生成两个shape,一个为圆形类,一个为矩形类。
如果圆形类和矩形类都是virtual继承shape类,那么生成一个共享的shape
160,
结果:
161,
162,
163,
虚指针或虚函数指针是带有虚函数的类中,一个对象都有一个虚指针指向该类的虚函数表
C++如果阻止一个类被实例化?
使用抽象类或者构造函数被声明为private
一般在什么时候构造函数被声明成private?
要阻止编译器生成默认的copy constructor的时候
什么时候编译器会生成默认copy constructor?
只要自己没写,而程序中需要,就会生成
如果已经写了一个构造函数,编译器还会生成copy constructor么?
会
164,
虚函数的入口地址和普通函数有什么不同?
每个虚函数都在vtable中占了一个表项,保存着一条跳转到它入口地址的指令(实际上就是保存了它的入口地址)。当一个包含虚函数的对象(不是对象的指针)被创建
的时候,它在头部附加一个指针,指向vtable中相应的位置。调用虚函数的时候,不管你是用什么指针调用的,它先根据vtable找的入口地址再执行,从而实现了“动态联编”
而不像普通函数那样简单的跳转到一个固定的地址
165,
166,
结果:
167,
一个C++程序员想要运行一个static_cast<char *>()。为了能够确保合法性。
应该增加函数 operator char*();
168,
C++中,typeid运算符的返回值为:type_info常量对象的引用
169,
170,
171,
172,
173,
174,
175,
176,
177,
178,
179,
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
180,
181,
182,
183,
求补码:(貌似有问题)
unsigned short get(short x )
{
short y ;
y = x >> 15 ;
return ((x^y)-y)|(y<<15);
}
184,
185,
186,
187,
全局变量放在 数据段
函数内部变量static int ncount 放在 数据段
函数内部变量char *p = "AAA",p放在 堆栈
指向的空间放在 数据段
函数内变量char *p = new char,p放在 堆栈
指向空间放在 堆
188,
189,
190,
191,