【CPU底层那些事(数组和指针真的一样吗?)】

1.对数组的理解
在这里插入图片描述
在这里插入图片描述

从上述简单的数组赋值,及其底层代码可以看出,数组赋值过程其实就是对一段连续的内存赋值。int是四个字节,每个元素的地址间隔都是四个字节。char是一个字节,每个元素的地址间隔都是一个字节。从上述我们就知道数组在内存中的样子。看到连续的内存就想到了指针。只要知道数组a的首地址和数组长度,就可以准确定位数组在内存中的空间位置。如下图所示。
在这里插入图片描述

2.数组和指针之间的关联
在这里插入图片描述

void fun1(int a[2])
{  
    a[0] = 1;
    a[1] = 2;
}
void fun2(int* a)
{  
    *a = 1;
    *(a + 1) = 2;
}
fun1(int*):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1
  mov rax, QWORD PTR [rbp-8]
  add rax, 4
  mov DWORD PTR [rax], 2
  nop
  pop rbp
  ret
fun2(int*):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1
  mov rax, QWORD PTR [rbp-8]
  add rax, 4
  mov DWORD PTR [rax], 2
  nop
  pop rbp
  ret

1.上述第一函数是一个简单的用数组变量做参数的函数。第二个函数是一个简单的用指针变量做参数的函数。从汇编指令可以看出,CPU在处理时,一模一样。
2.所以,不管你的数组有多大,你的编译器都不会像普通变量一样给你在堆栈中构建一个临时的数组变量,而是传递一个数组的内存首地址,并存放在一个临时的指针变量里面。
3.数组读写等于指针的*操作。

3.数组的调用
在这里插入图片描述

void fun1(int a[2])
{  
    a[0] = 1;
    a[1] = 2;
}
void fun2(int* a)
{  
    *a = 1;
    *(a + 1) = 2;
}
int call()
{
    int a[2] = {0,9};
    fun1(a);
    fun2(a);
    fun2(&a[0]);
}
fun1(int*):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1
  mov rax, QWORD PTR [rbp-8]
  add rax, 4
  mov DWORD PTR [rax], 2
  nop
  pop rbp
  ret
fun2(int*):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1
  mov rax, QWORD PTR [rbp-8]
  add rax, 4
  mov DWORD PTR [rax], 2
  nop
  pop rbp
  ret
call():
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-8], 0
  mov DWORD PTR [rbp-4], 9
  lea rax, [rbp-8]
  mov rdi, rax
  call fun1(int*)
  lea rax, [rbp-8]
  mov rdi, rax
  call fun2(int*)
  lea rax, [rbp-8]
  mov rdi, rax
  call fun2(int*)
  nop
  leave
  ret

1.上述三种调用的汇编指令完全相同,都是在传递数组的内存首地址。而非构建临时数组变量。
2.我们需要习惯的时传递指针的形式。
void func(int* array)
为了防止越界,一般会增加一个数组长度的参数。
void func(int* array, int length)

4.多维数组

在很多人眼里,对于多维数组在内存中的样子如下。
在这里插入图片描述

但其实多维数组在内存中是一维的,在cpu眼中,他们都是一维数组。如下图。
在这里插入图片描述
下面我们分别写三个函数,分别是一维,二维,三维。看看他们的汇编指令有什么区别。
1.一维数组在这里插入图片描述
2.二维数组
在这里插入图片描述
3.三维数组
在这里插入图片描述
从上述对一维、二维、三维数组赋值可以看出,他们的汇编指令完全相同。他们都是一段连续的一维内存。

void fun1()
{  
    int a[8];
    a[0] = 1;
    a[1] = 2;
    a[2] = 3;
    a[3] = 4;
    a[4] = 5;
    a[5] = 6;
    a[6] = 7;
    a[7] = 8;
}

void fun2()
{  
    int a[2][4];
    a[0][0] = 1;
    a[0][1] = 2;
    a[0][2] = 3;
    a[0][3] = 4;

    a[1][0] = 5;
    a[1][1] = 6;
    a[1][2] = 7;
    a[1][3] = 8;
}

void fun3()
{
    int a[2][2][2];
    a[0][0][0] = 1;
    a[0][0][1] = 2;

    a[0][1][0] = 3;
    a[0][1][1] = 4;

    a[1][0][0] = 5;
    a[1][0][1] = 6;

    a[1][1][0] = 7;
    a[1][1][1] = 8;
}
fun1():
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-32], 1
  mov DWORD PTR [rbp-28], 2
  mov DWORD PTR [rbp-24], 3
  mov DWORD PTR [rbp-20], 4
  mov DWORD PTR [rbp-16], 5
  mov DWORD PTR [rbp-12], 6
  mov DWORD PTR [rbp-8], 7
  mov DWORD PTR [rbp-4], 8
  nop
  pop rbp
  ret
fun2():
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-32], 1
  mov DWORD PTR [rbp-28], 2
  mov DWORD PTR [rbp-24], 3
  mov DWORD PTR [rbp-20], 4
  mov DWORD PTR [rbp-16], 5
  mov DWORD PTR [rbp-12], 6
  mov DWORD PTR [rbp-8], 7
  mov DWORD PTR [rbp-4], 8
  nop
  pop rbp
  ret
fun3():
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-32], 1
  mov DWORD PTR [rbp-28], 2
  mov DWORD PTR [rbp-24], 3
  mov DWORD PTR [rbp-20], 4
  mov DWORD PTR [rbp-16], 5
  mov DWORD PTR [rbp-12], 6
  mov DWORD PTR [rbp-8], 7
  mov DWORD PTR [rbp-4], 8
  nop
  pop rbp
  ret

总结:
1.数组是一段连续的内存,除了定义数组变量会用数组表示,也会用指针表示。并用指针的*操作来读写数组元素。
2.传递数组参数,本质上是在传递指针。所以,在函数内改变数组的值,也会改变函数外数组的值。
函数调用前
在这里插入图片描述
函数调用后
在这里插入图片描述
3.高维数组本质上还是一维数组。只是索引方式不同,应用场景不同而已。

往期链接:
1: CPU底层那些事(二进制陷阱).
2: CPU底层那些事(底层视角看i++ VS ++i).
3: CPU底层那些事(左值VS右值).
4: CPU底层那些事(CPU如何读写变量).
5: CPU底层的那些事(main函数).

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值