C中指向常量字符串的指针

13
char *p = "string"; //creates pointer to constant string

char p[] = "string"; //just an array with "string"

我有点困惑,为什么在第一个例子中它会创建一个指向常量字符串的指针?它不应该只是一个指向内存中“string”位置的指针吗?


你可能想阅读c-faq的第6节(和其他部分)。 - pmg
1
发现了一些有趣的东西:http://www.geeksforgeeks.org/storage-for-strings-in-c/ - NDestiny
4个回答

25
在第一种情况下,"string" 可能存储在进程的只读区域,因此尝试修改由 p 指向的内存会导致未定义的行为。
在第二种情况下,实际的内存在运行时在堆栈上分配和初始化(或者在程序启动时在进程的适当部分分配),因此修改内存是可以的。
第一种情况可以像这样说明:
+---+           +---+---+---+---+---+---+---+
|   |   ---->   | s | t | r | i | n | g | \0|
+---+           +---+---+---+---+---+---+---+
  p

第二种情况是:
+---+---+---+---+---+---+---+
| s | t | r | i | n | g | \0|
+---+---+---+---+---+---+---+
              p

这两个声明有显著的不同,虽然你可能从低质量的 C 编程书籍中听说它们是相同的。 第一个是 char * 类型的指针,而第二个是 char [] 类型的数组。在某些情况下,数组标识符会退化为指针,但这并不意味着它们是指针。 指针只是一个地址,而数组是“整个东西”-在第一种情况下,sizeof(p) 将产生指针的大小(通常为 4 或 8 取决于目标机器),而在第二种情况下,它将产生实际字符串的长度 7 * sizeof(char)。

一个可执行文件的只读区域:也许在这里更重要的是“进程”...“可执行文件”更侧重于包含程序的磁盘文件,但是将文件内容复制到其中的内存大多数现代操作系统加载器都将被设置为只读。我喜欢您把第二种情况的 'p' 放在 'i' 下面...部分原因是我想把它放在 's' 下面来强调“数组起始位......”-因为理解数组偏移量很重要-但是您已经强调了 p * 是包含文本数据的整个数组,在这种情况下,这更重要。+1 - Tony Delroy
非常好的答案。此外,图形方法可以解释差异。 - Alberto Solano

17

遗憾的是,在 C 语言中(并且在 C++03 中也是为了兼容性而合法),可以这样做。但是,任何尝试通过指针修改字符串字面值都会导致未定义行为。因此,最好始终将字符串字面值赋值给一个const char*

const char * cp = "Hello"; //OK
char* p = "Hello"; //OK in C and C++03 (unfortunately), Illegal in C++11
cp[0] = 'Y'; //Compile-time error, good
p[0] = 'Y'; //no compiler error, undefined behavior

3
第一个方法创建指针并将其设置为常量字符串的地址(大概位于没有页面写保护的区域)。对该指针进行写操作是非法的(可能会导致崩溃)。
第二个方法创建数组并复制字符。对该数组进行写操作将写入堆栈上的某个位置,是完全合法的。

1
在第一种情况下,在 C 语言中写入指针并不是非法的,而是未定义的行为。它可能会导致内存保护错误,也可能正常工作。这完全取决于编译器操作系统和机器架构的组合。 - JeremyP

1
在第一种情况下,“string”可能存储在进程的只读区域,因此尝试修改p指向的内存将导致未定义的行为。
可以通过反复运行上述代码来证明这一点。
char *p="string";

你会注意到,p标签中的内容(即“string”的地址)保持不变。
char p[] = "string"; 

因此,每次运行时分配了这些内存,因此p的内容会更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接