众所周知,c语言中有全局变量,局部变量,常量数据。。。
其中,全局变量又有初始化和不初始化。
int array[1024];
int main()
{
return 0;
}
lyh@debian:~/test$ gcc -g bss.c -o bss
lyh@debian:~/test$ objdump -h bss | grep bss
bss:
file format elf32-i386
24 .bss 00001020 08049580 08049580 00000568 2**5
lyh@debian:~/test$ ls -l
bss
-rwxr-xr-x 1 lyh lyh 5395 4月 26 18:29 bss
(bss段用来存放未初始化的全局变量)
上面代码中全局变量未初始化,全局变量array 4k的数据存放在bss中。
int array[1024] = {1};
int main()
{
return 0;
}
lyh@debian:~/test$ gcc -g data.c -o data
lyh@debian:~/test$ objdump -h data | grep\\.data
23 .data 00001020 08049560 08049560 00000560 2**5
lyh@debian:~/test$ ls -l data
-rwxr-xr-x 1 lyh lyh 9520 4月 26 18:35 data
(.data段用来存放初始化了的全局变量)
上面代码中全局变量初始化了,全局变量array 4k的数据存放在.data中。发现data文件的大小为9520,也就是说初始化了的全局变量的大小会加入到文件大小中,同时又会占内存。
所以我们在定义全局变量的时候,要考虑要不要初始化,能不初始化就不初始化,毕竟会增大文件的大小。
如果我们把全局变量初始化为0时,会发生什么呢?
int array[1024] = {0};
int main()
{
return 0;
}
lyh@debian:~/test$ objdump -h data | grep\\.data
23 .data 00000008 08049560 08049560 00000560 2**2
当我们查看.data时发现初始化为0的全局变量array竟然不是存储在.data段中。
lyh@debian:~/test$ objdump -h data | grep bss
24 .bss 00001020 08049580 08049580 00000568 2**5
查看bss段,发现array存放在该段里
编译器会把该全局变量当作没初始化处理,即把全局变量存到bss段中。所以可以得出bss段是用来存放未初始化或者初始化为0的全局变量。
常量数据一般放在.rodata段(ro代表read only)。
常量数据有哪些呢,一般有字符串常量和带const的变量;
代码一:
int main()
{
char *str = "abcdef";
return 0;
}
lyh@debian:~/test$ objdump -h rodata | grep rodata
rodata: file format elf32-i386
15 .rodata
0000000f
08048468 08048468 00000468 2**2
lyh@debian:~/test$ objdump -h rodata | grep text
13
.text
0000016c
080482e0 080482e0 000002e0 2**4
看出字符串“abcdef”存在.rodata段.rodata的初始大小是0x00000008,加上“abcdef”的大小7刚好就是上面.rodata的大小0x0000000f了。
那么是不是所有的字符串常量都是存在.rodata段中呢?
代码二:
int main()
{
char array[] = "abcdef";
return 0;
}
lyh@debian:~/test$ gcc -g rodata.c -o rodata
lyh@debian:~/test$ objdump -h rodata | grep rodata
rodata: file format elf32-i386
15 .rodata
00000008
08048478 08048478 00000478 2**2
可以发现.rodata段中大小为0x00000008,也就是说字符串常量“abcdef”没有在该段里,那它在哪里呢?
lyh@debian:~/test$ objdump -h rodata | grep text
13 .text
0000017c
080482e0 080482e0 000002e0 2**4
汇编代码:
char array[] = "abcdef";
804839a: c7 45 f9 61 62 63 64 movl
$0x64636261
,-0x7(%ebp)
80483a1: 66 c7 45 fd 65 66 movw
$0x6665
,-0x3(%ebp)
80483a7: c6 45 ff 00 movb
$0x0
,-0x1(%ebp)
综合可知,该字符串常量存储在了代码段中,也就是.text段。
所以,字符串常量也可能在.text(代码段)也可能在.rodata段中。
还有一种const类型的变量也存在.rodata段中。
代码三:
const int i = 1;
int main()
{
return 0;
}
lyh@debian:~/test$ objdump -h rodata | grep rodata
rodata: file format elf32-i386
15 .rodata
0000000c
08048458 08048458 00000458 2**2
上面的全局const类型存在.rodata中。那是不是任何const的都是这样呢?
代码四:
int main()
{
const int i = 1;
return 0;
}
15 .rodata
00000008
08048468 08048468 00000468 2**2
汇编代码:
int main()
{
8048394:
55
push %ebp
8048395:
89 e5
mov %esp,%ebp
8048397:
83 ec 10
sub $0x10,%esp
const int i = 1;
804839a:
c7 45 fc 01 00 00 00
movl
$0x1
,-0x4(%ebp)
return 0;
80483a1:
b8 00 00 00 00
mov $0x0,%eax
}
再看const类型作参数的情况
代码五:
int fun(const int i)
{
return i;
}
int main()
{
fun(1);
return 0;
}
15 .rodata
00000008
08048478 08048478 00000478 2**2
汇编代码:
int fun(const int i)
{
8048394:
55
push %ebp
8048395:
89 e5
mov %esp,%ebp
return i;
8048397:
8b 45 08
mov
0x8(%ebp)
,%eax
}
int main()
{
804839c:
55
push %ebp
804839d:
89 e5
mov %esp,%ebp
804839f:
83 ec 04
sub $0x4,%esp
fun(1);
80483a2:
c7 04 24 01 00 00 00
movl
$0x1,(
%esp)
80483a9:
e8 e6 ff ff ff
call 8048394 <fun>
return 0;
80483ae:
b8 00 00 00 00
mov $0x0,%eax
}
发现 .rodata段也没变,且从fun()函数的汇编代码发现 变量i存在.text,其实参数中的const只是告诉编译器该值不可修改。
所以带const类型的全局变量存在.rodata段中,
所以带const类型的全局变量和一些字符串常数存在文件的.rodata段(程序运行时该数据存在只读Memory中,看代码七和代码八)中,他们不可以修改;带const类型的参数和一些字符串常量保存在文件的.text段中(程序运行时把这些数据存进栈中,参考下面的代码六),他们也不可以修改(如果代码中有直接修改他们的代码,则不能通过编译)
代码六:
int main()
{
const int i = 1;
int a;
*(&a+1) = 2; /
/这里&a是一个地址,实际的操作是得到 &a+sizeof(int) 该处的地址
printf("i=%d",i);
return 0;
}
结果:
i=2
改代码通过先绕过编译器,在程序运行时,成功把const类型的变量i成功修改了。(因为运行时,i存储在普通的栈中而已)
然而:
代码七:
const int a;
const int b;
int main()
{
printf("&a=%d\n", &a);
printf("&b=%d\n", &b);
return 0;
}
lyh@debian:~/test$ ./rodata
&a=134518260
&b=134518264
发现变量a和b的地址是连续的,那么我们想到可不可以用一下代码来试着修改a的值呢
代码八:
const int a;
const int b;
int main()
{
*(&b-1) = 1; //即对变量a进行赋值操作
printf("a=%d",a);
}
试着编译,会发生这么个错误:
rodata.c:12: error: assignment of
read-only
location ‘*(&b + 0xfffffffffffffffffffffffffffffffcu)’
说明变量a、b是存放在一个only read 的特殊存储空间。