- 1. 相同字符串常量的地址是否相同?
- 2. 指针形式的字符串常量(char *)可以更改吗?
- 3. 数组形式的字符串常量(char [])可以更改吗?
- 4. 动态申请内存的字符串(malloc)可以更改吗?
- 5. 参考文献
1. 相同字符串常量的地址是否相同?
- 源码
// File: strdemo1.c #includechar *a = "hello world"; char c[] = "hello world"; int main(int argc, char *argv[]) { char *b = "hello world"; printf("ap=%p, bp=%p, a%sbn", a, b, ((a==b)? "==": "!=")); char d[] = "hello world"; printf("cp=%p, dp=%p, c%sdn", c, d, ((c==d)? "==": "!=")); return 0; }
- 执行
root@ubuntu:/opt/constval# gcc -Wall -g strdemo1.c -o strdemo1 root@ubuntu:/opt/constval# ./strdemo1 ap=0x55995a62d004, bp=0x55995a62d004, a==b cp=0x55995a62f010, dp=0x7fffdebdfa8c, c!=d
- 结论
(1) 指针形式的字符串常量(*a 和 *b):地址相同,指向同一段存储空间,即仅存储一份相同的字符串。
(2) 数组形式的字符串常量(c[] 和 d[]):地址不同,指向不同的存储空间,即存储了两份相同的字符串。
2. 指针形式的字符串常量(char *)可以更改吗?
- 源码
// File: strdemo2.c #includechar *a = "hello world"; // 已初始化 char *b; // 未初始化 int main(int argc, char *argv[]) { *a = "get out"; // compile [-Wint-conversion] and execute Segmentation fault // *(a+3) = '-'; // compile normal and execute Segmentation fault // *a = 123; // compile normal and execute Segmentation fault // *a = 't'; // compile normal and execute Segmentation fault // *b = "get out"; // compile [-Wint-conversion] and execute Segmentation fault // *(b+3) = '-'; // compile normal and execute Segmentation fault // *b = 123; // compile normal and execute Segmentation fault // *b = 't'; // compile normal and execute Segmentation fault // printf("++ap=%p, ", ++a); // normal // printf("++ac=%sn", a); // normal // printf("++bp=%p, ", ++b); // normal (++bp=0x1, because *b is uninitialized) // printf("++bc=%sn", b); // compile normal and execute Segmentation fault return 0; }
- 执行
执行结果参见源码中的注释,读者可以自行验证每一行。例如"*a = “get out”; //compile [-Wint-conversion] and execute Segmentation fault",即编译时报 [-Wint-conversion] 警告,执行时报Segmentation fault,如下所示。
root@ubuntu:/opt/constval# gcc -Wall -g strdemo2.c -o strdemo2
strdemo2.c: In function ‘main’:
strdemo2.c:6:5: warning: assignment to ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
6 | *a = "get out"; // compile [-Wint-conversion] and execute Segmentation fault
| ^
root@ubuntu:/opt/constval# ./strdemo2
Segmentation fault (core dumped)
- 结论
(1) 无论 char * 是否初始化,在编译时,输入int型、char型无警告,输入字符串型报[-Wint-conversion]警告;在执行时,均会报Segmentation fault错误。
(2) 指针形式的字符串常量存放在不可以更改的静态内存存储区域,更改其指向的任何数据都是不允许的。
(3) 指针本身是一个变量,单独预留一个存储位置,用于存储字符串常量的地址(指向首字符地址),可进行++a等类似的操作。
3. 数组形式的字符串常量(char [])可以更改吗?
- 源码
// File: strdemo3.c #includechar c[12] = "hello world"; int main(int argc, char *argv[]) { printf("cp=%p, cv=%sn", c, c); c[5] = '-'; // normal printf("cp=%p, cv=%sn", c, c); *(c+5) = '&'; // normal printf("cp=%p, cv=%sn", c, c); *c = "get out"; // compile [-Wint-conversion] and execute normal printf(""get out" pointer is %pn", "get out"); printf("cp=%p, cv=%sn", c, c); // *(++c) = '#'; // compile error: lvalue required as increment operand return 0; }
- 执行
root@ubuntu:/opt/constval# gcc -Wall -g strdemo3.c -o strdemo3
strdemo3.c: In function ‘main’:
strdemo3.c:16:8: warning: assignment to ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
16 | *c = "get out";
| ^
root@ubuntu:/opt/constval# ./strdemo3
cp=0x557291653010, cv=hello world
cp=0x557291653010, cv=hello-world
cp=0x557291653010, cv=hello*world
"get out" pointer is 0x557291651012
cp=0x557291653010, cv=ello&world
- 结论
(1) 用字符串常量初始化的数组(char [])可以更改,因为它不是存放在不可更改的内存区域。
(2) 对于 *c = “get out” 进一步说明:*c 实际上指向的是c[12]的第一个字符,将 “get out” 赋值给 *c时,强制将其地址0x557291651012截取后两位,即char型0x12,赋值给c[12]的第一个字符。因为0x12在ASCII码表中对应的是控制字符,即非打印字符,所以其输出为ello&world。
(3) 编译器会把数组名c看作是数组首元素的地址&c[0]的同义词,即c是个地址常量。可以用c+1来标识下一个元素,但不能使用++c;c[12]在计算机内存中被分配一个有12个元素的数组(其中每个元素对应一个字符,还有一个附加的元素对应结束的空字符’ ’),每个元素都被初始化为相应的字符;通常,被引用的字符串存储在可执行文件的数据段部分,当程序被加载到内存中时,字符串也被加载到内存中,把被引用的字符串复制到数组中。
4. 动态申请内存的字符串(malloc)可以更改吗?
- 源码
// File: strdemo4.c #include#include int main(int argc, char *argv[]) { char *a = "hello world"; char *b = NULL; if ((b = (char *)malloc(sizeof(char) * 12)) == NULL) { perror("malloc fail!"); return -1; } while(*a != ' ') *b++=*a++; *b = ' '; b -= 11; printf("bp=%p, bc=%sn", b, b); *(b+5) = '-'; printf("bp=%p, bc=%sn", b, b); free(b); return 0; }
- 执行
root@ubuntu:/opt/constval# gcc -Wall -g strdemo4.c -o strdemo4 root@ubuntu:/opt/constval# ./strdemo4 bp=0x559b61db72a0, bc=hello world bp=0x559b61db72a0, bc=hello-world
- 结论
(1) 用动态申请的方法给一个字符串赋值,那么该字符串在程序运行过程中的值依然可以修改。
5. 参考文献
- 《C语言字符串定义》
- 《gcc_warning:assignment makes integer from pointer without a cast》
- 《ASCII码字符对照表》



