为什么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=0在一些递归函数中,然后printf(“%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);
...