C 常量字符串存储在哪里?堆栈中还是.data?

C 常量字符串存储在哪里?堆栈中还是.data?,c,memory-layout,const-string,C,Memory Layout,Const String,我已经编写了一个简单的c代码,如下所示。在这个代码段中,我想验证常量字符串abcd存储在哪里。我首先猜测,它应该存储在.data部分中,以便只读。然而,在Debian中进行了一次测试之后,情况与我最初的猜测有所不同。通过检查gcc生成的汇编代码,我发现它被放置在函数p的堆栈框架中。但当我稍后在OSX中尝试时,字符串再次存储在.data部分。现在我对此感到困惑。常量字符串的存储有什么标准吗 #include<stdio.h> char *p() { char p[] = "ab

我已经编写了一个简单的
c
代码,如下所示。在这个代码段中,我想验证常量字符串
abcd
存储在哪里。我首先猜测,它应该存储在
.data
部分中,以便只读。然而,在Debian中进行了一次测试之后,情况与我最初的猜测有所不同。通过检查gcc生成的汇编代码,我发现它被放置在函数
p
的堆栈框架中。但当我稍后在OSX中尝试时,字符串再次存储在
.data
部分。现在我对此感到困惑。常量字符串的存储有什么标准吗

#include<stdio.h>
char *p()
{
    char p[] = "abcd";
    return p;
}

int main()
{
    char *pp = p();
    printf("%s\n",pp);
    return 0;
}
#包括
char*p()
{
字符p[]=“abcd”;
返回p;
}
int main()
{
char*pp=p();
printf(“%s\n”,pp);
返回0;
}


更新:rici的回答唤醒了我。在OSX中,初始文本存储在
.data
中,然后移动到函数的堆栈帧中。因此,它成为此函数的局部变量。然而,Debian中的gcc处理这种情况与OSX不同。在Debian中,gcc直接将文本存储在堆栈中,而不是将其从
.data
中移动。很抱歉我的粗心大意。

对你来说,它位于堆栈中。返回指向main的指针将导致未定义的行为。但是,如果您有
静态字符p[]=“abcd”
char*p=“abcd”它们(数据)位于.data中。

它们之间存在巨大差异:

const char s[] = "abcd";

其中第一个声明
s
为从字符串“abcd”初始化的数组对象<代码>s
的地址与程序中任何其他对象的地址不同。字符串本身可能是编译时工件;初始化是一个副本,因此如果编译器可以找到执行初始化的其他方法(例如存储立即操作),则在运行时不需要显示字符串

第二个声明将
t
声明为指向字符串常量的指针。字符串常量现在必须在运行时出现,因为像
t+1
这样的表达式是有效的,它们是字符串中的指针。语言标准不能保证程序中出现的每一个字符串文本都是唯一的,也不能保证所有出现的字符串文本都被合并(尽管好的编译器会尝试第二种方法)。但是,它可以保证它们具有静态生存期

因此,这是未定义的行为,因为数组
s
的生存期在函数返回时结束:

const char *gimme_a_string() {
  const char s[] = "abcd";
  return s;
}
但是,这很好:

const char *gimme_a_string() {
  const char *s = "abcd";
  return s;
}
此外:

保证打印
0
,而

const char* s = "abcd";
const char* t = "abcd";
printf("%d\n", s == t);
可能打印
0
1
,具体取决于实现。(如前所述,它几乎肯定会打印
1
。但是,如果这两个声明位于单独的编译单元中,并且lto未启用,则它可能会打印
0

由于数组表单是用副本初始化的,因此非常量版本是可以的:

char s[] = "abcd";
s[3] = 'C';
但是字符指针版本必须是
常量
,以避免未定义的行为

// Will produce a warning on most compilers with compile option -Wall or equivalent
char* s = "abcd";
// *** UNDEFINED BEHAVIOUR *** Can cause random program breakage
s[3] = 'C';

从技术上讲,
s
的非常量声明是合法的(这就是为什么编译器只发出警告),因为它是试图修改UB常量。但你应该时刻注意编译器的警告;最好认为声明/初始化是错误的,因为它是错误的。

由实现决定在何处存储文字您正在返回指向本地数组的指针,但该数组的行为尚未定义。FWIW,当您声明文字存储在堆栈框架中时,我不相信您。相信我,它准确地存储在Debian的堆栈框架中。我在函数p的堆栈框架中找到了
movl$1684234849,-16(%rbp)
。如您所见,
$1684234849
表示
abcd
,然后将其移动到
-16(%rbp)
,反过来,
-16(%rbp)
的地址作为函数的返回值返回。如果它有默认值,则它在.data中,如果不在ELF中,则在.bss中。
p
应该是指向const的指针,因为您不能更改该字符串文字。@black.rodata加载了.data,所以我认为.rodata是.data的一个子段,至少在ELF中是这样。如果我错了,请纠正我。
char s[] = "abcd";
s[3] = 'C';
// Will produce a warning on most compilers with compile option -Wall or equivalent
char* s = "abcd";
// *** UNDEFINED BEHAVIOUR *** Can cause random program breakage
s[3] = 'C';