浅谈 C 字符串数组的内存布局


首先感谢秋豪大佬对文章的校对以及耐心的指导 先献上我的膝盖 or2
整理了四种常见的使用 C 语言字符串数组的形式
分别对这以下几种的内存布局进行分析

char* array[] = {
    "line1",
    "line10",
    "line100",
    "line1000",
};
char **array = {
    "line1",
    "line10",
    "line100",
    "line1000"
};
char array[][4] = {
    "line1",
    "line10",
    "line100",
    "line1000"
};
char array[][0x10] = {
    "line1",
    "line10",
    "line100",
    "line1000"
};
char * array = {
    "line1",
    "line10",
    "line100",
    "line1000"
};

默认读者已经有了以下基本知识:

  1. C 语言基础
  2. GDB 调试器的使用
  3. Linux 内存布局

环境

Ubuntu 18.04 x64

参考资料

Linux 程序 64 位内存布局:


image.png

image.png
gdb-peda$ vmmap
Start              End                Perm      Name
0x0000555555554000 0x0000555555555000 r-xp      /home/sun/Desktop/c/research/a.out
0x0000555555754000 0x0000555555755000 r--p      /home/sun/Desktop/c/research/a.out
0x0000555555755000 0x0000555555756000 rw-p      /home/sun/Desktop/c/research/a.out
0x00007ffff79e4000 0x00007ffff7bcb000 r-xp      /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bcb000 0x00007ffff7dcb000 ---p      /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcb000 0x00007ffff7dcf000 r--p      /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 0x00007ffff7dd1000 rw-p      /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dd1000 0x00007ffff7dd5000 rw-p      mapped
0x00007ffff7dd5000 0x00007ffff7dfc000 r-xp      /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7fdd000 0x00007ffff7fdf000 rw-p      mapped
0x00007ffff7ff7000 0x00007ffff7ffa000 r--p      [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp      [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p      /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p      /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p      mapped
0x00007ffffffdd000 0x00007ffffffff000 rw-p      [stack]
0xffffffffff600000 0xffffffffff601000 r-xp      [vsyscall]

第一种

char* array[] = {
    "line1",
    "line10",
    "line100",
    "line1000",
};

首先该变量为一个数组,元素的类型为指向字符的指针,在定义时就包含四个元素,那么在栈中就占有 4 * WORD_SIZE = 32 字节的空间,这 32 字节的空间用来分别存放指向真正字符串的指针。
在使用 execve 这个函数的时候,参考 man 手册,即可发现,参数的类型即为这种类型:

EXECVE(2)                  Linux Programmer's Manual                 EXECVE(2)

NAME
       execve - execute program

SYNOPSIS
       #include <unistd.h>

       int execve(const char *filename, char *const argv[],
                  char *const envp[]);
[----------------------------------registers-----------------------------------]
RAX: 0x555555554805 ("line1000")
RBX: 0x0
RCX: 0x555555554760 (<__libc_csu_init>: push   r15)
RDX: 0x0
RSI: 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1
RBP: 0x7fffffffdac0 --> 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:   push   r15)
RSP: 0x7fffffffda90 --> 0x5555555547f0 --> 0x696c0031656e696c ('line1')
RIP: 0x5555555546c2 (<function1+88>:    leave)
R8 : 0x7ffff7dd0d80 --> 0x0
R9 : 0x7ffff7dd0d80 --> 0x0
R10: 0x2
R11: 0x3
R12: 0x555555554560 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbb0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555546b2 <function1+72>:       xor    rdx,QWORD PTR fs:0x28
   0x5555555546bb <function1+81>:       je     0x5555555546c2 <function1+88>
   0x5555555546bd <function1+83>:       call   0x555555554540 <__stack_chk_fail@plt>
=> 0x5555555546c2 <function1+88>:       leave
   0x5555555546c3 <function1+89>:       ret
   0x5555555546c4 <function2>:  push   rbp
   0x5555555546c5 <function2+1>:        mov    rbp,rsp
   0x5555555546c8 <function2+4>:        lea    rax,[rip+0x121]        # 0x5555555547f0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda90 --> 0x5555555547f0 --> 0x696c0031656e696c ('line1')
0008| 0x7fffffffda98 --> 0x5555555547f6 --> 0x6c003031656e696c ('line10')
0016| 0x7fffffffdaa0 --> 0x5555555547fd --> 0x303031656e696c ('line100')
0024| 0x7fffffffdaa8 --> 0x555555554805 ("line1000")
0032| 0x7fffffffdab0 --> 0x555555554760 (<__libc_csu_init>:     push   r15)
0040| 0x7fffffffdab8 --> 0x2d54ce84036a2500
0048| 0x7fffffffdac0 --> 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:  push   r15)
0056| 0x7fffffffdac8 --> 0x55555555473a (<main+14>:     mov    eax,0x0)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00005555555546c2      10      }
gdb-peda$ p array
$2 = {0x5555555547f0 "line1", 0x5555555547f6 "line10", 0x5555555547fd "line100",
  0x555555554805 "line1000"}
gdb-peda$ x /32xb array
0x7fffffffda90: 0xf0    0x47    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffda98: 0xf6    0x47    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffdaa0: 0xfd    0x47    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffdaa8: 0x05    0x48    0x55    0x55    0x55    0x55    0x00    0x00
gdb-peda$

第二种

char **array = {
    "line1",
    "line10",
    "line100",
    "line1000"
};

这种写法在编译的时候会存在 Warning:

➜  ~ gcc ./main.c 
./main.c:2:5: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     "line1",
     ^~~~~~~
./main.c:2:5: note: (near initialization for ‘array’)
./main.c:3:5: warning: excess elements in scalar initializer
     "line10",
     ^~~~~~~~
./main.c:3:5: note: (near initialization for ‘array’)
./main.c:4:5: warning: excess elements in scalar initializer
     "line100",
     ^~~~~~~~~
./main.c:4:5: note: (near initialization for ‘array’)
./main.c:5:5: warning: excess elements in scalar initializer
     "line1000"
     ^~~~~~~~~~
./main.c:5:5: note: (near initialization for ‘array’)
excess elements in scalar initializer
过多的元素在变量的初始化中

C 语言中有两个概念:(参考 C11-§6.2.5 Types)

  • scalar type
  • aggregate types

可以简单记忆为:包含单个值的变量类型为scalar type,而多个值的变量为aggregate types
注意有一个容易搞错的地方:union 类型为scalar type, 因为在内存中同时只能包含一个数据

image.png

image.png

这里的 Warning 就是警告我们:

char ** 是一个标量,但是我们使用了用来初始化聚合类型的方式来初始化了

这样,我们在花括号中后面的值则会被忽略掉

[----------------------------------registers-----------------------------------]
RAX: 0x5555555546b4 --> 0x31656e696c ('line1')
RBX: 0x0 
RCX: 0x555555554630 (<__libc_csu_init>: push   r15)
RDX: 0x7fffffffdbf8 --> 0x7fffffffe01e ("rvm_version=1.29.3 (latest)")
RSI: 0x7fffffffdbe8 --> 0x7fffffffdffb ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1 
RBP: 0x7fffffffdae0 --> 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>:   push   r15)
RSP: 0x7fffffffdae0 --> 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>:   push   r15)
RIP: 0x55555555460a (<function+16>: pop    rbp)
R8 : 0x7ffff7dd0d80 --> 0x0 
R9 : 0x7ffff7dd0d80 --> 0x0 
R10: 0x2 
R11: 0x3 
R12: 0x5555555544f0 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbe0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555545fe <function+4>: lea    rax,[rip+0xaf]        # 0x5555555546b4
   0x555555554605 <function+11>:    mov    QWORD PTR [rbp-0x8],rax
   0x555555554609 <function+15>:    nop
=> 0x55555555460a <function+16>:    pop    rbp
   0x55555555460b <function+17>:    ret    
   0x55555555460c <main>:   push   rbp
   0x55555555460d <main+1>: mov    rbp,rsp
   0x555555554610 <main+4>: sub    rsp,0x10
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdae0 --> 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>:  push   r15)
0008| 0x7fffffffdae8 --> 0x555555554625 (<main+25>: mov    eax,0x0)
0016| 0x7fffffffdaf0 --> 0x7fffffffdbe8 --> 0x7fffffffdffb ("/home/sun/Desktop/c/research/a.out")
0024| 0x7fffffffdaf8 --> 0x100000000 
0032| 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>: push   r15)
0040| 0x7fffffffdb08 --> 0x7ffff7a05b97 (<__libc_start_main+231>:   mov    edi,eax)
0048| 0x7fffffffdb10 --> 0x1 
0056| 0x7fffffffdb18 --> 0x7fffffffdbe8 --> 0x7fffffffdffb ("/home/sun/Desktop/c/research/a.out")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555460a  40  }
gdb-peda$ p array
$1 = (char **) 0x5555555546b4
gdb-peda$ x /32xb array
0x5555555546b4: 0x6c    0x69    0x6e    0x65    0x31    0x00    0x00    0x00
0x5555555546bc: 0x01    0x1b    0x03    0x3b    0x40    0x00    0x00    0x00
0x5555555546c4: 0x07    0x00    0x00    0x00    0x14    0xfe    0xff    0xff
0x5555555546cc: 0x8c    0x00    0x00    0x00    0x24    0xfe    0xff    0xff
gdb-peda$ p *array
$2 = 0x31656e696c <error: Cannot access memory at address 0x31656e696c>
gdb-peda$ p **array
Cannot access memory at address 0x31656e696c
gdb-peda$ 

可以看到我们在访问:*array 的时候报错:

gdb-peda$ p *array
$2 = 0x31656e696c <error: Cannot access memory at address 0x31656e696c>

是因为 array 的类型为指针类型,并且我们在初始化的时候它的值为:line1
在访问的时候,line1 被当成了一个地址去访问,而它当然不是一个合法的地址,所以报错。
当我们将代码进行调整:

int function(){
    char ** array = {
        "12345678AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
        "line10",
        "line100",
        "line1000"
    };
}

int main(int argc, char* argv[]){
    function();
}

编译后再进行调试,可以发现,array所在的栈空间存储的数据为:

12345678AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

而后面的 line10, line100 等被忽略

[----------------------------------registers-----------------------------------]
RAX: 0x5555555546b8 ("12345678", 'A' <repeats 52 times>)
RBX: 0x0 
RCX: 0x555555554630 (<__libc_csu_init>: push   r15)
RDX: 0x7fffffffdbf8 --> 0x7fffffffe01d ("rvm_version=1.29.3 (latest)")
RSI: 0x7fffffffdbe8 --> 0x7fffffffdffa ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1 
RBP: 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>:  push   r15)
RSP: 0x7fffffffdae8 --> 0x555555554625 (<main+25>:  mov    eax,0x0)
RIP: 0x55555555460b (<function+17>: ret)
R8 : 0x7ffff7dd0d80 --> 0x0 
R9 : 0x7ffff7dd0d80 --> 0x0 
R10: 0x2 
R11: 0x3 
R12: 0x5555555544f0 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbe0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555554605 <function+11>:    mov    QWORD PTR [rbp-0x8],rax
   0x555555554609 <function+15>:    nop
   0x55555555460a <function+16>:    pop    rbp
=> 0x55555555460b <function+17>:    ret    
   0x55555555460c <main>:   push   rbp
   0x55555555460d <main+1>: mov    rbp,rsp
   0x555555554610 <main+4>: sub    rsp,0x10
   0x555555554614 <main+8>: mov    DWORD PTR [rbp-0x4],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdae8 --> 0x555555554625 (<main+25>: mov    eax,0x0)
0008| 0x7fffffffdaf0 --> 0x7fffffffdbe8 --> 0x7fffffffdffa ("/home/sun/Desktop/c/research/a.out")
0016| 0x7fffffffdaf8 --> 0x100000000 
0024| 0x7fffffffdb00 --> 0x555555554630 (<__libc_csu_init>: push   r15)
0032| 0x7fffffffdb08 --> 0x7ffff7a05b97 (<__libc_start_main+231>:   mov    edi,eax)
0040| 0x7fffffffdb10 --> 0x1 
0048| 0x7fffffffdb18 --> 0x7fffffffdbe8 --> 0x7fffffffdffa ("/home/sun/Desktop/c/research/a.out")
0056| 0x7fffffffdb20 --> 0x100008000 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555460b  40  }
gdb-peda$ p array
$1 = (char **) 0x5555555546b8
gdb-peda$ x /32xb array
0x5555555546b8: 0x31    0x32    0x33    0x34    0x35    0x36    0x37    0x38
0x5555555546c0: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x5555555546c8: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x5555555546d0: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
gdb-peda$ 

第三种:第二位长度小于最长的字符串

char array[][4] = {
    "line1",
    "line10",
    "line100",
    "line1000"
};

该变量为一个二维数组,第二维的长度为 4,二维数组在内存中事实上是一维存储的(整个内存空间都可以视为一维的,只是存在了数据结构才会将一维扩展为各种各样的结构),那么我们需要访问二维数组的其中一个元素,事实上是通过偏移来计算出来的,那么编译器就必须要求我们在定义二维数组的时候指定第二维的大小,如果不指定,则无法通过索引来访问二维数组的一个元素(事实上二维数组的一个元素就是一个一维数组)
在这个例子中,这个变量是一个栈变量(根据下面 gdb 调试日志可以看出地址 0x7f ,该空间为栈空间),存储在栈中,地址为 0x7fffffffdaa0,并且是线性存储。由于在定义时,二维数组的第二位长度为 4,编译器就会将长度大于 4 的字符串截断,只留下前四个字节,那么如果我们使用索引去访问它的第二个元素:也就会访问到 0x7fffffffdaa0 + 4 = 0x7fffffffdaa4,也就是第二个 line

[----------------------------------registers-----------------------------------]
RAX: 0x656e696c656e696c ('lineline')
RBX: 0x0
RCX: 0x555555554760 (<__libc_csu_init>: push   r15)
RDX: 0x0
RSI: 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1
RBP: 0x7fffffffdac0 --> 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:   push   r15)
RSP: 0x7fffffffdaa0 ("linelinelineline`GUUUU")
RIP: 0x555555554718 (<function3+66>:    leave)
R8 : 0x7ffff7dd0d80 --> 0x0
R9 : 0x7ffff7dd0d80 --> 0x0
R10: 0x2
R11: 0x3
R12: 0x555555554560 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbb0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555554708 <function3+50>:       xor    rdx,QWORD PTR fs:0x28
   0x555555554711 <function3+59>:       je     0x555555554718 <function3+66>
   0x555555554713 <function3+61>:       call   0x555555554540 <__stack_chk_fail@plt>
=> 0x555555554718 <function3+66>:       leave
   0x555555554719 <function3+67>:       ret
   0x55555555471a <function4>:  push   rbp
   0x55555555471b <function4+1>:        mov    rbp,rsp
   0x55555555471e <function4+4>:        lea    rax,[rip+0xcb]        # 0x5555555547f0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdaa0 ("linelinelineline`GUUUU")
0008| 0x7fffffffdaa8 ("lineline`GUUUU")
0016| 0x7fffffffdab0 --> 0x555555554760 (<__libc_csu_init>:     push   r15)
0024| 0x7fffffffdab8 --> 0x2d54ce84036a2500
0032| 0x7fffffffdac0 --> 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:  push   r15)
0040| 0x7fffffffdac8 --> 0x55555555474e (<main+34>:     mov    eax,0x0)
0048| 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:     push   r15)
0056| 0x7fffffffdad8 --> 0x7ffff7a05b97 (<__libc_start_main+231>:       mov    edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000555555554718      29      }
gdb-peda$ p array
$5 = {"line", "line", "line", "line"}
gdb-peda$ x /32xb array
0x7fffffffdaa0: 0x6c    0x69    0x6e    0x65    0x6c    0x69    0x6e    0x65
0x7fffffffdaa8: 0x6c    0x69    0x6e    0x65    0x6c    0x69    0x6e    0x65
0x7fffffffdab0: 0x60    0x47    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffdab8: 0x00    0x25    0x6a    0x03    0x84    0xce    0x54    0x2d
gdb-peda$ x /4s array
0x7fffffffdaa0: "linelinelineline`GUUUU"
0x7fffffffdab7: ""
0x7fffffffdab8: ""
0x7fffffffdab9: "%j\003\204\316T-\320\332\377\377\377\177"
gdb-peda$

第三种:第二位长度大于最长的字符串

char array[][0x10] = {
    "line1",
    "line10",
    "line100",
    "line1000"
};

这次定义二维数组的第二位尺寸的时候大于了最长字符串的长度,可以看到编译器就会按照我们定义的尺寸为我们在栈上分配内存,得到的矩阵是一个非常规整方正的矩阵。
这种情况是可以通过索引访问的,访问的结果为得到一个固定长度的字符数组(包括后面的若干个空字节)

[----------------------------------registers-----------------------------------]
RAX: 0x30303031656e696c ('line1000')
RBX: 0x0
RCX: 0x0
RDX: 0x0
RSI: 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1
RBP: 0x7fffffffdad0 --> 0x5555555547f0 (<__libc_csu_init>:      push   r15)
RSP: 0x7fffffffdac8 --> 0x5555555547dd (<main+34>:      mov    eax,0x0)
RIP: 0x5555555547a8 (<function3+210>:   ret)
R8 : 0x7ffff7dd0d80 --> 0x0
R9 : 0x7ffff7dd0d80 --> 0x0
R10: 0x2
R11: 0x3
R12: 0x555555554560 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbb0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555547a0 <function3+202>:      je     0x5555555547a7 <function3+209>
   0x5555555547a2 <function3+204>:      call   0x555555554540 <__stack_chk_fail@plt>
   0x5555555547a7 <function3+209>:      leave
=> 0x5555555547a8 <function3+210>:      ret
   0x5555555547a9 <function4>:  push   rbp
   0x5555555547aa <function4+1>:        mov    rbp,rsp
   0x5555555547ad <function4+4>:        lea    rax,[rip+0xc0]        # 0x555555554874
   0x5555555547b4 <function4+11>:       mov    QWORD PTR [rbp-0x8],rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdac8 --> 0x5555555547dd (<main+34>:     mov    eax,0x0)
0008| 0x7fffffffdad0 --> 0x5555555547f0 (<__libc_csu_init>:     push   r15)
0016| 0x7fffffffdad8 --> 0x7ffff7a05b97 (<__libc_start_main+231>:       mov    edi,eax)
0024| 0x7fffffffdae0 --> 0x1
0032| 0x7fffffffdae8 --> 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
0040| 0x7fffffffdaf0 --> 0x100008000
0048| 0x7fffffffdaf8 --> 0x5555555547bb (<main>:        push   rbp)
0056| 0x7fffffffdb00 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00005555555547a8      29      }
gdb-peda$ p array
$3 = {"line1", '\000' <repeats 26 times>, "line10", '\000' <repeats 25 times>,
  "line100", '\000' <repeats 24 times>, "line1000", '\000' <repeats 23 times>}
gdb-peda$ x /64xb array
0x7fffffffda30: 0x6c    0x69    0x6e    0x65    0x31    0x00    0x00    0x00
0x7fffffffda38: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffda40: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffda48: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffda50: 0x6c    0x69    0x6e    0x65    0x31    0x30    0x00    0x00
0x7fffffffda58: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffda60: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffda68: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
gdb-peda$ p array[0]
$4 = "line1", '\000' <repeats 26 times>
gdb-peda$

第四种

char * array = {
    "line1",
    "line10",
    "line100",
    "line1000"
};

这种和第二种相同,同样编译器警告我们初始化一个标量的时候参数过多,这里不再赘述
事实上这种写法是不合逻辑的
贴出 gdb 日志

[----------------------------------registers-----------------------------------]
RAX: 0x5555555547f0 --> 0x696c0031656e696c ('line1')
RBX: 0x0
RCX: 0x555555554760 (<__libc_csu_init>: push   r15)
RDX: 0x0
RSI: 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
RDI: 0x1
RBP: 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:      push   r15)
RSP: 0x7fffffffdac8 --> 0x555555554758 (<main+44>:      mov    eax,0x0)
RIP: 0x55555555472b (<function4+17>:    ret)
R8 : 0x7ffff7dd0d80 --> 0x0
R9 : 0x7ffff7dd0d80 --> 0x0
R10: 0x2
R11: 0x3
R12: 0x555555554560 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffdbb0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555554725 <function4+11>:       mov    QWORD PTR [rbp-0x8],rax
   0x555555554729 <function4+15>:       nop
   0x55555555472a <function4+16>:       pop    rbp
=> 0x55555555472b <function4+17>:       ret
   0x55555555472c <main>:       push   rbp
   0x55555555472d <main+1>:     mov    rbp,rsp
   0x555555554730 <main+4>:     mov    eax,0x0
   0x555555554735 <main+9>:     call   0x55555555466a <function1>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdac8 --> 0x555555554758 (<main+44>:     mov    eax,0x0)
0008| 0x7fffffffdad0 --> 0x555555554760 (<__libc_csu_init>:     push   r15)
0016| 0x7fffffffdad8 --> 0x7ffff7a05b97 (<__libc_start_main+231>:       mov    edi,eax)
0024| 0x7fffffffdae0 --> 0x1
0032| 0x7fffffffdae8 --> 0x7fffffffdbb8 --> 0x7fffffffdfb7 ("/home/sun/Desktop/c/research/a.out")
0040| 0x7fffffffdaf0 --> 0x100008000
0048| 0x7fffffffdaf8 --> 0x55555555472c (<main>:        push   rbp)
0056| 0x7fffffffdb00 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555472b      38      }
gdb-peda$ p array
$6 = 0x5555555547f0 "line1"
gdb-peda$ x /32xb array
0x5555555547f0: 0x6c    0x69    0x6e    0x65    0x31    0x00    0x6c    0x69
0x5555555547f8: 0x6e    0x65    0x31    0x30    0x00    0x6c    0x69    0x6e
0x555555554800: 0x65    0x31    0x30    0x30    0x00    0x6c    0x69    0x6e
0x555555554808: 0x65    0x31    0x30    0x30    0x30    0x00    0x00    0x00
gdb-peda$

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,027评论 8 265
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,082评论 1 44
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,387评论 3 44
  • 注: 效果:水彩边界 笔刷:水彩及其他 FIN
    拾壹曰11阅读 258评论 0 2
  • 因为我的局限太多,老天爷总是不放心,给我安排很多能忍耐我坏脾气的家人,给我以最大的包容,还给我安排三个榜样,让我可...
    悠然_3c09阅读 1,536评论 0 3