大家看下如下程序的执行结果应该是什么?
#include "stdio.h"
int main()
{
int i=10;
printf("%d %d",++i,i);
return 0;
}
是打印11 11吗?
而实际上是:
为什么呢?
这个就牵扯到关于函数的调用方式了。
printf是一个打印函数,它的参数有两个,都是整型,一个是++i,一个是i.
在执行的时候,需要把这两个参数传到printf函数接收到位置,那么这里就是有个先后顺序了,说的更具体点,就是在main函数中是先把前面的++i传过去呢,还是先把i传过去呢?
这就分为几类不同的方式,但是一般编译器都遵循这样的原则:那就是先把后面的参数传过去,为什么呢?这样使得后面传过去的参数是在原函数中处于开始位置,符合习惯。
于是,先把i传过去,所以得到了10,再传过去++i,又得到了11.最终都变成了11.
最后打印出来即是:11 11.
这个规则不光这个使用,函数基本都是这样。
#include "stdio.h"
int main()
{
int i=10;
printf("%d %d ",i);
return 0;
}
这个程序和以往的程序不一样的地方在于printf还是后面有两个要被打印的整型参数,但是后面只有一个变量与之对应。那么会打印什么呢?
我们发现第一个正是我们要的数,而第二个就是个乱数了,具有系统依赖性。
再变下:
#include "stdio.h"
int main()
{
int i=10;
printf("%d",i,i+1);
return 0;
}
看出来了:只考虑前面双引号里面的参数个数,和后面的变量个数无关。
#include "stdio.h"
int main()
{
printf("%d");
return 0;
}
这个是为什么呢?
大家都知道下面的执行结果:
#include "stdio.h"
int main()
{
printf("%d",1);
return 0;
}
那么小改下1的值:
#include<stdio.h>
int main()
{
printf("%d",1.5);
return 0;
}
为什么呢?
写过这个程序:
#include <stdio.h>
int main()
{
int a=4,b=7;
printf("%d\n",a=a+1,b+a,b+1);
return 0;
}
这个怎么理解?
那么如下呢:
#include <stdio.h>
int main()
{
int a=4,b=7;
printf("%d/n",(a=a+1,b+a,b+1));
return 0;
}
(a=a+1,b+a,b+1) 被真正看成了是逗号表达式!
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
float f;
scanf("%d",&f);
cout<<f<<endl;
return 0;
}
输入1.2时,结果如下:
这个结果你能接受吗?
是关于scanf函数的,它里面包含了很多空格:
#include <iostream>
using namespace std;
int main()
{
int a;
scanf("a=%d ",&a);
cout<<a<<endl;
return 0;
}
结果呢?
似乎输入了a=5然后按回车,没反应;
再按回车,还是没反应,为什么?
于是,按下别的键,比如字母键 't',再回车,5就出来了!
为什么呢?
原来,scanf函数里面要输入的内容的最后有个空格,也就是说,必须接收到键盘端输入了空格才能算结束;可是空格在这里又是隔断不同输入变量的方式,经常被忽略,于是就出现了上述不能继续下去的状态。那么为什么,输入一个别的字符就可以继续执行了呢?笔者也不知道。
突发奇想:
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
int a=1,b;
scanf("%d",&(a+b));
cout<<b<<endl;
return 0;
}
这个scanf函数中的这个&(a+b)是什么,笔者也不清楚,只是试着能否从键盘输入这个的值,即a+b的和?
结果呢?编译错误!
错误原因: "&" requires l-value ;
即 &需要左值。
左值是什么?可以是一个变量,变量又是什么?当然,就是内存中的一个位置。
那么a+b是什么? 当然不是内存中的一个位置。
前面我们讨论过关于printf函数后面的变量个数大于参数列表中的个数的情况,下面:
#include <iostream>
using namespace std;
int main()
{
int x=1,y=2,z=3;
printf("%d %d\n",x+1,y+2,z+3);
return 0;
}
前面的格式说明和后面的输出列表显然不一一对应,但是:
编译很正确,连编译的警告都没有!
什么是C语言的自由?这里就是答案。
可是最后再执行的时候怎么保证只用到了前两个输出项了呢?
A:下面将介绍个更能显示它自由的程序:
#include <iostream>
using namespace std;
int main()
{
int a=2,b=3;
printf( a>b? "***a=%d" : "###b=%d" ,a,b);
return 0;
}
a>b? "***a=%d" : "###b=%d" 是什么东西?是个条件表达式!
它符合printf函数的语法吗?
咋一看,不太符合!仔细观察下:这个条件表达式的值只有两种:
"***a=%d" 或者 "###b=%d"
两个值都是字符串,那么就符合了printf函数要求前面的格式列表为字符串的要求!
因为a不是大于b的,所以值为后面的,即本程序里的printf函数可以转换成:
printf( "###b=%d" ,a,b);
结果是多少,我想你已经知道了!
如下:
#include <stdio.h>
int main()
{
char s[]="hello";
printf(s);
return 0;
}
也许这是个唯一在printf函数中可以直接放在括号内的程序了,为什么它可以?因为它本身代表着字符串。像如下代码是肯定通过不了编译的:
#include<stdio.h>
int main()
{
int a=1;
printf(a);
return 0;
}
编译的错误信息如下:
#include <iostream>
using namespace std;
int add(int x,int y)
{
return x+y;
}
int main()
{
int a=10;
cout<<add(a,++a)<<endl;
return 0;
}
不知道这个结果是不是让你吃惊!
不过当初确实让笔者吃了好几惊!
函数在传递参数的时候一般遵循从右向左传递的原则,这个是有自身的道理的。
也许有很多时候大家还为输出一个数,但是对齐不是很好而苦苦琢磨到底该空几个格的时候,现在介绍printf函数有的一种控制输出列数和对齐方式的格式:
以%d格式为例:
在此格式的中间加一个整数值,就表示以宽度为此整数值打印;
#include <iostream>
using namespace std;
int main()
{
int a=100;
printf("%4d\n",a);
return 0;
}
此格式表示以宽度为3打印数据a;那么该是左对齐还是右对齐呢?
这里就需要看此数值前面有没有负号了:
如果没有,表示按默认格式,即是右对齐;如果有,那么是左对齐;
在这里没有,即是右对齐,执行图如下:
在4前面加个"-"号:
#include <iostream>
using namespace std;
int main()
{
int a=100;
printf("%-4d!!",a);
return 0;
}
此代码中格式后面的两感叹号仅仅是为了与前面的格式能表达出位置关系。
我们发现,变量a以左对齐方式打印了,且确实占据的是4个位置。
Q:那么如果这个数值小于要打印的数的基本长度呢?在这里,假设小于3的值又该如何打印了呢?
A:当然不能委屈了该打印的数了,这样的话就会按原长度打印。
#include <iostream>
using namespace std;
int main()
{
int a=100;
printf("%2d!!",a);
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a=100;
printf("%-2d!!",a);
return 0;
}
Q:您这里所说的占据多少个位置是指的是多少个ASCII码字符吧,汉字是不是算两个字符的位置?
A:是的。
#include <iostream>
#include<conio.h>
using namespace std;
int main()
{
int a=100;
printf("%-2d!!\n",a);
printf("我!!",a);
getch();
return 0;
}
在cmd.exe中执行得到如下:
应该可以看出来,一个汉字"我"确实占据了2个位置。
Q:这里的负号起着对齐方式的作用,那么正号在这样的格式里有什么作用吗?
A:有的。如下代码:
#include <iostream>
using namespace std;
int main()
{
int a=100;
printf("%+d\n",a);
return 0;
}
%d格式的中间加了个 "+" 号,那么表示输出的数如果是正数,那么会在前面打印表示正数的正号"+".
同样适用于:%f格式、%e格式、%g格式。
对于浮点数,如%f格式,因为这种数据包含了整数部分和小数部分,所以呢,还有个小数位数的问题。
还有额外可以用于表示输出格式控制的符号,如l字符表示是否按照长整型输出。
……
这是个关于printf函数的返回值的问题:
#include <iostream>
using namespace std;
int main()
{
cout<< printf("chenxi\n") <<endl;
return 0;
}
【深入printf函数】
下面是个关于scanf函数的实例:
#include <iostream>
using namespace std;
int main()
{
int a;
scanf("%d\n",&a);
cout<<a<<endl;
return 0;
}
本例中与以往的scanf函数不同之处在于,它里面有个\n符号;
有了这个可就麻烦了,也就是说在输入的时候必须输入个回车才算scanf函数能完成,可是:回车又是用户确认scanf函数结束的标志,那么程序必然会进入混乱状态,那么实际上,程序是如何执行的呢?
输入了我们想要输入的数字1,然后回车,没反应!
再回车,还是没反应!
再空格,还是没反应!
输入一个别的字符,比如本例中输入的字母r,有用了!
不知道为什么?
【关于printf函数最不可思议的程序】
#include <stdio.h>
int main()
{
printf("chenxihello"+3);
return 0;
}
一种理解方式是:+3代表着要剔除其前面的双引号内部可打印字符的前3个,把接下来的打印出来。但是为什么会有这种规定呢?
研究C代码为什么会出现一些神奇的现象,最好的方式就是去看它所对应的汇编代码,如下是GCC编译器编译出的.s汇编代码(这不是x86汇编格式):
.file "printfMistery.c"
.def ___main; .scl 2; .type 32; .endef
.text
LC0:
.ascii "chenxihello/0"
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
call __alloca
call ___main
movl $LC0+3, (%esp)
call _printf
movl $0, %eax
leave
ret
.def _printf; .scl 2; .type 32; .endef
我们发现了movl $LC0+3, (%esp) 这条指令:LC0正对应着上面的我们之后要打印的的字符串: .ascii "chenxihello/0"
而把它加上3也就是说地址加上了3,接着再被打印出来,由此我们得到了为什么执行的结果是那样的了。
既然可以+3,我们试试可不可以减3:
#include <stdio.h>
int main()
{
printf("chenxihello"-3);
return 0;
}
执行结果如下,这是为什么呢?
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是程序员小迷(致力于C、C++、Java、Kotlin、Android、iOS、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。
欢迎关注。助您在编程路上越走越好!