两个字符串指向不同字符串文字的地址是相同的
#include #include int main() { char * p = "abc"; char * p1 = "abc"; printf("%d %d", p, p1); }
当我打印两个指针的值时,它打印相同的地址。 为什么?
具有相同内容的两个不同字符串文字是否放置在相同的内存位置或不同的内存位置是依赖于实现的。
您应该始终将p
和p1
视为两个不同的指针(即使它们具有相同的内容),因为它们可能指向同一地址,也可能不指向同一地址。 您不应该依赖编译器优化。
C11标准,6.4.5,字符串文字,语义
如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的。 如果程序试图修改此类数组,则行为未定义。
打印格式必须为%p
:
printf("%p %p", (void*)p, (void*)p1);
看到这个答案的原因。
您的编译器似乎非常聪明,检测到两个文字是相同的。 由于文字是不变的,编译器决定不再存储两次。
似乎值得一提的是,这不一定是必须的。 请看蓝月亮 对此的回答 。
顺便说一句: printf()
语句应如下所示
printf("%p %p", (void *) p, (void *) p1);
因为"%p"
将用于打印指针值,并且仅为void *
类型的指针定义。 * 1
另外我会说代码错过了一个return
语句,但C标准似乎正在被改变。 其他人可能会澄清这一点。
* 1:转换为void *
这里不是char *
指针所必需的,而是指向所有其他类型的指针。
你的编译器做了一个叫做“字符串池”的东西。 你指定你想要两个指针,都指向相同的字符串文字 – 所以它只制作了一个文字的副本。
技术上:它应该抱怨你没有使指针“const”
const char* p = "abc";
这可能是因为您使用的是Visual Studio,或者您在没有-Wall的情况下使用GCC。
如果您明确希望将它们存储在内存中两次,请尝试:
char s1[] = "abc"; char s2[] = "abc";
在这里,您明确声明您需要两个c字符串字符数组,而不是两个指向字符的指针。
警告:字符串池是编译器/优化器function,而不是语言的一个方面。 因此,不同环境下的不同编译器会根据优化级别,编译器标志以及字符串是否位于不同的编译单元中而产生不同的行为。
正如其他人所说,编译器注意到它们具有相同的值,因此决定让它们在最终的可执行文件中共享数据。 但它变得更加漂亮:当我使用gcc -O
编译以下内容时
#include #include int main() { char * p = "abcdef"; char * p1 = "def"; printf("%d %d", p, p1); }
它给我打印4195780 4195783
。 也就是说, p1
在p
之后开始3个字节,因此GCC已经看到了def
的公共后缀(包括\0
终止符),并对你所显示的那个进行了类似的优化。
(这是一个答案,因为评论太长了。)
代码中的字符串文字存储在代码的只读数据段中。 当你写下像“abc”这样的字符串文字时,它实际上会返回一个’const char *’,如果你有所有的编译器警告就会告诉你那时你正在施放。 由于您在此问题中指出的原因,您不能更改这些字符串。
当你创建一个字符串文字(“abc”)时,它被保存到一个包含字符串文字的内存中,然后如果你引用相同的字符串文字就会被重用,因此两个指针指向同一个位置,其中“ abc“存储字符串文字。
很久以前我已经学会了这个,所以我可能没有清楚地解释它,对不起。
这实际上取决于您使用的编译器 。
在我的TC ++ 3.5系统中,它为两个指针打印两个不同的值,即两个不同的地址 。
您的编译器是设计好的,它将检查内存中是否存在任何值,并且根据其存在,如果引用相同的值,它将重新分配或使用先前存储的值的相同引用。
所以不要过多考虑因为它取决于编译器解析代码的方式。
就这样…
因为字符串“abc”本身就是内存中的一个地址。 当你再次写“abc”时,它会存储相同的地址
它是编译器优化但忘记了可移植性的优化。 有时编译的代码比实际代码更易读。
你是使用字符串文字,
当complier捕获两个相同的字符串文字时,
它提供相同的内存位置,因此它显示相同的指针位置./