C语言知识点记录

对c语言中的printf的新认知


因为编程的基础不好加上做过的项目重复性太高了,最近就报班打算学习下,从C语言开始。某天有个练习题,问p++ 和§++的区别。然后我就随手打开了一个网上的C的编译器,写下了以下的代码。

// An highlighted block
int main(){
 int n = 0;
 printf("%d %d %d\n", n++, n, n++);
 return 0;
}

其实主要是因为这是网上的编译器,自带功能是编译加运行,所以加了跟没加其实一样的,因为像命令行一样。

本来以为结果可能是

1, 1, 0

还有人可能会以为是

0, 1, 1

如果有人以为是以下结果,请去学习n++和++n的区别。

1, 1, 2
或者是
2, 1, 1

这边就出现了两个跟printf有关的问题。为什么我觉得答案应该是1, 1, 0呢?因为我某天在类似程序员面试大全的地方看到了一句话。说printf是从左至右执行的。我当时就记住了,也没是有深究,所以我觉得我这行printf代码执行结果首先是n++,就是打印n的值,即0,然后再+1。紧接着就是打印n,就是1,最后先打印n的值,再+1。所以运行结束后n的值应该是2。

然而,是我太天真,我一运行,懵了。第一个和第三个是对的,可是为什么中间这个是最大的?
网上编译器的结果
然后我直接去找了学习班的老师,他让我看下源代码。我去搜了printf的源代码,有点多,就不贴在这里了,有兴趣可以自己去找下。看了下还是看不懂,就直接回去找老师,得出了以下的答案。

重点来了啊。
首先,printf在C里面是不定参数。就是说printf这个函数里面可以带上的参数的数量是不定的。比如这里的代码就带了4个参数。关于不定参数有个问题,就是这些参数在传进来以后,怎么放,怎么用。这里就要涉及内存的问题了。深奥的就不多讲了,我也不懂。大概就记住了一点。内存里面有一块区域叫。它主要是用于存放局部变量和函数调用结束时将要执行的下一条指令的返回地址,以及将参数传递给函数。这说明什么。这说明了函数的参数是存在里面的。栈有个特点,就是先进后出。它就跟一个长条型的饼干收纳瓶子一样,要拿最先放进去的饼干,得把它上面的饼干都拿出来。那么根据printf传入的函数来看,最后放入的是第二个n++,即最后一个参数,所以它看起来是从右向左执行的。

那么下一个问题,为什么中间这个值是2呢?其实是编译器的问题。其实每个编译器运行出来的效果是不一样的。因为不同的编译器对++,或者–这种自运算的操作,他们的处理方式是不同的。有些 编译器它是按照我上面设想的过程一样,中间的n比第一个n++先处理,即先打印n再处理左边这个第一个n++。结果会是1,1,0。但是有些 编译器它会先执行++,就相当于,首先处理了每个参数再打印。根据上面的代码,执行过程是这样的。

  1. n = 0
  2. 首先n++,打印的肯定n这个值,即0。再执行n++。
  3. n = 1
  4. 到了第二个,看只打印n,估计就留了个n的地址,让它晚点去那个地址找n,就往下走。
  5. n = 1
  6. 到了第三个,打印n++,那么就把打印了n这个值,即1。
  7. n = 2

那么打印出来的结果就是1,2,0。

为了验证这个回答的准确行,我改了下打印的顺序。

// An highlighted block
int main(){
 int n = 0;
 printf("%d %d %d\n", n++, n++, n);
 return 0;
}

这是n最先打印,那么按照网上编译器的处理顺序,它先执行了++这个自运算,再打印。所以顺序应该是这样的。

  1. n = 0
  2. 首先打印,留了个地址,指向n,往下走。
  3. n = 0
  4. 到了第二个,打印n++,打印0,并执行n++。
  5. n = 1
  6. 到了第三个,打印n++,那么就把打印了n这个值,即1。
  7. 最后n = 2
  8. 那么在打印的时候,n = 2

所以打印出来的结果应该是 1,0,2
跑一下,结果是跟我刚刚描述的是一样的。网上c测试

那么其他编译器是怎么样的么?
因为没装虚拟机,我就没跑unix环境的,我跑了mac和树莓派。树莓派没跑第二个测试。有兴趣可以自己跑一下。(Mac在编译的时候,它自己会提醒说这个顺序是不定的。厉害的。)
mac test1

mac test2

在这里插入图片描述
结果特别的惊喜。

  1. 根据结果,Mac和树莓派居然是从左到右执行的。这说明它们自己处理了printf的打印的顺序,让它从左到右。
  2. 根据树莓派和Mac的打印结果来看。它们对处理++和打印的顺序是不一样的。树莓派跟网上的编译器有点像,是先处理++,后打印。而Mac是先打印再处理++。而且,Mac的执行结果更像是一无所知的我想的结果。(在看书之前,我一直以为printf是从左到右执行的。)以第一个测试为例子,首先打印n,即0,然后执行++,n 就为1。然后到了中间打印n,就打印出了1。最后到最后一个参数,先打印n,就是打印1,在执行++。结果是0,1,1。

总结

  1. 直接在printf里面做除打印以外的操作是不推荐的,因为每个编译器处理的方式是不一样的。所以,以后呢,只在printf里面打印结果,不要操作任何东西
  2. 不同的编译器对代码的处理是不一样的。就跟unix的c的有些功能,在windows里面是没有的差不多。
  3. 一般来说嵌入式都是在unix环境下操作的。所以比较推荐去熟悉unix环境下的C语言的编译器。
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页