• 数组与指针实验


    指针与数组实验

    先简单看一下以下c代码

    #include 
    #include 
    int main() {
      char array[10];
      array[0] = 0x56;
      array[1] = 0x78;
      array[9] = 0x12;
      char *p = (char *)malloc(10);
      p[0] = 0x34;
      p[1] = 0x12;
      printf("%p\n%p\n%p\n%p\n", array, &array, p, &p);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    生成目标文件并进行反汇编

    gcc point.c -o point  -g # 加上调试选项
    objdump -S point > point.s -g
    gcc point.c -o point -fno-stack-protector -g #关掉栈保护机制
    
    • 1
    • 2
    • 3

    截取主要汇编代码

    0000000000001189 <main>:
    #include 
    #include 
    int main() {
        1189:	f3 0f 1e fa          	endbr64 
        118d:	55                   	push   %rbp
        118e:	48 89 e5             	mov    %rsp,%rbp
        1191:	48 83 ec 20          	sub    $0x20,%rsp
        1195:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
        119c:	00 00 
        119e:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
        11a2:	31 c0                	xor    %eax,%eax
      char array[10];
      array[0] = 0x56;
        11a4:	c6 45 ee 56          	movb   $0x56,-0x12(%rbp)
      array[1] = 0x78;
        11a8:	c6 45 ef 78          	movb   $0x78,-0x11(%rbp)
      array[9] = 0x12;
        11ac:	c6 45 f7 12          	movb   $0x12,-0x9(%rbp)
      char *p = (char *)malloc(10);
        11b0:	bf 0a 00 00 00       	mov    $0xa,%edi
        11b5:	e8 d6 fe ff ff       	callq  1090 <malloc@plt>
        11ba:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
      p[0] = 0x34;
        11be:	48 8b 45 e0          	mov    -0x20(%rbp),%rax
        11c2:	c6 00 34             	movb   $0x34,(%rax)
      p[1] = 0x12;
        11c5:	48 8b 45 e0          	mov    -0x20(%rbp),%rax
        11c9:	48 83 c0 01          	add    $0x1,%rax
        11cd:	c6 00 12             	movb   $0x12,(%rax)
      printf("%p\n%p\n%p\n%p\n", array, &array, p, &p);
        11d0:	48 8b 4d e0          	mov    -0x20(%rbp),%rcx
        11d4:	48 8d 75 e0          	lea    -0x20(%rbp),%rsi
        11d8:	48 8d 55 ee          	lea    -0x12(%rbp),%rdx
        11dc:	48 8d 45 ee          	lea    -0x12(%rbp),%rax
        11e0:	49 89 f0             	mov    %rsi,%r8
        11e3:	48 89 c6             	mov    %rax,%rsi
        11e6:	48 8d 3d 17 0e 00 00 	lea    0xe17(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
        11ed:	b8 00 00 00 00       	mov    $0x0,%eax
        11f2:	e8 89 fe ff ff       	callq  1080 <printf@plt>
        11f7:	b8 00 00 00 00       	mov    $0x0,%eax
        11fc:	48 8b 7d f8          	mov    -0x8(%rbp),%rdi
        1200:	64 48 33 3c 25 28 00 	xor    %fs:0x28,%rdi
        1207:	00 00 
        1209:	74 05                	je     1210 <main+0x87>
        120b:	e8 60 fe ff ff       	callq  1070 <__stack_chk_fail@plt>
        1210:	c9                   	leaveq 
        1211:	c3                   	retq   
        1212:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
        1219:	00 00 00 
        121c:	0f 1f 40 00          	nopl   0x0(%rax)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    fs:0x28与linux的堆栈保护机制有关,为简化问题,将该机制关掉。
    面试官不讲武德,居然让我讲讲蠕虫和金丝雀!
    Why does this memory address %fs:0x28 ( fs[0x28] ) have a random value?
    再次反汇编,并加上一些注释

    0000000000001169 <main>:
    #include 
    #include 
    int main() {
        1169:	f3 0f 1e fa          	endbr64 
        116d:	55                   	push   %rbp
        116e:	48 89 e5             	mov    %rsp,%rbp
        1171:	48 83 ec 20          	sub    $0x20,%rsp   # 申请32字节堆栈空间
        # rbp-1到rbp-10为array空间,array为rbp-10地址别名,array[9]即array+9 * sizeof(char)
        # 数组赋值一步到位
      char array[10];
      array[0] = 0x56;
        1175:	c6 45 f6 56          	movb   $0x56,-0xa(%rbp) 
      array[1] = 0x78;
        1179:	c6 45 f7 78          	movb   $0x78,-0x9(%rbp)
      array[9] = 0x12;
        117d:	c6 45 ff 12          	movb   $0x12,-0x1(%rbp)
      char *p = (char *)malloc(10);     # 指针变量也是局部变量,占8个字节 rbp-17到rbp-24
        1181:	bf 0a 00 00 00       	mov    $0xa,%edi   # 申请10个字节空间
        1186:	e8 e5 fe ff ff       	callq  1070 <malloc@plt>
        118b:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
      # 指针赋值需要两步/三步
      p[0] = 0x34;
        118f:	48 8b 45 e8          	mov    -0x18(%rbp),%rax # 取得指针变量中存储的地址
        1193:	c6 00 34             	movb   $0x34,(%rax)     # 将值写入该地址
      p[1] = 0x12;
        1196:	48 8b 45 e8          	mov    -0x18(%rbp),%rax # 取得指针变量中存储的地址
        119a:	48 83 c0 01          	add    $0x1,%rax        # 设置偏移量
        119e:	c6 00 12             	movb   $0x12,(%rax)     # 将值写入该地址 
        # lea: Load Effective Address,即装入有效地址的意思,它的操作数就是地址
      printf("%p\n%p\n%p\n%p\n", array, &array, p, &p);
        11a1:	48 8b 4d e8          	mov    -0x18(%rbp),%rcx # p
        11a5:	48 8d 75 e8          	lea    -0x18(%rbp),%rsi # &p
        11a9:	48 8d 55 f6          	lea    -0xa(%rbp),%rdx  # &array
        11ad:	48 8d 45 f6          	lea    -0xa(%rbp),%rax  # array
        11b1:	49 89 f0             	mov    %rsi,%r8
        11b4:	48 89 c6             	mov    %rax,%rsi
        11b7:	48 8d 3d 46 0e 00 00 	lea    0xe46(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
        11be:	b8 00 00 00 00       	mov    $0x0,%eax
        11c3:	e8 98 fe ff ff       	callq  1060 <printf@plt>
        11c8:	b8 00 00 00 00       	mov    $0x0,%eax
        11cd:	c9                   	leaveq 
        11ce:	c3                   	retq   
        11cf:	90                   	nop
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在这里插入图片描述
    C++内存模型以及寄存器指针rsp和rbp
    看完汇编代码,可以很容易猜到:p与&p结果不一样,array与&array结果一致。

    0x7ffd85499f96
    0x7ffd85499f96
    0x55e303c302a0
    0x7ffd85499f88
    
    • 1
    • 2
    • 3
    • 4

    使用gdb显示相关数据

    可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
    
    x/<n/f/u> <addr>
    
    n:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内容,
    一个内存单元的大小由第三个参数u定义。
    
    f:表示addr指向的内存内容的输出格式,s对应输出字符串,此处需特别注意输出整型数据的格式:
      x 按十六进制格式显示变量.
      d 按十进制格式显示变量。
      u 按十进制格式显示无符号整型。
      o 按八进制格式显示变量。
      t 按二进制格式显示变量。
      a 按十六进制格式显示变量。
      c 按字符格式显示变量。
      f 按浮点数格式显示变量。
    
    u:就是指以多少个字节作为一个内存单元-unit,默认为4。u还可以用被一些字符表示:
      如b=1 byte, h=2 bytes,w=4 bytes,g=8 bytes.
    
    <addr>:表示内存地址。
    
    
    Reading symbols from ./point...
    (gdb) b point.c:11
    Breakpoint 1 at 0x11a1: file point.c, line 11.
    (gdb) r
    Starting program: /root/tianchi/codebase/expr/point/point 
    
    Breakpoint 1, main () at point.c:11
    11        printf("%p\n%p\n%p\n%p\n", array, &array, p, &p);
    (gdb) p &array
    $1 = (char (*)[10]) 0x7fffffffdef6
    (gdb) p &p
    $2 = (char **) 0x7fffffffdee8
    (gdb) p p
    $3 = 0x5555555592a0 "4\022"
    (gdb) x/10ab 0x5555555592a0  # 查看指针变量指向区域内容
    0x5555555592a0: 0x34    0x12    0x0     0x0     0x0     0x0     0x0     0x0
    0x5555555592a8: 0x0     0x0
    (gdb) x/32tb 0x7fffffffdee8	 # 打印该地址之上32字节的内容,二进制
    0x7fffffffdee8: 10100000        10010010        01010101        01010101        01010101        01010101        00000000        00000000
    0x7fffffffdef0: 11110000        11011111        11111111        11111111        11111111        01111111        01010110        01111000
    0x7fffffffdef8: 00000000        00000000        00000000        00000000        00000000        00000000        00000000        00010010
    0x7fffffffdf00: 00000000        00000000        00000000        00000000        00000000        00000000        00000000        00000000
    (gdb) x/32ab 0x7fffffffdee8  # 打印该地址之上32字节的内容,十六进制,有些前面有fff是符号位补全的原因(符号位为1),小端方式,低位在低地址
    0x7fffffffdee8: 0xffffffffffffffa0      0xffffffffffffff92      0x55    0x55    0x55    0x55    0x0     0x0
    0x7fffffffdef0: 0xfffffffffffffff0      0xffffffffffffffdf      0xffffffffffffffff      0xffffffffffffffff      0xffffffffffffffff      0x7f    0x56    0x78
    0x7fffffffdef8: 0x0     0x0     0x0     0x0     0x0     0x0     0x0     0x12
    0x7fffffffdf00: 0x0     0x0     0x0     0x0     0x0     0x0     0x0     0x0
    (gdb) x/1tg 0x7fffffffdee8 # 打印指针变量内容 二进制
    0x7fffffffdee8: 0000000000000000010101010101010101010101010101011001001010100000
    (gdb) x/1ag 0x7fffffffdee8 # 打印指针变量内容 十六进制
    0x7fffffffdee8: 0x5555555592a0
    (gdb) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    注意区分“地址y”和“地址y的内容”之间的区别

    指针数组
    保存数据的地址保存数据
    间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。
    如果指针有一个下标[i],就把指针的内容加上i为地址,从中提取数据
    直接访问数据,a[i]只是简单地以a+i为地址取得数据
    通常用于动态结果通常用于存储固定数目且数据类型相同的元素
    相关的函数为malloc,free隐式分配和删除
    通常指向匿名数据自身即为变量名
  • 相关阅读:
    C++布隆过滤器和哈西切分
    代码坏味道与重构之注释
    Django系列6-数据库操作
    单片机开发——宠物自动饮水器方案
    vue总结(一)
    实战分析Java的异步编程,并通过CompletableFuture进行高效调优
    开发笔记:如何对【动态链接库】文件进行加密保护?
    软件测试项目实战经验附视频以及源码【商城项目,app项目,电商项目,银行项目,医药项目,金融项目】(web+app+h5+小程序)
    查找大于某个数的最小质数
    Mysql-SQL优化
  • 原文地址:https://blog.csdn.net/freedom1523646952/article/details/127891423