字符数组和字符指针可谓是我学C语言时最让人迷惑的了,下面让我娓娓道来!
int main()
{
char * ter = "terrific"; //这是一个赋予ter地址的过程
char * exc = "excellent";
printf("%p\n", ter); //将打印出"terrific"的内存地址
printf("%p\n", exc); //将打印出"excellent"的内存地址
return 0;
}
字符指针变量ter和exc被分别赋值为"terrific"和"excellent"变量的内存地址,让ter和exc有了归宿,指向了具体的内存区域。理解了这一点,就可以继续往下看了。int main()
{
char * poem = "Rain is falling all around";
printf("%p\n", poem); //静态存储区地址
char str[] = "It falls on field and tree";
printf("%p\n", str); //栈中的地址
printf("%p\n\n", &str[0]);
str[3] = 's'; //合法语句
//poem[3] = 's'; 此语句非法
printf("%c\n", *(++poem));
//printf("%c\n", *(++str)); str不能被改变,语句非法
printf("%c\n", *(str+3));
poem = str; //str是常量,将常量赋值给一个指针变量poem合法
/*
str = poem;
char ctr[30];
ctr = "two birds with one stone"; //这些都是错误的,数组常量是不能作为左值的
*/
return 0;
}
上面的程序显示结果是:
poem这种字符指针指向的字符串位于静态存储区,该内存块为只读,另外poem是指针类型,poem的值可以被改变。
str是在栈上分配的内存,main函数结束后会自动释放,str不是一个指针类型,充其量可以称他是数组类型,str是一个常量,保存着数组中第一个字符的地址,可以把它当做一个指针来用,但是不能改变其值。(从结果显示图中,可以发现栈和静态存储区分配地址的方式是不一样的)
综上所述,对于poem[3] = 's';这条语句的非法,就可以解释为:字符数组保存的字符串存放在内存的栈中,而字符指针指向的字符串保存在内存的静态存储区中,这一块内存是只读内存区域。所以字符数组保存的字符串属于字符串变量,可以被修改,而字符指针指向的字符串是属于字符串常量,不能被修改,这个类似数组名是常量不能被修改一样。
Next, 来看看字符指针指向一个堆中动态内存的情况:
int main()
{
char * tai = (char*)malloc(sizeof(char)*50);
printf("%p\n", tai); //tai在堆中内存首地址
tai = "when day is done";
printf("%p\n", tai); //tai在静态存储区内存首地址
char * taige = "if birds sing no more";
printf("%p\n", taige); //tai在静态存储区内存首地址
return 0;
}
运行结果:
tai指针是存储在栈中的,但它的内容是malloc后的堆内存的地址。由于tai指向的地址内容是用malloc在堆上分配的,它的生命周期是动态的,要由程序员手动用free来释放。
如果没有释放,则会在程序结束才被释放,也就是在free之前,该空间一直存在。
发现没有,tai指针变量被赋一个字符串后,地址却变了,说明tai被重新赋值了,指向了静态存储区的地址。那将这个保存字符串地址的变量tai设为const呢?const的意思就是让tai本身的值不能被改变。看下一段代码:
int main()
{
char * const tai = (char*)malloc(sizeof(char)*50);
if (tai == NULL)
{
printf("内存分配出错!");
exit();
}
printf("%p\n", tai); //tai在堆中内存首地址
//tai = "when day is done"; 编译出错,tai是一个只读变量,只能是堆中的那个地址,不能重新给tai一个静态存储区的一个地址
tai[0] = 't';
*(tai+1) = 'h';
tai[2] = 'e';
*(tai+3) = 'b';
*(tai+4) = '\0'; /*给字符串一个结束的标志因为malloc在动态分配完内存后,
不会初始化内存,里边数据是随机的垃圾数据。*/
printf("%s\n", tai);
printf("%p\n", tai);
free(tai); //为了防止垃圾数据,亲们最好记得malloc之后free一下
return 0;
}
运行结果:
可以往堆中分配的内存写入数据了,大伙是不是有点激动!上面的程序也是动态分配内存的字符数组进行初始化的一种方式。
再来对比一个字符数组的例子:
int main()
{
char tai[20];
tai[0] = 't';
*(tai+1) = 'h';
tai[2] = 'e';
*(tai+3) = 'b';
//tai[4] = '\0'; 没有加上手动附上'\0'时,会把垃圾数据给输出来
printf("%s\n", tai);
printf("%p\n", tai);
}
看结果:
在定义字符数组时,我们可以进行初始化,防止出现注意不到的错误,如:char tai[20] = {'\0'}。
更多知识请看:
http://www.cnblogs.com/GODYCA/archive/2013/01/31/2888331.html
http://c.biancheng.net/cpp/html/2752.html