问题描述
int main() {
char* p;
p = "hello";
p[2] = 'A'; // 非法操作
char c[30] = "hello";
c[2] = 'A'; // 正确操作
}
结论:
数组c的声明,是将字符串常量“复制”到数组中,复制来的字符串是可以修改的
指针p的声明,指向的是字符串常量的地址,而常量只读不可修改
分析过程:
先看这段代码
char* p, *m;
p = (char*)malloc(30);
m = (char*)malloc(30);
p = "hello";
char* q="hello";
char c[30] = "hello";
p[2] = 'A';
puts(p);
解释:为什么要给p动态申请内存,因为要与q对比,有人认为是因为指针没有动态申请内存,而真实的结果是申请内存后,p的值又被字符串的地址覆盖了。
首先要知道,数据存储会放在堆内存和栈内存,此外,还有一个数据区,这里叫做字符串常量区,我们会把
"hello"
存储在字符串常量区。
先看这张图
要看每个变量的存储地址:
数组c的存储地址为 0x004FFD1C
指针p的存储地址为 0x004FFD5C
指针q的存储地址为 0x004FFD44
你会发现他们地址几乎都在一起,因为他们存储的地方是栈内存
要知道
p = "hello";
意思是将字符串
"hello"
的地址存储到p内
而p内存储字符串的地址为 0x00667B30
q内存储的字符串的地址为 0x00667B30
你会发现存储的地址是一样的,说明字符串
"hello
的地址放在一个地方,也就是我们说的字符串常量区,
字符串常量区内的元素,只读不可修改
。
你们会问,为什么p内的地址为字符串常量的地址呢?
首先,指针p申请动态内存后,p内存储的是申请的内存的起始地址,而之后,p又存储字符串“hello”的地址,所以,最后p内存储的地址为字符串常量“hello”的地址。
你们又会问,为什么数组可以修改呢?
首先看这两张图的对比
经过对比,我们可以知道,数组c内存储的是字符串,
数组在被字符串赋值时,是将字符串常量的值“复制”到数组中
,通俗的理解是
char c[30] = "hello"; // 等价于 strcpy(c, "hello");
大家可以看一下这个代码:
int main() {
char* p, *m;
p = (char*)malloc(30);
m = (char*)malloc(30);
//p = "hello";
strcpy(p, "hello");
char* q="hello";
char c[30] = "hello";
p[2] = 'A';
puts(p);
}
这里输出结果为:
大家又会问:为什么用
strcpy(p, "hello");
因为字符串被复制到堆内存中,而不是访问字符串常量区
指针p首先动态申请内存,这时,指针p的值是申请的内存起始地址,所以strcpy是将字符串复制到申请的内存当中。
所以strcpy是可以修改的