从内存看字符串常量与字符数组的区别


字符串声明有两种方式

char str1[] = “hello”    ①

char *str2 = “world”   ②

在有些函数中,如strtok,strrep中,只能使用①,为什么?

Str1和str2的区别在哪里?先说结论!

Str1的值”hello”存储在可读可写区,而str2的值”world”存储在只读区。

某些函数会修改字符串,如果存放在只读区,当然就会报错了。

请看以下分析:

我们从底层入手,定义如下函数

文件名:main.c

#include <stdio.h>

char format[] = "Hello, %s !\n";

char* name = "yahoo!";

int main(int argc, char* argv[])

{

printf(format, name);

return 0;

}

Linux下编译产生可执行文件

gcc -g -o main main.c

这时候得到可执行的elf文件main

对elf文件进行分析,首先获取elf文件的符号表

$ readelf -s main

可以得到elf文件中的变量、函数、文件等的信息,我们只需要关注变量format和name两个变量,首先分析format变量,从符号表中筛选出format变量的信息:

$ readelf -s main | grep format

结果如下:

 

这些信息是什么意思呢?结合符号表各列的定义,我们主要关注以下几项:

Value:0804a014 (变量的值的内存起始位置)

Size:13 (变量的大小)

Ndx:24 (变量属于哪个section)

而每个section存放的内容是不同的,常用的section定义如下:

  • .text: 已编译程序的机器代码
  • .rodata(read only data):只读数据
  • .data:已初始化的全局c变量,可读可写
  • .bss:未初始化的全局c变量,不占用实际空间,作占位符使用。
  • .symtab:符号表,存放程序中被定义和引用的函数和全局变量信息,不包含局部变量

Format变量存储在编号为24的区域,我们再看这个是什么区域:

$ readelf -S main | grep 24

 

可见format变量的值存储在.data区域,正是可读可写区!

再进入到.data中验证一下,

$ readelf -x .data main

 

看图片右边,正是format这个字符串。

 

我们再看name这个变量,用类似的方法,

$ readelf -s main | grep name

结果如下

 

注意观察,name这个变量也在24区,也在.data中,但长度只有4字节,而内存位置为0x0804a024

我们进入.data中一探究竟

 

我们需要计算内存位置,第二列的第一个字节内存位置为0x0804a01c,而name的内存位置是0x0804a024,相差8个字节,我们往后数8字节,name的值就出来了。就是图上用红框标记的部分,name的值不是一个字符串吗,为什么变成了这样一串数字?因为这串数字是内存地址,指向字符串的真正位置!那这个地址到底在哪里呢?考虑我是inter 89386架构,采用小端存储数据。所以,图中的e0840408应该从后往前,一个个字节读,正是0x080484e0,那这个地址又指向哪里呢?我们需要查看各个section的内存范围:

$ readelf -S main

在Addr这一列可以看到,内存范围,观察得到.rodata的范围,起始是0x080484d8,结束是0x080484e8,而name变量指向的地址0x080484e0正在.rodata区间(不同机器范围可能不同,重点在于该地址是在哪个区间内)。

我们到.rodata验证一下:

$ readelf -x .rodata main

 

图片右边正是name对应的字符串!而该字符串在.rodata中,可读不可写。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM