Understanding and using c pointers 第二章读书笔记


tonybai在他的网站上写了一个本书的知识点纲要

第二章 C语言中的动态内存管理

C99引入了可变长度数组(VLAs),数组长度在运行时决定,然而一旦创建后数组就不会再改变其长度。

C语言中的动态内存管理分为如下几步:
1.使用malloc函数分配内存
2.使用这块内存来做点事情
3.完后使用free函数回收分配的内存

如下:
int *pi = (int*) malloc(sizeof(int));
*pi = 5;
printf("*pi,%d\n",*pi);
free(pi);

常见错误的方式如下
int *pi,
*pi = (int *) malloc(sizeof(int));
*pi是指针pi所指向的地址里存贮的值,此时指针pi还没有初始化,很可能发生错误
正确的应该是pi = (int*) malloc(sizeof(int));

内存释放后不应该再访问,但是有时候还是会无意访问,最好在释放内存后将指针置为NULL

当内存分配后,有关附加信息作为数据结构的一部分由堆管理器维护。这些信息包括内存块大小等信息,它们
通常被放置在紧邻所分配的内存块,如果应用程序越界访问该内存块,则原有的数据结构可能会被破坏掉,导
致程序运行不正常运行或崩溃,比如下面的例子
char *pc = (char*)malloc(5 + 1);
for(int i = 0; i < 8; i++)
{
	*pc[i] = 0;
}
上述例子是要申请一个5个char的字符串+1是表示字符串结束符NUL,但是for循环进行了8次,越界访问了。

内存泄露
1.只分配,没回收
char *chunck;
while(1)
{
	chunck = (char*)malloc(1000000);
	printf("Allocating\n");
}
2.地址丢失
int *pi = (int *) malloc(sizeof(int));
*pi = 5;
...
//内存泄露,之前分配的还没有回收,指针值却改变指向了另外一块内存,之前分配的没被回收
pi = (int *) malloc(sizeof(int));

char *name = (char *) malloc(strlen("susan") + 1);
strcpy(name,"susan");
while(*name != 0)
{
	printf("%c\n", *name);
	name++;//name++破坏了原有内存块的地址,造成了内存内存泄露
}
回收由struct定义的结构时也可能发生内存泄露,如果结构含有动态分配的内存,那么在回收结构之前先要将
这些动态分配的内存释放

动态内存分配函数
在大多数的系上,stdlib.h包里的头文件定义了如下动态内存分配函数
malloc 从堆分配内存
realloc 根据之前分配的内存块大小,重新分配更大或者更小的内存块
calloc 从堆中分配内存并将将分配的内存块全部置零 (allocates and zero out memory from the heap)
free 将内存块返还给堆

malloc函数原型如下:
void* malloc(size_t);
所以传递参数时要特别注意不能传负数,因为size_t是一个无符号数,很多系统上传负数会返回NULL指针,我在gcc上测试是这样的。
传递0的话会根据系统而不同,有的会返回NULL有的会返回一个指向0字节的指针

you cannot use a function call when initializing a static or global variable.In the following code sequence,we declare a static
variable and then attempt fo initiazlize it using malloc:
	static int *pi = malloc(sizeof(int));
this will generate a compiler-time error message.

对于静态变量,像上面直接初始化,编译器会报错(gcc)
但是分开就ok 如下:
static int *pi;
pi = malloc(sizeof(int));
对于全局变量,直接初始化与分开初始化都会报错

calloc函数原型如下:
void *calloc(size_t numElements,size_t elementSize);

使用方式如下:
int *pi = calloc(5,sizeof(int));
使用malloc和memset能够达到同样的效果如下:
int *pi = malloc(sizeof(int) * 5);
memset(pi,0,5 * sizeof(int));/-
calloc可能要被malloc慢一些

realloc函数原型如下
void *realloc(void *ptr,size_t size);
根据参数不同,其结果调用如下

Understanding and using c pointers 第二章读书笔记_第1张图片

free函数原型如下
void free(void *ptr);
如果给free传递null指针,则什么也不做
如果传递的指针不是malloc类型的函数分配的指针,那么该函数的行为是不确定的。
如下,pi被赋予了num的地址,然而它不是一个合法的堆地址
int num;
int *pi = #
free(pi); //undefined behaviour

重复释放的同一块内存会造成异常
如下
int *pi = (int *) malloc(sizeof(int));
*pi = 5;
free(pi);
...
free(pi);
或者
p1 = (int *) malloc(sizeof(int));
int *p2 = p1;
free(p1);
...
free(p2);	//这种其实也是多次释放,作者叫指针别名
当两个指针指向相同的地址,it is referred to as aliasing.

堆管理器无需将free掉的内存返还给操作系统,回收的内存可能会被应用程序再次使用

悬挂指针问题:
内存访问时的不确定行为
内存不可访问时造成段错误
潜在的安全问题

悬挂指针例子:

int *pi = (int*) malloc(sizeof(int));
*pi = 5;
printf("*pi: %d\n", *pi);
free(pi);//pi依然指向int变量的地址

int *p1 = (int *)malloc(sizeof(int));
*p1 = 5;
...
int *p2;
p2 = p1;
...
free(p1);
...
*p2 = 10; //悬挂指针

代码块声明也能造成悬挂指针
int *pi;
...
{
	int tmp = 5;
	pi = &tmp;
}
//pi现在成了悬挂指针,因为上述大括号代码块执行完毕后就弹出了程序栈
foo();

处理悬挂指针
1.内存释放后将指针置为NULL,但这不能解决指针别名带来的问题
2.编写代替free函数的函数
3.使用特殊值如VS里的0xCC,0xCD等等
4.使用第三方工具


你可能感兴趣的:(Understanding and using c pointers 第二章读书笔记)