c语言中的各种变量是如何存储的

众所周知,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 的特殊存储空间。


你可能感兴趣的:(c语言中的各种变量是如何存储的)