C语言:表达式

3 篇文章 0 订阅

表达式就是表示如何计算值的公式,最简单的表达式就是变量和常量,复杂的表达式还包括运算符

变量:程序运行时计算的值

常量:不变的值

运算符:用于构建表达式

运算符一共分为以下几种

1)算数运算符:如,加减乘除

2)关系运算符:如,大于小于这种比较运算符

3)逻辑运算符:如,与或非等

算数运算符:

一元运算符:

+ 一元正号运算符

- 一元负号运算符

二元运算符:

加法类:

+ 加法运算符

- 减法运算符

乘法类:

* 乘法运算符

/ 除法运算符

% 取余运算符

这里解释一下元的意思

这里的元指的是运算符所要连接的表达式个数,比如你想强调一个数是正数或负数,只需要写出

+1

-1

而二元运算符最少需要两个表达式

1+1

2-2

%取余运算符

i%j取的是i除以j所剩的余数

注意:

1)算数运算符可以混合两种不同的数据类型,

如9+2.5f = 11.5

8.88f/2 = 4.44

2)当两个int型使用/运算时将变为整除,

如3/2 != 1.5 而是等于1

3)%要求只能使用整形操作数,不能出现其他浮点型,否则编译通不过

4)0不能作为/或%的左值

5)/或%用于操作负数时,结果会很难确定

C89标准下,取出的负数结果可能向下或者向上取整

如-9/7的结果既可以为-1也可以为-2

   -9%7的结果可能为-2或者5

C99标准下,取出的数结果只有一种,和上面一样,但C99标准只按照其中绝对值最小的值计算

如-9/7的结果既为-1

   -9%7的结果为-2

总结:尽量避免除法和取余运算中有负数参与,如果无法避免,要明确自己使用的那种C语言标准

这种现象出现的原因:

《C语言与程序设计》一书中给出的解释是:

C89、C99都要保证(a/b)*b + a%b = a

也就是说只要除法a与b运算的结果,无论是整除/还是取余%运算,只要满足上面这个公式,C89都会认为结果正确,

当-9/7时,C89认为-1、-2都满足,当-9%7时,C89认为--2或者5都满足

结果就是:

-1*7+(-2) = -9//结果1

-2*7+5 = 9//结果2

这样就造成了两种结果均为正确的问题

C99这里的大多数CPU都习惯以除法中绝对值最小的为正确值,也就是上面的第一种结果

C语言与硬件强相关,根据CPU的不同,策略不同,所以同一台计算机在不换CPU的情况下,除法与取余的结果通常是确定的,但不同计算机上可能就会出现问题,尤其是一些年代久远的平台上。

运算符的优先级和结合性

这里可以参考

运算符优先级和结合性 - 苦涩的茶 - 博客园

赋值运算符

赋值运算符用于将表达式的值存储在变量当中

v = e

v是存值得变量,e是表达式,注意,如果v与e类型不同,会将e转化为v的类型

并且这个v一定要为左值,这个左值我们可以理解为变量,或者任何对象结构体的句柄,并且一定不是常量的

int i = 12.66f

i = 12  而不会等于12.66

所以,赋值运算的两个操作数不同时,一定要注意由此产生的精度丢失问题

赋值运算符与算数运算符的不同点:

 赋值运算符做赋值操作会改变左操作数的值

而算数运算符不会改变左操作数

int i = 0;

i = 1;//这里 i的值因为赋值发生改变,i此时应该为1

而算数运算符

int i = 0;

i + 1;

此时,i仍然为0,“i + 1;”当做一个共同的整体时才为1,在没有赋值前,表达式值是一个运算结果

所以当我们不希望改变原有值时可以使用运算符表达式代替

if((i+1)>0)

//dosm

因为赋值运算符可以连续如

#include <stdio.h>

int main()
{
    int a,b, c;
    a = b =c = 6;
    printf("a = %d  b = %d  c = %d\n",a,b,c);
    return 0;
}

输出结果为

a = 6 b = 6 c = 6

如果这样写:

int a,b, c= 10;

那么只有c会被赋值为6,因为赋值运算符是右结合的。

而a和b会输出意料之外的值,具体是什么看CPU心情。

又因为赋值运算符是右结合的,所以

a = b =c = 6;的复制顺序如下

a = (b= (c = 6)).

注意!!!

使用连续赋值运算符时,要保持变量类型一致,否则极可能出现精度丢失问题

#include <stdio.h>

int main()
{
    float a,c;
    int b;
    a = b =c = 12.066f;
    printf("a = %f  b = %d  c = %f\n",a,b,c);
    return 0;
}

输出结果为

a = 12.000000 b = 12 c = 12.066000

 嵌入赋值运算符的表达式

#include <stdio.h>

int main()
{
    int a,b,c;
    a = 1;
    b = 1 +(c = a);
    printf("a = %d  b = %d  c = %d\n",a,b,c);
    return 0;
}

输出结果为

a = 1 b = 2 c = 1

但是,强烈不建议这样写,首先他不利于阅读,其次容易引发其他错误

左值 

左值也就是是赋值运算符左面的值,他不能为常量,可以为变量或者结构体的属性

复合赋值

如i = i + 2;

可以写成如下形式

i += 2;

同样+、-、*、/、%都可以这样结合

不过,实际开发中同样也不建议使用这种复合运算符赋值的方式

原因有以下两点

1)书写错误问题

熟悉if语句的人一般都知道,if(表达式)使用表达式判断==时如果我们少写一个 = 会变成赋值表达式,这样编译器是不会报错的,如if(a = 0),相当于if(a),又因为C语言表达式0相当于false,这样我们的选择表达式就失去了判断的意义

所以我们一般这样写if(0 == a),把常量作为左值,这样如果我们少写一个 = 时,if语句变为if(0 = a)

这样常量表达式作为赋值运算符左值时,编译器会报错,这样的习惯会帮助我们发现书写错误

同理,如果我们将复合赋值运算符写反了,会造成什么后果呢?

如下

int main()
{
    int a = -1;
    int b = 1;
    b =- a;
    printf("a = %d  b = %d \n",a,b );
    return 0;
}

输出结果为

a = -1 b = 1

编译器并没有报错,因为+、-在作为一元运算符时与操作数左结合时会优先被编译器识别为一元正号运算符一元符号运算符,实际输出的结果会与我们的期望天差地别,这种疏忽导致的问题在查找起来非常的麻烦

2) 优先级问题

int main()
{
    int a = 2;
    int b = 3;
    int c = 4;
    int d = 5;
    a *= b +c +d;
    printf("a = %d \n",a );
    return 0;
}

输出为

a = 24

 a *= b +c +d;

会被编译器视为a *=(b +c +d);

而很多人可能会理解为

a = a *b  +c +d;

但实际结果天差地别,会让不熟悉的人难以阅读

结合方式:

复合赋值运算符与普通赋值运算符都是右结合的

也就是说

a += b += c

等价与

a += (b += c)

自增/自减运算符

使用++或者--与变量结合可以达到自增加1和自减少1的操作,

i++ 相当于 i += 1

i-- 相当于 i-=1

上面这种是后缀的自增减

如果少写一个符号的话,编译时会报错

还有一种是前缀形式的

++i

--i

两者有什么区别呢?

前缀:立即变化

后缀:随后变化

立即变化很好理解,如++i时,这个i直接发生改变,和i += 1结束一样

而后缀自增减,是要等待自增减这一行结束才会变化,

如何证明呢

int main()
{
    int a = 1;
    printf("a = %d \n",a++ );
    return 0;
}

输出

a = 1

其实这个时候a++ 就相当于打印a,证明这行结束前,这个a还是为1的

我们换种写法

int main()
{
    int a = 1;
    a++;
    printf("a = %d \n",a );
    return 0;
}

 结果为

a = 2

可见a++;结束完成后,才执行了a = a +1;也就是(a += 1) 的赋值操作,

你也可以理解为先计算,在表达式执行完毕后赋值。

实际使用中容易出现的问题

int main()
{
    int a = 1;
    int b = 2;
    int c;
    c = a++ + b ++;
    printf("a = %d \n",a );
    printf("b = %d \n",b );
    printf("c = %d \n",c );
    return 0;
}

先看看自己的期待结果是什么。。。。

实际输出

a = 2

b = 3

c = 3

如果这个结果和你期待的一致,那就证明你理解的正确,如果不一致,那这里就再说明一次

为什么a、b都有增加,但c却跟b一样,而不是5,

因为运算中的a++再运行到;前始终是a的状态,只有;结束的时候,才会自变化。也就是说,没出;时这个运算的表达式a、b、c还是1,2,3的状态,因为c的被赋值操作已经完成,还是(1+2)3,而a和b才去完成他们的赋值操作(+1)。

未定义行为

更新于2023年11月27日,之前这里写到的关于未定义行为

int a = 2;
int b = a*a++;

 对于b等于6是未定义行为的说法有误,因为我搞错了一点,后缀自增的优先级高于加减乘除,其实表达式按照先自增的操作完全没有问题,先计算a++,随后访问到a 变成3*2的形式,所以为6,这个例子举得很不恰当,这个问题在文章写出之后一段时间就发现了,可惜平时太忙,并且最近惰性过强没有及时发现,再此向此前看过我文章的同学道歉

其他未定义行为请参考如下两个回答

C语言面试必备——C语言自增/自减操作的陷阱_从善若水的博客-CSDN博客_c语言 自减

C语言自增、自减运算符使用中应注意的问题 王红_xiaofei0859的博客-CSDN博客_自增自减运算符的运算对象只能为

表达式语句

就是表达式以;结尾可以成为一个单独的语句

如:

a+b;//这个就可以成为一个单独的语句不会报错

但要注意,赋值语句(+=、-=、++、--、=)会改变值并存到变量中,

但如a+b这种结果如果单独执行的话出了;后就会被丢弃

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值