说明:这里说的是代码执行顺序,不是c文件的预处理、编译、链接、执行…
1 问题起因
1、上学时,就有问过老师代码执行顺序,老师只是说不要想当然代码执行顺序,并未详细解释。
2、工作了还真遇到这个问题,搞了我两天才找到原因…
2 简单问题引入
先上一段简单代码。
#include<stdio.h>
int main(){
int i=10;
printf("%d %d %d\n",i++,i++,i++);
i=10;
printf("%d %d %d\n",++i,++i,++i);
return 0;
}
大部分的编译器在数据入栈时都是从右向左执行,
先别看执行结果,按自己的认知写下执行结果。
10 11 12
11 12 13
我猜大部分人已经写下了这样的执行结果。
下面来编译执行一下来看看:
对的你没看错,和你想的不一样。
这是为什么呢?肯定和编译器有关系啊。那就是汇编文件有关,生成一个汇编文件打开来看看。
[root@hezaizai test]# gcc -S execute.c
[root@hezaizai test]# vim execute.s
有点不太方便查看,更好的办法是生成目标文件通过反汇编打开。
[root@hezaizai test]# gcc -c execute.c -o execute.o
[root@hezaizai test]# objdump -d execute.o
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp)
f: 8b 4d fc mov -0x4(%rbp),%ecx
12: 8d 41 01 lea 0x1(%rcx),%eax
15: 89 45 fc mov %eax,-0x4(%rbp)
18: 8b 55 fc mov -0x4(%rbp),%edx
1b: 8d 42 01 lea 0x1(%rdx),%eax
1e: 89 45 fc mov %eax,-0x4(%rbp)
21: 8b 45 fc mov -0x4(%rbp),%eax
24: 8d 70 01 lea 0x1(%rax),%esi
27: 89 75 fc mov %esi,-0x4(%rbp)
2a: 89 c6 mov %eax,%esi
2c: bf 00 00 00 00 mov $0x0,%edi
31: b8 00 00 00 00 mov $0x0,%eax
36: e8 00 00 00 00 callq 3b <main+0x3b>
3b: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp)
42: 83 45 fc 01 addl $0x1,-0x4(%rbp)
46: 83 45 fc 01 addl $0x1,-0x4(%rbp)
4a: 83 45 fc 01 addl $0x1,-0x4(%rbp)
4e: 8b 4d fc mov -0x4(%rbp),%ecx
51: 8b 55 fc mov -0x4(%rbp),%edx
54: 8b 45 fc mov -0x4(%rbp),%eax
57: 89 c6 mov %eax,%esi
59: bf 00 00 00 00 mov $0x0,%edi
5e: b8 00 00 00 00 mov $0x0,%eax
63: e8 00 00 00 00 callq 68 <main+0x68>
68: b8 00 00 00 00 mov $0x0,%eax
6d: c9 leaveq
6e: c3 retq
在这里简单解释些要点,更多汇编语言自行学习。
++i对应的是: addl $0x1,-0x4(%rbp)
也就是直接执行a+1;
i++对应的是: mov -0x4(%rbp),%ecx
lea 0x1(%rax),%esi
入栈对应:mov 0x1c(%esp), %edx
总结下:
- 在将参数入栈前,编译器会先把参数的的表达式都处理掉,哪怕这些运算会改变其中某些参数的值
- 对于a++操作,编译器会开辟一个缓冲区来保存当前a的值,然后再对a操作,取值时是从缓冲区取,而不是直接从a的内存地址里取。
3 实际问题解决
源代码:
static void skipSpace(char **v_strptr)
{
while ((** v_strptr>=0) &&(** v_strptr < _ctype_len) && isspace ( ** v_strptr))
++ * v_strptr;
if(** v_strptr >= _ctype_len)
{
* v_strptr =NULL;
}
}
修改后:
static void skipSpace(char **v_strptr)
{
char *temp = *v_strptr;
char *p = *v_strptr;
size_t len = strlen(temp) - 1;
while ((*v_strptr < temp + len) && (*p >= 0) && (*p < _ctype_len) && isspace (*p)){
++ *v_strptr;
++ p;
}
if(** v_strptr >= _ctype_len)
{
* v_strptr =NULL;
}
}