1.请问prinf函数的参数中含有指针表达式,是按照什么顺序运算的,代码运算结果中显然不是从左到右进行的。
#include <stdio.h>
int main() {
int a[5] = { 1,2,3,4,5 };
int *p = a;
printf("%d\n", *p);
printf("%d %d %d %d\n", *(++p)++,*p, *p++, *p);
getchar();
return 0;
}
1.请问prinf函数的参数中含有指针表达式,是按照什么顺序运算的,代码运算结果中显然不是从左到右进行的。
#include <stdio.h>
int main() {
int a[5] = { 1,2,3,4,5 };
int *p = a;
printf("%d\n", *p);
printf("%d %d %d %d\n", *(++p)++,*p, *p++, *p);
getchar();
return 0;
}
函数参数压栈的顺序是一定的,只是参数的求值顺序是未指定
的,
编译器只保证在printf
调用之前,所有参数的值是已知的
这方面的资料可以搜索序列点(Sequence Point)
函数参数的运算顺序,跟函数的内部逻辑关系不大,应该是入栈前(函数执行前)先计算++p
,函数结束后,再计算p++
。如果想知道具体的顺序,可以参照汇编代码(具体含义参考注释)
.file "a.cpp"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\12\0"
LC1:
.ascii "%d %d %d %d\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB10:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
andl $-16, %esp
subl $64, %esp
.cfi_offset 6, -12
.cfi_offset 3, -16
call ___main
movl $1, 40(%esp) ;从(esp+40)到(esp+56)间,依次放入1~5。int a[5] = { 1,2,3,4,5 };
movl $2, 44(%esp)
movl $3, 48(%esp)
movl $4, 52(%esp)
movl $5, 56(%esp)
leal 40(%esp), %eax ;(esp+40)的地址,放入(esp+60)。int *p = a;
movl %eax, 60(%esp)
movl 60(%esp), %eax ;从(esp+60)地址中取值,放入eax。*p
movl (%eax), %eax
movl %eax, 4(%esp) ; printf参数2, eax(即*p)
movl $LC0, (%esp) ; printf参数1, "%d\12\0"
call _printf ; 调用printf
movl 60(%esp), %eax ; 从(esp+60)地址中取值,放入ebx(即*p)
movl (%eax), %ebx
movl 60(%esp), %eax ; 把(esp+60)的值放入eax
leal 4(%eax), %edx ; eax中记录的地址+4的值,放入edx(即*(++p))
movl %edx, 60(%esp) ; 把edx的值,放入(esp+60)
movl (%eax), %ecx ; 从eax记录的原(esp+60)地址中的值,放入ecx(即*p)
movl 60(%esp), %eax ; 从(esp+60)地址中取值,放入edx
movl (%eax), %edx
addl $4, 60(%esp) ; (esp+60)的值+4,放入eax(即eax=*(p+2))
movl 60(%esp), %eax ; 从(esp+60)地址中取值,+4后的地址放入esi
leal 4(%eax), %esi
movl %esi, 60(%esp) ; 把esi的值,放入(esp+60),
movl (%eax), %eax ; eax = *eax
movl %ebx, 16(%esp) ; printf参数5, %ebx(即*p)
movl %ecx, 12(%esp) ; printf参数4, %ecx(即*p)
movl %edx, 8(%esp) ; printf参数3, %edx(即*(++p))
movl %eax, 4(%esp) ; printf参数2, %eax(即*(p+2))
movl $LC1, (%esp) ; printf参数1, "%d %d %d %d\12\0"
call _printf
call ___getreent
movl 4(%eax), %eax ; 把*(eax+4)的放入*esp
movl %eax, (%esp)
call _getc
movl $0, %eax
leal -8(%ebp), %esp
popl %ebx
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE10:
.ident "GCC: (GNU) 5.4.0"
.def _printf; .scl 2; .type 32; .endef
.def ___getreent; .scl 2; .type 32; .endef
.def _getc; .scl 2; .type 32; .endef
题外话:
结果中出现的2和3还可以说的通。
4很奇怪,非要勉强地解释的话,*(++p)++
括号外面的++
也对p
起作用了,
但是形如p++
运算符,应该在语句结束后才自增的,所以这样解释明显不对。
我在Cygwin + gcc (GCC) 5.4.0
环境,运行的结果如下,没有出现4。
1
3 2 1 1
请问题主用了什么环境?如果有汇编代码的话,贴出来可以一起分析下。
6 回答6.9k 阅读✓ 已解决
3 回答2k 阅读✓ 已解决
2 回答3.9k 阅读✓ 已解决
2 回答3.2k 阅读✓ 已解决
1 回答3.2k 阅读✓ 已解决
1 回答2.7k 阅读✓ 已解决
3 回答3.4k 阅读
在一条语句里多次改变一个变量属于未定义行为,在不同平台可能有不同结果。这个问题没有意义。