1、不同数据类型的指针可以通过用void类型进行比较。
例如:
int i_a = 2;
void *vo_t1,*vo_t2;
double d_b = 1;
vo_t1 = &i_a;
vo_t2 = &d_b;
if(vo_t1 == vo_t2)
printf("不同数据类型的指针可以通过用void类型进行比较!!\n");
else
printf("不同数据类型的指针可以通过用void类型进行比较!!\n");
2、不同数据类型的指针不能通过void来相互交换值。
3、不能使用NULL来作为字符串的结束。(NULL是指针)
4、可以“int *p=0;”,此时编译器把0当做一个NULL指针
Because:
#define NULL ((void*)0)
5、搞清楚:memset()函数,和calloc()函数的用法。
6、‘a’它的数据类型并不是char而是int。
7、*(p+i)等于*(i+p),也就是说p[i]等于i[p]
例如:
int a[10]={0};
int *p;
int i;
for(i=0,p=a;i<10;i++)
printf("%d ",p[i]);
puts("");
for(i=0,p=a;i<10;i++)
printf("%d ",i[p]);
puts("");
8、存在a[10];
例如:
for(i=0,p=a;i<10;i++)
printf("%d ",*(p+i));
i++;
printf("\n%d",i[p]);
9、*p要比*(p+i)更高效
例如:
for(p=a;p<&a[10];p++)
printf("%d ",*p);
puts("");
for(i=0,p=a;i<10;i++)
printf("%d ",*(p+i));
Because:*(p+i)每循环一次就要计算一次,
10、下标运算符[]和数组是没有关系的,[]只不过是为了人类写法方便,不需要写成*(p+i), 这样的输入工作量会比较大
11、C对数组的长度范围是不做检查的,所以当数组越界写入数据的时候,经常产生“内存被 破坏”的问题。
12、无论如何都要讲数组进行值传递的时候,建议将数组整体整理成结构体成员。
13、当数组出现在表达式中的时候,它会立刻解读成指针,也就是说原先的a[i]会被解读成*(a+i)
14、当今的操作系统都会给应用程序的每一个进程分配独立的“虚拟地址空间”,这是cpu和操 作系统系统工作的结果。所以就算我们毛手毛脚,糊里糊涂的制造了一个bug,破坏了某个 内存区域,顶多就是让当前的应用程序趴窝,但不会影响其它进程。
15、当然了,真正保存内存数据的还是物理内存。操作系统负责将物理内存分配给虚拟地址空 间,同时还对每一个内存区域设定“只读”或者“可读写”等属性
16、在如今的运行环境中,应用程序面对的是虚拟地址空间。
17、搞清fflush()函数,和sscanf()函数
18、全局变量可以在其他文件中使用,静态变量(static)只能在本程序中使用.
19、局部变量通常在它锁在的语句块结束的时候被释放,如果不想释放某个局部变量,可以在局部变量的前面加上static
20、C语言变量除了作用域不同,还有存储期的差别。
21、静态变量:寿命从程序运行时开始,到程序关闭时结束。
22、自动变量:寿命到声明该变量的语句块被执行结束为止。(没有加static的局部变量,栈的机制)
23、通过malloc()分配的领域:寿命直到调用free()为止。
24、常量也是有地址的。
例如:printf("%p %p",9,"abc");
25、自定义函数的自动变量(局部变量)分配了完全相同的内存地址
例如:void func1(void)
{
int a;
printf("%p......func1a\n",&a);
}
void func2(void)
{
int f;
printf("%p......func2f\n",&f);
}
倘若每个函数里面同时增加变量:
void func1(void)
{
int a,b,c;
printf("%p......func1a\n",&a);
printf("%p......func1b\n",&b);
printf("%p......func1c\n",&c);
}
void func2(void)
{
int f,k,o;
printf("%p......func2f\n",&f);
printf("%p......func2k\n",&k);
printf("%p......func2o\n",&o);
}
函数1和函数2的地址会一一对应。
27、main()函数不同于上面。
28、函数和字符串常量在内存地址上存放的很近。它们汇总配置在只读内存区域的。
29、字符串常量相当于一个数组,
例如:
char *str="abc";
printf("%s.....str",str);
printf("%c.....str[0]",str[0]);
printf("%c.....str[1]",str[1]);
printf("%c.....str[2]",str[2]);
是可以的。
30、函数可以在表达式中被解读成“指向函数的指针”,
例如:
Void func1(void)
{
Printf(“%p ”,func1);
}
31、表达式中的数组可以解读成“指向初始元素的指针”。
32、静态变量总是在虚拟地址空间上占有固定的区域。
33、自动变量的地址是在运行时被决定的。
34、c语言通常将自动变量保存在栈中。
35、如果原型声明的函数中出现...,对于这部分的参数是不会做类型检查的。
例如:
void tiny_printf(char *format,...)
36、assert.h头文件中有一个assert(表达式)函数,如果表达式为真,什么也不会发生,相反,就 会强制终止程序。
37、什么是stderr,和srtbuf()?
38、利用递归运算来快速排序,会有很高的效率。
39、可以利用宏来交换两个数的值,
例如:
#define SWAP(a,b){int t;t=a;a=b;b=t;}
但是这些语句必须写在同一行上面,否则会出错。
40、自动变量前,如果加上一个static作为静态变量分配内存区域,就可以在程序的执行前被完 全初始化。
41、倘若一个字符串的长度是你不知道的,那么你可以定义为:char *ch_p;只要你定义成了指 针类型,那样也不会浪费内存空间。
例如:
char *ch_p;
int len;
printf("please input number:");
scanf("%d",&len);
ch_p=(char *)malloc(sizeof(char)*len);
printf("please input %d character:",len);
scanf("%s",ch_p);
printf("%s\n",ch_p);
但是这里会有很多疑问:当len的长度不够时,数据依然能够完好的保存,并且程序也能够正常的结束。不知道bug出现在哪里。
42、malloc()函数是堆
43、Malloc()大体的实现是,从操作系统一次性的取得比较大的内存,然后将这些内存“零 售”给应用程序。
44、Malloc()函数的基本原理:
1、最单纯的实现方式----通过链表实现:
Malloc()遍历链表寻找到空的块,如果发现尺寸大小能够满足使用的块,就分割出来将其变成使用中的块,并且向应用程序返回紧邻管理区域的后面区域的地址。Free()将管理区域的标记改写成“空块”,顺便也将上下空块合并成一块。这样可以防止块的碎片化。
如果不够大的空块(malloc()需要的大小),就请求操作系统对空间进行扩容(使用系统调用)。
那么,在这种内存管理方式的运行环境下,一旦数组越界检查发生错误,越过malloc()分配的内存区域写入了数据,又会发生什么?
此刻将会破坏下一个块的管理区域,所以从此以后的malloc()和free()调用中出现程序崩溃的几率会非常高(这并不是库(malloc)中的bug)。
2、现实中的处理环境是不会这样单纯的实现malloc()的功能的。
比如,作为内存管理方法,除了这里说明的链表方式以外,还有一个被大家熟知的“buddly block system”方法。这种将大的内存逐步对半分开的方式,虽然速度很快,但会造成内存的使用效率低下。
44、malloc()绝对不是一个魔法函数。对于malloc()的动作原理,如果不是非常了解,就 很有可能陷入程序不能正常进行调试的窘境,或者常常写出非常低效的程序。
45、一般来说调用free()之后,对应的内存区域是不会立刻返还给操作系统的。
仓促的调用free是有问题的,就算调用了free,指针B(指针A释放空间,B则还在继续用)引用的内存区域也不会立刻被破坏,暂时还保持着以前的值,直到在某个地方执行malloc,随着当前内存区域被重新分配,内容才开始被破坏。这样的bug,从原有产生到bug被发现之间周期比较长,因此给程序调试带来了很大困难。
46、我们无法直到当前指针指向的区域大小。
47、C语言将虚拟地址直接交给了应用程序,库的一方是不能随意移动内存区域的。
48、指针不能直接赋常量,但是可以通过malloc先分配内存,然后再赋值常量。
49、自动变量重复使用内存区域,因此,自动变量的地址是不一定的。(现在就可以解释25那 里为撒子不同函数中的自动变量地址会一一对应)。
50、fprintf(stdin,””,...);=scanf(“”,...);
51、fprintf(stdout,””,...)=printf(“”,...);
52、不管什么函数的自动变量,还有返回地址和值都是放在栈中,并且他们的距离不远,所以 一旦数据越界的话,很可能影响该函数的返回地址,更严重的是,还有可能改写其它函数 中的自动变量。而函数不是和它们放在一起的。
53、如果你调用了malloc(),就必须做返回值检查。
54、即使手工进行布局对其,也不能提高可移植性。
55、对于cpu来说采用小端字节序方式会更轻松。
56、一旦出现bug,就要带着“指针就是地址”的观点去解决它。