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