国2c语言中指针与数组的赋值运算,C语言到汇编-指针与数组2

本章剩下的内容主要有:

1.指针与数组 & 地址算术运算

2.字符指针与函数

3.多维数组 & 指针与多维数组

4.指向函数的指针

每个内容举一个例子,第一个例子:

int a[10] = {1,2,3,4,5,6};

int *pa = &a[0];

main(){

pa = &a[0];

pa = a;

int x = *pa;

int i;

for(i=0; i<5; i++){

printf("%d", a[i]);

printf("%d", pa[i]);

printf("%d", *(pa+i));

}

}

指针类型的pa 指向了a[0],即pa 的值是a[0]的地址。数组变量a 的值也是数组的首地址,所以pa 的两种赋值方式完全相同。x 的值是a[0],即1。pa[i]与*(pa+i)指向的也是同一个元素。对照编译后的汇编代码,如下:

.globl _a

.data

_a:

.long 1

.long 2

.long 3

.long 4

.long 5

.long 6

.space 16

.globl _pa

_pa:

.long _a

.section .rdata,"dr"

LC0:

.ascii "%d\0"

.text

.globl _main

_main:

mov DWORD PTR _pa, OFFSET FLAT:_a

mov DWORD PTR _pa, OFFSET FLAT:_a

mov eax, DWORD PTR _pa

mov eax, DWORD PTR [eax]

mov DWORD PTR [ebp-4], eax

mov DWORD PTR [ebp-8], 0

L2:

cmp DWORD PTR [ebp-8], 4

jg L3

mov eax, DWORD PTR [ebp-8]

mov eax, DWORD PTR _a[0+eax*4]

mov DWORD PTR [esp+4], eax

mov DWORD PTR [esp], OFFSET FLAT:LC0

call _printf

mov eax, DWORD PTR [ebp-8]

lea edx, [0+eax*4]

mov eax, DWORD PTR _pa

mov eax, DWORD PTR [edx+eax]

mov DWORD PTR [esp+4], eax

mov DWORD PTR [esp], OFFSET FLAT:LC0

call _printf

mov eax, DWORD PTR [ebp-8]

lea edx, [0+eax*4]

mov eax, DWORD PTR _pa

mov eax, DWORD PTR [edx+eax]

mov DWORD PTR [esp+4], eax

mov DWORD PTR [esp], OFFSET FLAT:LC0

call _printf

lea eax, [ebp-8]

inc DWORD PTR [eax]

jmp L2

L3:

leave

ret

可以看到_pa 的值就是标号_a ,而后面两个printf 的代码也完全一样。

第二个例子,字符指针与函数:

char amessage[] = "now is the time"; /* 定义一个数组 */

char *pmessage = "now is the time"; /* 定义一个指针 */

char *b[] = {"xxx","yyy"};

main(){

char *a[] = {amessage,pmessage};

}

编译后的汇编代码:

.globl _amessage

.data

_amessage:

.ascii "now is the time\0"

.section .rdata,"dr"

LC0:

.ascii "now is the time\0"

.globl _pmessage

.data

_pmessage:

.long LC0

.section .rdata,"dr"

LC1:

.ascii "xxx\0"

LC2:

.ascii "yyy\0"

.globl _b

.data

_b:

.long LC1

.long LC2

.text

.globl _main

_main:

mov DWORD PTR [ebp-8], OFFSET FLAT:_amessage

mov eax, DWORD PTR _pmessage

mov DWORD PTR [ebp-4], eax

leave

ret

变量a 和b 是指针数组,在汇编代码中与其他变量其实并无不同,在函数内部都是一个内存空间,在函数外部都是一个标号。

第三个例子,指针与多维数组:

static char daytab[2][13] = {

{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},

{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

};

int a[10][20];

int *w[10];

main(){

int a = daytab[1][10];

f(daytab);

}

f(char daytab[][13])

{

char b = daytab[1][10];

}

编译后的汇编代码:

.data

_daytab:

.byte 0

.byte 31

.byte 28

.byte 31

.byte 30

.byte 31

.byte 30

.byte 31

.byte 31

.byte 30

.byte 31

.byte 30

.byte 31

.byte 0

.byte 31

.byte 29

.byte 31

.byte 30

.byte 31

.byte 30

.byte 31

.byte 31

.byte 30

.byte 31

.byte 30

.byte 31

.text

.globl _main

_main:

movsx eax, BYTE PTR _daytab+23

mov DWORD PTR [ebp-4], eax

mov DWORD PTR [esp], OFFSET FLAT:_daytab

call _f

leave

ret

.globl _f

_f:

push ebp

mov ebp, esp

sub esp, 4

mov eax, DWORD PTR [ebp+8]

add eax, 23

movzx eax, BYTE PTR [eax]

mov BYTE PTR [ebp-1], al

leave

ret

.comm _a, 800 # 800

.comm _w, 48 # 40

可以发现,汇编代码中并不区分一维还是二维数组,而是调用时根据行和列的值计算出具体位置。

最后一个例子,指向函数的指针:

int f(int i){

return i+1;

}

int w(int i){

return i+2;

}

int useforw(int (*x)(int));

main(){

int (*pf)(int);

pf = f;

int a = 1;

int b = 2;

b = (int (*)(int)) a;

useforw(f);

useforw(w);

useforw((int (*)(int))(1+1==2 ? f : w));

}

int useforw(int (*x)(int)){

int y = (*x)(9);

}

main 函数上面定义了两个函数f 和w ,省去了声明。useforw 函数的参数是一个指向函数的指针,也即函数指针。函数指针其实就是指向了函数代码的首地址,而函数名也是代码的首地址,这和数组很类似,在汇编代码中,数组名和函数名都是一个标号。

main 函数中第一行:

int (*pf)(int);

声明了一个函数指针变量,变量名为pf ,类型为int (*)(int)。接着把函数f 的地址赋给了pf 。其实不管pf 是什么类型,int 型还是看起来很复杂的指针型,在汇编代码中都只是一个没有姓名的内存空间而已。

后面的代码将变量a 强制转换为int (*)(int)类型,也就是函数指针类型,这种写法是可以的,但是不能用(int (*)(int)) a;这种方式来将a 声明为此种类型的变量。

编译后的汇编代码如下:

.text

.globl _f

_f:

push ebp

mov ebp, esp

mov eax, DWORD PTR [ebp+8]

inc eax

pop ebp

ret

.globl _w

_w:

push ebp

mov ebp, esp

mov eax, DWORD PTR [ebp+8]

add eax, 2

pop ebp

ret

.globl _main

_main:

mov DWORD PTR [ebp-4], OFFSET FLAT:_f

mov DWORD PTR [ebp-8], 1

mov DWORD PTR [ebp-12], 2

mov eax, DWORD PTR [ebp-8]

mov DWORD PTR [ebp-12], eax

mov DWORD PTR [esp], OFFSET FLAT:_f

call _useforw

mov DWORD PTR [esp], OFFSET FLAT:_w

call _useforw

mov DWORD PTR [esp], OFFSET FLAT:_f

call _useforw

leave

ret

.globl _useforw

_useforw:

push ebp

mov ebp, esp

sub esp, 8

mov DWORD PTR [esp], 9

mov eax, DWORD PTR [ebp+8]

call eax

mov DWORD PTR [ebp-4], eax

leave

ret

可以看到,函数指针作为参数时,虽然写法看起来复杂,实际只是把函数名/标号的地址传递了过去。

好了,第五章就到这里,下一篇开始学习第六章——结构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值