1、 三种内存分配方式?
这是最基本的知识。
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的 整个运行期间都存在。例如全局变量,static 变量。 (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函 数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多 少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
?
2、易错点
(1)?内存分配未成功,却使用了它。
应在使用前先检查是否为NULL, 如果在函数中则可用assert来判断:assert(p != NULL);
(2)?内存分配虽然成功,但是尚未初始化就引用它。
(3)?内存分配成功并且已经初始化,但操作越过了内存的边界。最常见的就是数组。
(4)?忘记了释放内存,造成内存泄露。
动态内存的申请与释放必须配对, malloc后记得 free,new后记得delete。
(5)?释放了内存却继续使用它。比较难发现的一个错误是用return返回指向“栈内存”的“指针”或者“引用” ,但该内存在函数体结束时就被自动销毁了。
(6)使用 free 或 delete 释放了内存后,要将指针设置为 NULL,否则会导致产生“野指针”
(7)不要忘记为数组和动态内存赋初值,防止将未被初始化的内存作为右值使用。
?
3、指针与数组
很多人都认为指针和数组是等价的,其实不然。
数组?要么在静态存储区被创建 (如全局数组) , 要么在栈上被创建。 数组名对应着 (而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。 指针?可以随时指向任意类型的内存块,它的特征是“可变” ,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
?
?
char a[] = “hello”; a[0] = ‘X’; cout << a << endl; char *p = “world”; // 注意 p 指向常量字符串 p[0] = ‘X’; // 编译器不能发现该错误 cout << p << endl;
从上面这个例子可以看出, 指针 p 指向常量字符串“world” (位于静态存储区,内容为 world\0) ,常量字符串的内容是不可以被修改的。?
注意,不能把数组a的内容用b=a的方式赋值给数组b,因为这会产生编译错误。应该用strcpy 进行复制。
而if(p==a) ?比较的不是内容而是地址,应该用库函数 strcmp来进行比较。
下面是它与指针在这些方面的差别。
?
// 数组… char a[] = "hello"; char b[10]; strcpy(b, a); // 不能用 b = a; if(strcmp(b, a) == 0) // 不能用 if (b == a) … // 指针… int len = strlen(a); char *p = (char *)malloc(sizeof(char)*(len+1)); strcpy(p,a); // 不要用 p = a; if(strcmp(p, a) == 0) // 不要用 if (p == a) …
?
?4、计算内存容量。
这一点在面试时可以考倒一大堆人。
sizeof 可以计算出数组的容量(字节数),包括'\0'。如果参数是一个指针的话,那么返回的是指针的长度:4 。。 C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
还有一点:当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。看几个例子:
?
char a[] = "hello world"; char *p = a; cout<< sizeof(a) << endl; // 12 字节 cout<< sizeof(p) << endl; // 4 字节
void Func(char a[100]) { cout<< sizeof(a) << endl; // 4 字节而不是 100 字节 }?
?
?