天天看点

C语言中的自增自减运算符详解,printf等函数的应用,及其源码等前言

C语言中的自增自减运算符详解,printf等函数的应用,及其源码等

  • 前言
    • 作用
    • 基础的介绍
    • 一些应用中的坑
      • 连加时的加号左结合性
      • 条件运算符中的短路规则
      • 负号的右结合性
      • printf的出栈入栈与压参打印方向
    • 源码(解释一下左值的问题)

前言

本人大一新生,接触C语言才几周,所以有错的地方,还请大佬们评论区给我说一下或者私聊均可。这篇博客是我被要求做自增自减PPT的产物,原本以为很简单,逛完论坛才发现坑好多。有些不适合课上说的,都放到这了。

作用

这是最简单的了,可分为前置与后置。

前置:

#include<stdio.h>
int main()
{
	int i(1);
	++i;
	printf("%d",i);
	return 0;
}
           

#include<stdio.h>
int main()
{
	int i(1);
	--i;
	printf(“%d”,i);
	return 0;
}
           

后置:

#include<stdio.h>
int main()
{
	int i(1);
	i++;
	printf(“%d”,i);
	return 0;
}
           

#include<stdio.h>
int main()
{
	int i(1);
	i--;
	printf(“%d”,i);
	return 0;
}
           

答案就不必说了,懂的都懂,前置与后置的区别是:前置先将变量自身加一,然后再带入计算表达式;后置是先带入计算表达式,然后再将变量的值加一。

接下来以i++与++i为介绍对象,减号与之同理。

基础的介绍

1.它是单目运算符,且具有右结合性,所以只针对单个变量,无法针对表达式或常量。对于k=++i++(即k=++(i++));或6++这种类型的是错误的。

2.++i可以作为左值,但是i++不行(这个后面说源码时会介绍我自己理解的原因)

3.读取规则按照贪心法,即一个字符下尽可能多的结合其他字符。这里为了程序的可读性与后期维护,还是用括号分开吧,空格也可以起到同样的作用。

比如:对于

#include<stdio.h>
int main()
{
	int a,i(1);
	a=++i + ++i + ++i;
	printf(“%d”,a);
	return 0;
}
           

输出结果是a=10;i=4;(为什么a=10,后面会解释)

如果不加空格或括号的话,这个表达式会被系统依据贪心法认为是++(i++)+(i++)+i;就直接报错了。

一些应用中的坑

连加时的加号左结合性

对于刚才提到的a=++i + ++i + ++i,这两个加号的优先级一样,所以取决于其结合性。

所以系统之前读出来的不是a=(++i)+(++i)+(++i),而是a=((++i)+(++i))+(++i)。

翻译过来是这样

#include<stdio.h>
int main()
{
	int i(1),a;
	i=i+1;
	i=i+1;
	a=i+i;
	i=i+1;
	a=a+i;
	printf(“%d”,a);
	return 0;
}
           

所以a=(3+3)+4=10;

条件运算符中的短路规则

这个主要是if的条件中如果用了&&或||,且后面的条件包括自增自减,可能会被短路掉,不作处理。

例如:

#include<stdio.h>
int main()
{
	int i(1);
	if(1!=0||i++)
	printf(“%d”,i);
	return 0;
}
           

#include<stdio.h>
int main()
{
	int i(1);
	if(1==0&&i++)
	printf(“%d”,i);
	return 0;
}
           

由于都被短路掉了,所以输出的i值都是1。

负号的右结合性

由于优先级相同所以取决于“-”的右结合性,遵循从右到左的执行顺序。

这个比较简单所以不举例了。

printf的出栈入栈与压参打印方向

printf函数是先从后往前遍历所有元素并计算出每个元素对应的变量的值,然后将相应的临时变量或实参入栈,最后出栈打印。所以printf的最终打印是最后一步。i与前置最后入栈的都是i的实参,所以打印的是i的终值。而后置都是读取的相应的临时变量。

例如

#include<stdio.h>
int main()
{
	int i(1);
	printf(“%d %d %d %d”,i++,++i,++i,i++);
	return 0;
}
           

对应的是4,5,5,1;

翻译一下是:

取i的值为一临时变量(q);

i=i+1;

返回i值为2;

i=i+1;

返回i值为3;

以实参i为其返回值;

i=i+1;

返回i值为4;

以实参i为其返回值;

取i的值为一临时变量(w);

i=i+1;

返回i值为5;

将“临时变量q,实参i,实参i,临时变量w”依次压入栈中,最后出栈打印。

源码(解释一下左值的问题)

int& int::operator++() 
{
*this += 1;
return *this;
}
const int int::operator++(int)
{
int oldValue = *this;
++(*this);
return oldValue;
}
           

(来自“C语言中自增自减的编译原理”)

很明显的看到i++返回的是临时变量自然没有固定的内存地址,就做不了左值,而++i;改变的是其自身的实参所以可以用来作为左值。

继续阅读