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


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

int main()
{
    char * p = "abc";
    char * p1 = "abc";
    printf("%d %d", p, p1);
}

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


66
您为什么认为不应该?这两个指针都指向完全相同的事物。您所看到的可能是称为字符串池的优化技术的效果。
Daniel Kamil Kozar 2013年

2
即使数据相同,但变量也不同。
Seereddi sekhar

2
当然,变量是不同的。如果您使用p和的地址p1,那么您会注意到这两个指针存储在两个不同的地址下。在这种情况下,它们的值相同是不重要的。
Daniel Kamil Kozar 2013年

是的,如果我更改值,则地址会不同。
Seereddi sekhar

11
@JanHudec:再次阅读问题。在这种情况下(由于编译器优化)p == p1(它们没有不同),但是&p != &p1(它们确实有所不同)。
MSalters 2013年

Answers:


86

具有相同内容的两个不同字符串文字是放置在相同的存储位置还是不同的存储位置取决于实现。

您应该始终将pp1当作两个不同的指针(即使它们具有相同的内容),因为它们可能指向或不指向同一地址。您不应该依赖编译器优化。

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

如果它们的元素具有适当的值,则不确定这些数组是否不同。如果程序尝试修改这样的数组,则行为是不确定的。


打印格式必须为%p

  printf("%p %p", (void*)p, (void*)p1);

为何要查看此答案


我使用了volatile,因此即使它们采用相同的地址,也必须没有内存优化。一个问题是,如果我修改其中一个指针,另一个指针中的数据是否也会被修改。
Megharaj

8
@Megharaji modify one of the pointer, will the data in the other pointed also be modified您可以修改指针,但不能修改字符串文字。例如,char *p="abc"; p="xyz";可以很好地char *p="abc"; p[0]='x';调用未定义的行为。这与无关volatile。无论您是否使用volatile,都不应更改我们在此感兴趣的任何行为。volatile基本上是强制每次从内存中读取数据。
2013年

2
@MSharathHegde是的。因为p指向字符串文字"abc"p[0]='x'尝试修改字符串文字的第一个字符。试图修改字符串文字是在C.未定义的行为
PP

2
@MSharathHegde因为C标准指出了这一点。原因主要是历史原因,因为标准C语言允许修改字符串文字。后来,C标准(C89)将其定义为未定义,以便新代码不执行此操作,而旧代码(预标准)照原样工作。我相信,基本上不破坏现有(标准)代码是一种折衷方案。另一个原因是字符串文字的类型是char []在C中。因此,使其成为只读类型(const char*在C ++中就是这种情况)也需要更改类型。[续]
PP

7
附录C:K&R第二版中有一行:"Strings are no longer modifiable, and so may be placed in read-only memory"历史证明字符串文字曾经是可修改的;-)
PP

28

您的编译器似乎很聪明,可以检测到两个文字是相同的。由于文字是常量,因此编译器决定不将它们存储两次。

似乎值得一提的是,并不一定是这种情况。请参阅蓝月亮对此回答


顺便说一句:该printf()语句应如下所示

printf("%p %p", (void *) p, (void *) p1);

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


我还要说代码缺少return语句,但是C标准似乎正在被更改。其他人可能会澄清这一点。


* 1:投射至 void *对于char *指针,此处不是必需的,但对于所有其他类型的指针,则此处。


谢谢。那么结论就是编译器优化对吗?在C主函数中默认情况下返回0
seereddi sekhar

@seereddisekhar:是的,这是一种优化。
2013年

2
@seereddisekhar但是要小心,这并不意味着==您应该使用strcmpy()函数来比较两个字符串(甚至是指针)。因为其他的编译器可能没有使用优化(取决于编译器-实施的延迟),如Alk回答PS:Blue Moon刚刚添加了优化。
Grijesh Chauhan

2
亲爱的@Megharaj:我是否可以提出一个单独的问题?您可以在此处发布指向此新问题的链接作为评论。
2013年

1
@Megharaj:您不能更改字符串文字的值。正如我在问题中提到的那样,它是不变的。
2013年

18

您的编译器已完成称为“字符串池”的操作。您指定了两个指针,它们都指向同一个字符串文字-因此,它仅制作了文字的一个副本。

从技术上讲:它应该抱怨您没有将指针设为“ const”

const char* p = "abc";

这可能是因为您使用的是Visual Studio或不使用-Wall的GCC。

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

char s1[] = "abc";
char s2[] = "abc";

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

注意:字符串池是编译器/优化器功能,而不是语言的一个方面。这样,在不同环境下的不同编译器将根据优化级别,编译器标志以及字符串是否在不同的编译单元中而产生不同的行为。


1
gcc (Debian 4.4.5-8) 4.4.5尽管使用,但不会抱怨(警告)-Wall -Wextra -pedantic
2013年

1
是的,从v4.8.1开始,默认情况下,gcc不会警告不要使用const字符串文字。该警告由option启用-Wwrite-strings。显然没有其他任何选项(例如-Wall-Wextra-pedantic)启用该功能。
sleske 2013年

1
无论是否带有-Wall,GCC 4.4.7和4.7.2都会给我警告。pastebin.com/1DtYEzUN
kfsone 2013年

15

正如其他人所说,编译器注意到它们具有相同的值,因此决定让它们在最终可执行文件中共享数据。但是它变得更奇特了:当我用gcc -O

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

int main()
{
  char * p = "abcdef";
  char * p1 = "def";
  printf("%d %d", p, p1);
}

4195780 4195783为我打印。也就是说,p1在之后的3个字节处开始p,因此GCC看到了的通用后缀def(包括\0终止符),并进行了与所示的类似的优化。

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


3

代码中的字符串文字存储在代码的只读数据段中。当您写下一个字符串文字(如“ abc”)时,它实际上会返回一个“ const char *”,并且如果您在上面带有所有编译器警告,都将告诉您此时正在强制转换。出于您在此问题中指出的原因,您不允许更改这些字符串。


2

创建字符串文字(“ abc”)时,它会保存到包含字符串文字的内存中,如果您引用相同的字符串文字,那么它将被重用,因此两个指针都指向同一位置,其中“ abc“字符串文字存储。

我是在一段时间前学到的,对不起,我可能还没有清楚地解释它。


2

这实际上取决于您使用的是哪个编译器

在我使用TC ++ 3.5的系统中,它为两个指针输出两个不同的值,即两个不同的地址

您的编译器设计为st,它将检查内存中是否存在任何值,并且如果引用了相同的值,则根据其是否存在将重新分配使用与先前存储的值相同的引用

因此,不要过多地考虑它,因为它取决于编译器解析代码的方式。

就这样...


1

因为字符串“ abc”本身就是内存中的地址。当您再次写入“ abc”时,它将存储相同的地址



0

您使用的是字符串文字,

当编译器捕获两个相同的字符串文字时,

它给出相同的内存位置,因此它显示相同的指针位置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.