【问题标题】:why is not const char put in rodata segment?为什么不将 const char 放入rodata 段?
【发布时间】:2020-07-03 08:46:30
【问题描述】:

有这个:

#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);
}

会产生气体:

...
.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);
...

这里,d const char 变量只有moved 入栈,但它的名字(撕裂位置)不在.section .rodata,这是为什么呢?当它具有 const 修饰符时。如果它是char* 字符串,那么它 会自动放置在rodata 上(char* 甚至不需要 const 修饰符)。我在某处读过 constness 是继承的(这意味着一旦使用 const 修饰符声明了一个变量,那么即使强制转换导致cast-away-constness,也不会改变 constness - 即它将保留)。但是这里甚至没有考虑 const char 修饰符(直接通过堆栈操作,就像数组一样)。为什么?

【问题讨论】:

  • 它是一个非静态的const local变量,所以它存储在堆栈中。

标签: c x86 char compiler-construction


【解决方案1】:

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

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

【讨论】:

  • 你说The C standard requires these instances to be distinct,但之后你说String literals (e.g. "foo") however are not required to have individual addresses when they appear in (recursive) functions。所以 char 需要有不同的地址,而 string 不是?我明白,为什么不应该更改字符串,但 si 不应该是字符。为什么 C 标准要求多个实例具有该变量的不同地址?
  • char 没有特殊规定。把它想象成ìnt。如果您在某些递归函数中有const int x = 0;,则需要printf("%p",&amp;x); 来打印不同的地址——与const char x = 0; 相同。那只是因为来自不同函数调用的局部变量可能不会重叠——毕竟它们是 local 的!另外,我不确定 C 是否禁止写入本地 const 变量,因为 C 没有“适当的”常量...由于不打算更改字符串文字,因此不需要单独的地址,除了const char x[]="foo";.
  • @Erlkoenig 你能解释一下为什么当我添加静态时我得到segmentation fault而不是assignment of read-only variable ‘d’......它应该在它之后的.ro,对吧?
  • ISO C 表示修改最初声明为 const 的对象是 UB,无论是静态存储还是自动存储。实际上,仅利用这一点将静态 const 数据放入只读内存中,因此违反此规则将崩溃。但是违反其他保证可能会导致以后的代码“没有注意到”对 const var 的更改。例如godbolt.org/z/fh67Le 显示在函数调用中进行常量传播,即使我将 &amp;n 传递给非内联函数。
  • 编译器不知道str实际上指向一个常量变量;它可能是一个指向非const 的指针,它被转换为const char*。通过抛弃const,您可以抑制任何编译器错误。你得到segmentation fault,因为当它是static时,变量实际上变成了只读的,因为操作系统保护.rodata部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 2021-07-18
  • 2023-04-02
相关资源
最近更新 更多