编写一个指针调用程序的C语言文件pointer.c
这里我声明了一个函数test
*代表运用指针,int来指定指针所操作内存单元大小
主函数这边,我声明了一个变量num,并赋值。调用test函数,通过&符号来提取num地址值并传参。
#include
void test(int *p){
*p = 3;
};
int main(){
int num = 1;
test(&num);
return 1;
}
编译C文件,生成汇编
// -S 只进行编译
// -fno-asynchronous-unwind-tables 过滤调试代码
[root@localhost practice]# gcc -S -fno-asynchronous-unwind-tables pointer.c
查看生成的汇编文件pointer.s
.file "pointer.c"
.text
.globl test
.type test, @function
test:
pushq %rbp // 开辟栈空间,栈基址bp指向该空间地址
movq %rsp, %rbp // 栈顶针sp指向栈基址bp所指向的空间地址
movq %rdi, -8(%rbp) // rdi寄存器的值放入rbp-8字节大小的空间里
movq -8(%rbp), %rax // 将rbp-8的地址值,放入rax寄存器中
movl $3, (%rax) // 把3赋予这个rax所指向的地址
nop // 无操作
popq %rbp // 还原bp
ret // 弹出返回地址空间,放入ip指令地址寄存器
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp // 将sp的地址空间-16byte
movl $1, -4(%rbp) // 将1赋值到 bp-4 的空间里
leaq -4(%rbp), %rax // 将该空间地址取出放入rax寄存器
movq %rax, %rdi // 将rax寄存器的地址放入rdi寄存去
call test // 调用test函数
movl $1, %eax // 将立即数返回
leave
ret
总结:可通过汇编文件看出,指针通过栈空间的地址,从而找到变量单元。并且指针必须严格声明类型,因为类型进而能告诉机器他所需内存大小是多少。换句话说指针的操控=栈内存地址的操控
编写一个数组定义的C文件demo.c
#include
int main(){
int arr[] = {11,22,33};
return 1;
}
编译C文件生成汇编
// -S 只进行编译
// -fno-asynchronous-unwind-tables 过滤调试代码
[root@localhost practice]# gcc -S -fno-asynchronous-unwind-tables pointer.c
查看生成的汇编文件demo.s
.file "demo.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $11, -12(%rbp) // 将11存储到rbp-12的空间里
movl $22, -8(%rbp) // 将22存储到rbp-8的空间里
movl $33, -4(%rbp) // 将33存储到rbp-4的空间里
movl $1, %eax
popq %rbp
ret
总结:不难通过上述汇编文件demo.s看出,定义的值分别放入栈空间里,并且按照低地址到高地址,依次放入。
编写一个指针与数组结合的C文件demo2.c
这里我编写了一个长度为3的数组,放入的是int类型
让指针取最后一个索引数组值
#include
int main(){
int arr[] = {11,22,33};
int *p = &arr[2];
return 1;
}
编译并生成汇编代码(上面步骤提及,不在重复)
.file "demo3.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $11, -20(%rbp)
movl $22, -16(%rbp)
movl $33, -12(%rbp)
leaq -20(%rbp), %rax // 将bp-20的地址(可理解为arr[0]的地址)放入rax寄存器
addq $8, %rax // 将rax的地址值加8byte大小(可理解为加完后变成arr[2]),并重新让如rax寄存器中
movq %rax, -8(%rbp) // 将rax地址保存到 rbp-8的位置上
movl $1, %eax
popq %rbp
ret
总结:通过将数组里各个索引下标,转换成地址值,供指针操作。
⭐️额外的一些指针与数组的操作
1.通过指针改变数组里的值
#include
int main(){
int arr[] = {11,22,33};
int *p = &arr[2];
*p = 44;
return 1;
}
------
.file "demo4.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $11, -20(%rbp)
movl $22, -16(%rbp)
movl $33, -12(%rbp)
leaq -20(%rbp), %rax
addq $8, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl $44, (%rax) // 改变数组指定下标为2的数值
movl $1, %eax
popq %rbp
ret
2.通过指针的内存加减获取数组的值
#include
int main(){
int arr[] = {11,22,33};
int *p = &arr[0]; //获取首个索引的4字节地址,
int a = *(p+1); // 获取第二个4字节地址
return 1;
}
------------
.file "demo5.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $11, -24(%rbp)
movl $22, -20(%rbp)
movl $33, -16(%rbp)
leaq -24(%rbp), %rax //取地址
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl 4(%rax), %eax //rax地址值是-24(%rbp) ,加4字节后,变成了数组中第二个值
movl %eax, -12(%rbp)
movl $1, %eax
popq %rbp
ret
运行该指令时什么都不做,但是会占用一个指令的时间。
当指令间需要有延时(给外部设备足够的响应时间;或是软件的延时等),可以插入“NOP”指令。
Intel手册 Volume2 Chapter4.3
释放分配的栈空间,并还原bp,sp到原始指向的地址空间
Intel手册 Volume1 Chapter6.6.2
删除由调用程序推送到堆栈上的任何参数和返回地址