C++数组与指针

249 阅读2分钟

一、背景描述

对于数组,大家肯定不陌生,众所周知c++中数组变量其实就是个指针,但笔者今天在阅读一个开源项目时发现一个奇怪的现象见下图,下面我们一起来探索下这里头到底有啥玄机呢?代码如下:

image.png

//对应的函数接受参数都是指针类型,但上图中一个传的参数是数组变量取地址,一个传的是数组变量
void     bzero(void *, size_t) __POSIX_C_DEPRECATED(200112L);

ssize_t  write(int __fd, const void * __buf, size_t __nbyte) __DARWIN_ALIAS_C(write);

二、数组与指针

因为上文中用到了数组和指针,所以我们简单通过一段代码来复习下 image.png

  • 在很多用到数组名字的地方,编译器会自动将其替换为一个指向首元素的指针,如下图。

image.png

  • 指向数组元素的指针可以执行所有迭代器元素,比如说递增,比较,两个指针相减等等。
  • 和迭代器一样,两个指针相减的结果就是它们之间的距离,所以我们可以用来计算长度。
  • cout对字符数组重载了<< 符,导致输出的整个数组的内容,如果想对打印字符串元素的地址得通过printf可以输出。

三、问题分析

那我们再回到我们问题上来看,细心的读者看到printf可以打印地址,那我们要不试打印下 &chschs两者的地址呢?走说干就干

image.png

  • 很惊呀!两者的地址居然一样。那按照这个结果看起来开源代码是没啥问题。
  • 但是讲道理字符数组变量编译器不是替换为指向首元素的指针了么?再取一遍地址不应该是获取指向指针的地址么?为啥两者居然还一致呢? 带着疑惑笔者尝试进行了一遍汇编,看看这里头到底发生了啥。

具体产生汇编码的方法可以参考该文

.LC0:
        .string "%p \n"
        .text
        .globl  _Z10test_arrayv
        .type   _Z10test_arrayv, @function
main:
.LFB1197:
        .cfi_startproc  // 用在每个函数的开始,用于初始化一些内部数据结构
        pushq   %rbp   // 保存旧的帧指针,相当于创建新的栈帧
        .cfi_def_cfa_offset 16  // 修改计算 CFA 的规则。 寄存器保持不变,但偏移量是新的。 请注意,它是将添加到定义的寄存器以计算 CFA 地址的绝对偏移量。
        .cfi_offset 6, -16 // 寄存器的先前值保存在 CFA 的偏移量处。
        movq    %rsp, %rbp // 
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movb    $97, -16(%rbp) // 将字符a放入栈地址-16上
        movb    $98, -15(%rbp)
        movb    $99, -14(%rbp)
        movb    $100, -13(%rbp)
        movb    $101, -12(%rbp)
        movb    $102, -11(%rbp)
        leaq    -16(%rbp), %rax // 将栈地址-16 传递给返回值
        movq    %rax, %rsi // 将返回值作为第二个参数传递
        movl    $.LC0, %edi  // 将上面的$.LC0(见上面) 传递给通用寄存器
        movl    $0, %eax 
        call    printf // 调用printf函数
        leaq    -16(%rbp), %rax // 将栈地址-16 传递给返回值
        movq    %rax, %rsi // 同上
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

从汇编码上看,在当做函数参数时,无论是对数组变量取地址,还是直接传递的是数组变量,其实本质上编译器都会替换为数组首元素的地址-编码中两次call printf时都有这么一样的指令 leaq -16(%rbp), %rax

附录:

汇编码快速入门可以参考该文 再分享一个在线生成汇编码并可以提示查看的平台