通过反汇编简要描述char *str和char str[]的区别以及调用printf输出字符串

结论:

假设以下代码存在于函数内部,即为初始化局部变量

  • char *pStr = "aaaaa";

  此时是把pStr初始化一个指向字符的指针,这个指将针被初始化在栈上或者寄存器中。
  字符串"aaaaa"作为常量直接被放在程序的.rodata段内,即一个固定的地方,在代码编译后,就已经将字符串的地址确定下来了,所以会将这个地址直接编码在指令中(加载时可能需重定位)。当初始化这个指针,为其赋值"aaaaa"时,会直接将常量"aaaaa"的地址赋予指针pStr
  • char arrayStr[] = "aaaaa";

  此时是初始化一个字符数组,"aaaaa"并非作为常量存放在.rodata段内,而是在调用拥有这行代码的函数时,初始化在其栈中,arrayStr"aaaaa"的首地址。
  因为str(字符数组的首地址)在初始化时就已经固定了,是该函数运行时栈某个位置的一个固定的地址,即其是一个常量,故以下最后两行赋值就是错误的:
  char arrayStr[] = "aaaaa";
  char *s = "abcd"
  
  str = "bbbbbb"; //error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  将常量字符串的地址赋值给不可被修改的数组首地址
  str = s;         //error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 将指向字符的指针赋值给不可被修改的数组首地址
  • pStrarrayStr作为printf参数时,实际上都是将字符数组的地址作为参数,此时如果格式化字段为%d就是打印这个地址,若是%c就打印字符数组。

    int printf(const char * formal ,[optional],[optional],[optional]...);
    

  

分析:

  • 初始化一个字符指针指向一个字符数组
linux >>> gcc  -Og  -o  pTest  -fno-stack-protector  pTest.c //编译源文件
linux >>> objdump -d -pTest.c                                //查看源文件反汇编代码
int pTest(){                               
  char *a = "aaaaaa";
  printf("%s",p);
}

                  
0000000000400546 
: 400526: 48 83 ec 08 sub $0x8,%rsp 40052a: be d4 05 40 00 mov $0x4005d4,%esi 40052f: bf db 05 40 00 mov $0x4005db,%edi 400534: b8 00 00 00 00 mov $0x0,%eax 400539: e8 c2 fe ff ff callq 400400 40053e: b8 00 00 00 00 mov $0x0,%eax 400543: 48 83 c4 08 add $0x8,%rsp 400547: c3 retq

我们看到本段c代码初始化了一个指向字符的指针,并将一个形如"aaaaa"的字符数组赋值给这个指针。
然后观察汇编代码,我们知道x86-64gcc会默认%rdi存放要调用的函数第一个参数,%rsi存放要调用函数的第二个参数。此时反汇编代码直接将0x4005d40赋值给寄存器%esi0x4005db赋值给寄存器%edi

所以我们去查看这两个地址上的数据:

 linux > objdump -s  pTest
Contents of section .rodata:
     4005d0 01000200 61616161 61610025 6400      ....aaaaaa.%d. 

你可能感兴趣的:(通过反汇编简要描述char *str和char str[]的区别以及调用printf输出字符串)