为什么const char不放在rodata段中?

为什么const char不放在rodata段中?,c,x86,char,compiler-construction,C,X86,Char,Compiler Construction,具有以下特征: #include <stdio.h> #include <stdlib.h> void f(const char *str){ char *p = (char*)str; *p=97; } int main(){ char c; f(&c); char *p = malloc(10); if (p) { f(p); printf("p:%s\n",p); free(p); }

具有以下特征:

#include <stdio.h>
#include <stdlib.h>

void f(const char *str){
    char *p = (char*)str;
    *p=97;
}

int main(){
    char c;
    f(&c);

    char *p = malloc(10);
    if (p) { f(p); printf("p:%s\n",p); free(p); }

    const char d = 0; //only this part in interest
    f(&d); // here the function modifies the the char, but since it is NOT in rodata, no problem
    printf("d:%c\n",d);

    printf("c:%c\n",c);
}

在这里,
d
const char变量只是
mov
ed到stack,但它的名称(rip位置)不在
.section.rodata中,这是为什么?当它有常量修饰符时。如果它是
char*
string,那么它会自动放置在rodata上(char*甚至不需要常量修饰符)。我已经读到了constness被继承的地方(这意味着一旦一个变量用const修饰符声明,那么即使是导致cast-away-constness的强制转换,也不会改变constness,即它将保持不变)。但这里甚至没有考虑constchar修饰符(直接通过堆栈操作,就像数组一样)。为什么?

变量
d
不是静态的,而是一个函数局部变量。如果包含它的函数被多次调用(递归调用,或在多个线程中并发调用),则会得到变量的多个实例(在函数的堆栈框架内),每个实例都有自己的单独地址,即使它们都包含相同的数据。C标准要求这些实例是不同的。如果将变量定义为
static
,编译器可能会将其移动到
.rodata
部分,这样您只会得到一个实例


但是,字符串文字(例如
“foo”
)在(递归)函数中出现时不需要有单独的地址(除非它们用于初始化
字符
数组),因此编译器通常将其放入
部分。

它是一个非静态常量局部变量,因此,它存储在堆栈上。你说
C标准要求这些实例是不同的
,但之后,你说
字符串文字(例如“foo”),但是当它们出现在(递归)函数中时,不要求它们有单独的地址
。所以char需要有不同的地址,而string不是?我明白,为什么字符串不应该改变,但si不应该是字符。为什么C标准要求多个实例具有该变量的distinc地址?关于
char
,没有特殊规则。把它想象成
ìnt
。如果你有
const int x=0printf(“%p”,&x)
需要打印不同的地址-与
const char x=0相同。这只是因为来自不同函数调用的局部变量可能不会重叠——它们毕竟是局部变量!另外,我不确定C是否禁止写入本地
常量
变量,因为C没有“正确的”常量。。。由于不打算更改字符串文字,因此除了
const char x[]=“foo”之外,不需要对单个地址进行任何更改
@Erlkoenig您能解释一下,当我向只读变量“d”添加static时,为什么会得到
分段错误
而不是
赋值吗。。。后面应该是.ro,对吗?ISO C说修改最初声明为
const
的对象是UB,不管是静态存储还是自动存储。实际上,只有利用这一点,才能将静态常量数据放在只读内存中,因此违反这一点就会崩溃。但是违反另一个保证可能会导致以后的代码“没有注意到”常量变量的更改。例如,即使我将
&n
传递给非内联函数,也会显示在函数调用中进行常量传播。编译器不知道
str
实际上指向常量变量;它可能是指向非
常量的指针,该常量被强制转换为
常量字符*
。通过丢弃
常量
,可以抑制任何编译器错误。您会得到
分段错误
,因为变量在
静态时实际上变成只读的
,因为操作系统保护
.rodata
部分。
...
.L3:
# a.c:16:   const char d = 0;
    movb    $0, -10(%rbp)   #, d
# a.c:17:   f(&d);
    leaq    -10(%rbp), %rax #, tmp98
    movq    %rax, %rdi  # tmp98,
    call    f   #
# a.c:18:   printf("d:%c\n",d);
    movzbl  -10(%rbp), %eax # d, d.0_1
    movsbl  %al, %eax   # d.0_1, _2
    movl    %eax, %esi  # _2,
    leaq    .LC1(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    printf@PLT  #
# a.c:20:   printf("c:%c\n",c);
...