数组与指针是内存管理的关键,也是C/C++中较难掌握的的地方。现在对其进行一些分析与总结。
1.从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
整个运行期间都存在。例如全局变量,static 变量,即使是局部static变量(即函数内部定义的static变量)。
如:
const char *func()
{
static char res[256]={0};
...
return res; // this is correct.
}
2.在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
中,效率很高,但是分配的内存容量有限。
const char *func()
{
char res[256]={0};
...
return res; // this is wrong.
}
3.从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期
由我们决定,使用非常灵活,但问题也最多。堆内存的释放必须用free或delete显式释放,
否则要等到程序结束后由操作系统负责收回。
const char *func()
{
char* res=new char[256];
...
return res; // this is correct.
}
4.文字常量区。常量就放在这里,即使是局部定义的常量也放在文字常量区,内容不可以被更改,如常量字符串,一直到程序结束后才由系统释放。例如:
const int* func(void)
{
const int x=2;
return &x;
}
const int *y=func();
cout<<*y<<endl;// result is 2.
1.数组长度必须在编译时确定;
2.数组长度不可变;
3.数组存在于栈中,只在定义它的语句块内存在;
4.数组名表示数组第一个元素的地址(它也是指针,也是迭代器,但只能作为右值),但是用sizeof操作符计算其大小是该数组所占的字节数,而不是数组名本身的字节数,唯一的例外是当数组作为函数的参数传递时,数组名自动退化为同类型的指针。例如:
int a[10];
cout<<sizeof(a)<<endl;// result is 40
void func(int a[10])
{
cout<<sizeof(a)<<endl;//result is 4
}
1.数组长度在编译时不需要知道;
2.一旦分配,数组长度不可变;
3.存在堆中,除非显示地释放,它一直存在,即使程序已经结束;
4.无数组名,只有返回的指针。该指针跟一般指针完全一样,如用sizeof 操作符计算其大小,得到是该指针所占内存(32位系统中,指针占4位),而不是其指向的数组所占的内存。如:
int a[10];
int *p=a;
cout<<sizeof(p)<<endl;// result is 4
p=new int[10];
cout<<sizeof(p)<<endl;// result is 4
C/C++中无法知道指针所指内容的内存容量,而只能知道指针本身所占的内存。
sizeof( 指针名)永远为4(在32位系统中),而不管指针指向的是不是数组,指向的内存在堆内还是在栈内。
1.C风格字符串:
以空字符null结束的字符数组即为C风格字符串。
2.字符串字面值:
(1)类型:const char*,即不可更改的字符串。
(2)存储位置及生命期:存储在文字常量区,一直到程序的结束。
(3)与数组关系:可以初始化(const char 或char型)数组,实质就是拷贝初始化,因此初始化后数组与文字常量区中的该字符串字面值无关了。
如:char a[]="hello world";
a[0]='s';// ok
又如:
char *GetString(void)
{
char p[] = "hello world";
return p; // 编译器将提出警告
}
char *str = NULL;
str = GetString(); // str 的内容是垃圾,注意函数返回后,str不为NULL,但其指向的栈内存已经被释放了。
(4)与指针关系:可以初始化或赋值给const char* 或char* 类型的指针,此指针指向的是文字常量区中的字面值常量的地址,因此不可以通过该指针修改其内容,
而且其内容和地址知道程序结束均不变。
如:char *p="hello world";
p[0]='s';// error
char *GetString(void)
{
char *p = "hello world";
return p;
}
char *str = NULL;
str = GetString(); // str 的内容是"hello world", 但注意str指向的是文字常量区里字面值的指针。
3.C风格字符串的标准库函数:
标准库strting.h里的以str开头的函数均是C风格字符串的标准函数。
这些函数要求C风格字符串:以NULL结尾的字符数组,接受char型数组名或char*型指针。
4. strlen与sizeof 的区别:
(1)strlen 只接受char* 型指针或char型数组名,而sizeof 接受任何类型的对象或任何类型名;
(2)strlen对char* 指针和char型数组名是完全一样的,而sizeof 对指针和数组名是不一样的:对指针结果为4,对数组名结果是该数组所占的内存空间;
(3)strlen计算C风格字符串的长度,不包括最后的null字符,而sizeof对char型数组名计算的长度包括最后的Null字符。