两个字符串指向不同字符串文字的地址是相同的

see*_*har 80 c pointers literals

#include<stdio.h>
#include<string.h>

int main()
{
    char * p = "abc";
    char * p1 = "abc";
    printf("%d %d", p, p1);
}
Run Code Online (Sandbox Code Playgroud)

当我打印两个指针的值时,它打印相同的地址.为什么?

P.P*_*.P. 86

具有相同内容的两个不同字符串文字是否放置在相同的内存位置或不同的内存位置是依赖于实现的.

你应该总是把pp1为两个不同的指针(即使它们具有相同的内容),因为它们可能会或可能不会指向同一个地址.您不应该依赖编译器优化.

C11标准,6.4.5,字符串文字,语义

如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的.如果程序试图修改此类数组,则行为未定义.


打印格式必须是%p:

  printf("%p %p", (void*)p, (void*)p1);
Run Code Online (Sandbox Code Playgroud)

看到这个答案的原因.

  • @Megharaj`我修改了一个指针,另一个指向的数据也会被修改.你可以修改*指针*而不是字符串文字.例如`char*p ="abc"; p ="xyz";`完全正常,而`char*p ="abc"; p [0] ='x';`调用*未定义的行为*.这与`volatile`无关.你是否使用`volatile`不应该改变我们感兴趣的任何行为.`volatile`基本上强制每次都从内存中读取数据. (8认同)
  • 附录C中的K&R第二版中有一行:"字符串不再可修改,因此可以放在只读存储器中",这是字符串文字*用于*可修改的历史证明;-) (7认同)
  • @MSharathHegde是的.因为`p`指向字符串文字`"abc"`和`p [0] ='x'`试图修改字符串文字的第一个字符.尝试修改字符串文字是C中未定义的行为. (2认同)
  • @MSharathHegde因为C标准说明了这一点.原因主要是历史,因为预标准C语言允许修改字符串文字.后来,C标准(C89)使它*未定义*,以便新代码不会这样做,旧代码(预标准)工作原样.基本上,我认为,不破坏现有(前标准)代码是妥协.另一个原因是字符串文字的类型是C中的`char []`.因此,将其设置为只读(`c char char*`就像C++中的情况一样)也需要转换*type*.[续] (2认同)

alk*_*alk 28

您的编译器似乎非常聪明,检测到两个文字是相同的.由于文字是不变的,编译器决定不将它们存储两次.

似乎值得一提的是,这不一定是必须的.请参阅Blue Moon对此回答.


顺便说一句:printf()声明应该是这样的

printf("%p %p", (void *) p, (void *) p1);
Run Code Online (Sandbox Code Playgroud)

as "%p"用于打印指针值,并且void *仅为类型指针定义.*1


另外我会说代码错过了一个return声明,但C标准似乎正在被改变.其他人可能会澄清这一点.


*1:void *对于char *指针,不需要转换到此处,而是指向所有其他类型的指针.

  • @seereddisekhar但是要小心它并不意味着你应该使用`==`来比较两个字符串(甚至指针)你应该使用`strcmpy()`函数.因为其他编译器可能没有使用优化(它是编译器 - 实现依赖性),因为Alk回答了PS:Blue Moon刚刚添加了它. (2认同)
  • 亲爱的@Megharaj:我可以请求就此提出一个单独的问题吗?您可以在此处发布此新问题的链接作为评论. (2认同)

kfs*_*one 18

您的编译器已经完成了一个名为"字符串池"的操作.你指定你想要两个指针,都指向相同的字符串文字 - 所以它只制作了一个文字的副本.

技术上:它应该抱怨你没有使指针"const"

const char* p = "abc";
Run Code Online (Sandbox Code Playgroud)

这可能是因为您使用的是Visual Studio,或者您在没有-Wall的情况下使用GCC.

如果您明确希望将它们存储在内存中两次,请尝试:

char s1[] = "abc";
char s2[] = "abc";
Run Code Online (Sandbox Code Playgroud)

在这里,您明确声明您需要两个c字符串字符数组而不是两个指向字符的指针.

警告:字符串池是编译器/优化器功能,而不是语言的一个方面.因此,不同环境下的不同编译器会根据优化级别,编译器标志以及字符串是否位于不同的编译单元中而产生不同的行为.


huo*_*uon 14

正如其他人所说,编译器注意到它们具有相同的值,因此决定让它们在最终的可执行文件中共享数据.但它变得更加漂亮:当我编译以下内容时gcc -O

#include<stdio.h>
#include<string.h>

int main()
{
  char * p = "abcdef";
  char * p1 = "def";
  printf("%d %d", p, p1);
}
Run Code Online (Sandbox Code Playgroud)

它打印4195780 4195783给我.也就是说,之后p1开始3个字节p,因此GCC已经看到了def(包括\0终结符)的公共后缀,并对您显示的那个进行了类似的优化.

(这是一个答案,因为评论太久了.)