写点什么

C 语言中奇妙又有趣的符号——C 语言运算(操作)符

作者:未见花闻
  • 2022 年 6 月 19 日
  • 本文字数:6510 字

    阅读完需:约 21 分钟

1.C 语言运算符

1.1 运算符概述

C 语言运算符是说明特定操作的符号 ,所以它也被称作为操作符,它是构造 C 语言表达式的工具 。C 语言的运算异常丰富,除了控制语句和输入输出以外的几乎所有的基本操作都为运算符处理。除了常见的三大类,算术运算符、关系运算符与逻辑运算符之外,还有一些用于完成特殊任务的运算符,比如位运算符。

1.2 运算符分类

1.3 运算符优先级

优先级一共可以划分为 15 级,1 级优先级最高,15 级优先级最低。



同一优先级的运算符,运算次序由结合方向所决定。

2.C 语言运算符超全总结

2.1 算数运算符

+:加法-:减法*:乘法/:除法%:取模(求余数)++:自增运算,i++++i都相当于i=i+1,但是i++先使用再++,++i是先++再使用。--:自减运算,用法和++相同。


  1. 除了 % 运算符之外,其他的几个运算符可以作用于整数和浮点数。

  2. 对于 / 运算符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。


OJ例题1:💡BC17 计算表达式的值


描述请计算表达式“(-8+22)×a-10+c÷2”,其中,a = 40,c = 212。输入描述:无。输出描述:(-8+22)×a-10+c÷2 计算之后的结果,为一个整数。


#include <stdio.h>int main(){    int a = 40,c = 212,b = 0;    b=(-8+22)*a-10+c/2;    printf("%d",b);    return 0;}
复制代码


OJ例题2:💡BC18 计算带余除法


描述给定两个整数 a 和 b (-10,000 < a,b < 10,000),计算 a 除以 b 的整数商和余数。输入描述:一行,包括两个整数 a 和 b,依次为被除数和除数(不为零),中间用空格隔开。输出描述:一行,包含两个整数,依次为整数商和余数,中间用一个空格隔开。


输入:15 2输出:7 1#include <stdio.h>int main(){    int a=0,b=0,c=0,d=0;    scanf("%d %d",&a,&b);    c=a/b;    d=a%b;    printf("%d %d",c,d);    return 0;}
复制代码


OJ例题3:💡BC27 计算球体的体积


描述给定一个球体的半径,计算其体积。其中球体体积公式为 V = 4/3*πr3,其中 π= 3.1415926。输入描述:一行,用浮点数表示的球体的半径。输出描述:一行,球体的体积,小数点后保留 3 位。


输入:3.0输出:113.097#include <stdio.h>int main(){    double V=0,r=0,pi=3.1415926;    scanf("%lf",&r);    V=4.0/3*pi*pow(r,3);    printf("%.3lf",V);    return 0;}
复制代码


OJ例题4:💡BC129 小乐乐计算函数



输入:1 2 3输出:0.30#include <stdio.h>
int max_3(int a, int b, int c){ int max = 0; if (a > max) max = a; if (b > max) max = b; if (c > max) max = c; return max;}int main(){ int a = 0; int b = 0; int c = 0; double m = 0; scanf("%d%d%d",&a,&b,&c); int m1 = max_3(a + b, b, c); int m2 = max_3(a, b + c, c); int m3 = max_3(a, b, b + c); m = 1.0*m1 / (m2 + m3); printf("%.2lf\n",m); return 0;}
复制代码

2.2 关系运算符

>:大于<:小于== :等于>= :大于等于<= :小于等于!=:不等于这些运算符用来构成关系表达式,用来做 if 分支语句或者循环语句的表达式。OJ例题:💡BC49 判断两个数的大小关系


描述 KiKi 想知道从键盘输入的两个数的大小关系,请编程实现。输入描述:题目有多组输入数据,每一行输入两个整数(范围(1 ~231-1),用空格分隔。输出描述:针对每行输入,输出两个整数及其大小关系,数字和关系运算符之间没有空格,详见输入输出样例。



#include <stdio.h>int main(){    int a = 0;    int b = 0;    while(scanf("%d %d",&a,&b) != EOF)    {        if (a == b)        {            printf("%d=%d\n",a,b);        }        else if (a > b)        {            printf("%d>%d\n",a,b);        }        else        {            printf("%d<%d\n",a,b);        }    }    return 0;}
复制代码

2.3 逻辑运算符

! :逻辑非。&&:逻辑与,两个条件都为真表达式才为真。||:逻辑或,两个条件都为假表达式才为假。用于构成逻辑表达式,在分支语句和循环语句用的多。需要注意的逻辑或||与逻辑与&&会出现短路效应。比如


int a = 2;int b = 3;int c = 6;a == 2 || b == 4;//逻辑或:对a进行判断如果为真,就不会判断后面的b是否等于4b == 4 && c == 6;//逻辑与:对b进行判断如果为假,就不会判断后面的c是否等于6
复制代码


OJ例题:💡BC51 三角形判断


描述 KiKi 想知道已经给出的三条边 a,b,c 能否构成三角形,如果能构成三角形,判断三角形的类型(等边三角形、等腰三角形或普通三角形)。输入描述:题目有多组输入数据,每一行输入三个 a,b,c(0<a,b,c<1000),作为三角形的三个边,用空格分隔。输出描述:针对每组输入数据,输出占一行,如果能构成三角形,等边三角形则输出“Equilateral triangle!”,等腰三角形则输出“Isosceles triangle!”,其余的三角形则输出“Ordinary triangle!”,反之输出“Not a triangle!”。


提示:所有的三角形都要满足两边之和大于第三边。


输入:2 3 23 3 3输出:Isosceles triangle!Equilateral triangle!#include <stdio.h>int main(){    int a = 0;    int b = 0;    int c = 0;    while(scanf("%d %d %d",&a,&b,&c) != EOF)    {        if ((a + b >c) && (b + c > a) && (a + c > b))        {            if ((a == b) && (b == c))                printf("Equilateral triangle!\n");            else if ((a == b ) || (a == c) || (b == c))                printf("Isosceles triangle!\n");            else                printf("Ordinary triangle!\n");        }        else        {            printf("Not a triangle!\n");        }    }    return 0;}
复制代码

2.4 位运算符

<<:左移整数在计算机中储存的是他的二进制补码,<<能够将它的二进制序列左移,末位使用 0 补齐。


操作数 << 移动位数
复制代码


比如数字2,补码为0000 0000 0000 0000 0000 0000 0000 00102<<1运算后,得0000 0000 0000 0000 0000 0000 0000 0100为数字4>>:右移整数在计算机中储存的是他的二进制补码,>>能够将它的二进制序列右移,如果为算术右移则负数首位补1,正数首位补0。如果为逻辑右移,首位通通补0。至于是算术右移还是逻辑右移,不同编译器不同,VS2019为算术右移。


操作数 >> 移动位数
复制代码


比如-1,原码为1000 0000 0000 0000 0000 0000 0000 0001取反,得1111 1111 1111 1111 1111 1111 1111 11101,得补码1111 1111 1111 1111 1111 1111 1111 1111-1>>1运算后得算术右移1111 1111 1111 1111 1111 1111 1111 1111逻辑右移0111 1111 1111 1111 1111 1111 1111 1111按 VS2019 算术右移转化为原码得1000 0000 0000 0000 0000 0000 0000 0001为数字-1~:按位取反,将一个数二进制序列每位都取反。


~操作数
复制代码


比如数字3,补码为0000 0000 0000 0000 0000 0000 0000 0011~3运算后得1111 1111 1111 1111 1111 1111 1111 1100为一个负数,先将补码还原成原码减1,得1111 1111 1111 1111 1111 1111 1111 1011除最高位取反1000 0000 0000 0000 0000 0000 0000 0100为数字-4|:按位或,对两个数的二进制序列每位比较都为0则为0,否则为1


操作数 | 操作数
复制代码


比如2 | 30000 0000 0000 0000 0000 0000 0000 00100000 0000 0000 0000 0000 0000 0000 0011结果为0000 0000 0000 0000 0000 0000 0000 0011为数字3^:按位异或,对两个数的二进制序列每位比较不相同为1,否则为0


操作数 ^ 操作数
复制代码


比如2 ^ 30000 0000 0000 0000 0000 0000 0000 00100000 0000 0000 0000 0000 0000 0000 0011结果为0000 0000 0000 0000 0000 0000 0000 0001为数字1&:按位与,对两个数的二进制序列每位比较都为1则为1,否则为0


操作数 & 操作数
复制代码


比如2 & 30000 0000 0000 0000 0000 0000 0000 00100000 0000 0000 0000 0000 0000 0000 0011结果为0000 0000 0000 0000 0000 0000 0000 0010为数字2。上面几种运算符的操作对象必须为整数。


#include <stdio.h>
int main(){ printf("%d\n", 2 << 1); //4,2左移1位 printf("%d\n", -1 >> 1);//-1,-1算术右移一位 printf("%d\n", ~ 3);//-4,对3按位取反 printf("%d\n", 2 | 3);//3,2|3 printf("%d\n", 2 ^ 3);//1,2^3 printf("%d\n", 2 & 3);//2,2&3
return 0;}
复制代码



OJ例题:💡BC29 2的n次方计算


描述不使用累计乘法的基础上,通过移位运算(<<)实现 2 的 n 次方的计算。输入描述:多组输入,每一行输入整数 n(0 <= n < 31)。输出描述:针对每组输入输出对应的 2 的 n 次方的结果。


输入:210输出:41024#include <stdio.h>int main(){    int a=0;    while(scanf("%d",&a) != EOF){        printf("%d\n",1<<a);    }    return 0;}
复制代码

2.5 赋值运算符

=:赋值,比如a = 2a = c复合赋值运算符:+= -= /= *= %= <<= >>= |= &= ^=a += 2相当于 a = a + 2,其他的以此类推。


OJ例题:💡BC20 kiki算数


描述问题:KiKi 今年 5 岁了,已经能够认识 100 以内的非负整数,并且并且能够进行 100 以内的非负整数的加法计算。不过,BoBo 老师发现 KiKi 在进行大于等于 100 的正整数的计算时,规则如下:

  1. 只保留该数的最后两位,例如:对 KiKi 来说 1234 等价于 34;

  2. 如果计算结果大于等于 100, 那么 KIKI 也仅保留计算结果的最后两位,如果此两位中十位为 0,则只保留个位。例如:45+80 = 25 要求给定非负整数 a 和 b,模拟 KiKi 的运算规则计算出 a+b 的值。

输入描述:一行,输入两个非负整数 a 和 b,用一个空格分隔。(0 <= a,b<= 231-1)。输出描述:针对每组输入,输出按照 KiKi 的运算规则计算出 a+b 的值。


输入:45 80输出:25#include <stdio.h>int main(){    int a=0,b=0,sum=0;    scanf("%d %d",&a,&b);    a%=100;    b%=100;    sum=a+b;    if(sum>=100)        sum%=100;    printf("%d",sum);    return 0;}
复制代码

2.6 条件运算符

? : :条件运算符,也被称为三目运算符,C 语言中唯一一个三目运算符。


if (a > b)  return a;else  return b;//相当于a > b ? a : b;
复制代码

2.7 逗号运算符

,:逗号运算符,就是用逗号隔开的多个表达式。从左向右依次执行,整个表达式的结果是最后一个表达式的结果。


int main(){  int a = 3;  int b = 2;  int c = 6;  int d = (a + b, b + c, a + c);//从左至右依次进行,d的值为最后一个表达式结果,也就是9  return 0;}
复制代码

2.8 指针运算符

* :解引用运算符,用于访问一个指针所指向空间内的数据。&:取地址运算符,用于得到一个数据或数据类型的地址。


#include <stdio.h>
int main(){ int a = 68; int* pa = &a; printf("%p\n", pa); printf("%d\n", *pa); printf("%d\n", a); return 0;}
复制代码


2.9 求字节运算符

sizeof:用来计算一个数据或数据类型所占字节大小。


#include <stdio.h>
int main(){ int a = 9; double pi = 3.14; char c = "w"; float b = 2.3f; int* pa = &a; char* pc = &c; double* ppi = &pi; printf("%d\n", sizeof(int)); printf("%d\n", sizeof(a)); printf("%d\n", sizeof(double)); printf("%d\n", sizeof(pi)); printf("%d\n", sizeof(char)); printf("%d\n", sizeof(c)); printf("%d\n", sizeof(float)); printf("%d\n", sizeof(b)); printf("%d\n", sizeof(int*)); printf("%d\n", sizeof(double*)); printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(ppi)); printf("%d\n", sizeof(pc)); return 0;}
复制代码


2.10 强制类型转换运算符

(数据类型):将一个数据强制转换成想要的数据类型。比如


int a = 6;double b = 3.14;int c = (int) b;double d = (double) a;a = (double) a;//6.000000b = (int) b;//3
复制代码

2.11 成员运算符

.`:访问结构体成员,`结构体.成员名``->`:访问结构体成员,`结构体指针 -> 成员名#include <stdio.h>
struct Book{ char book_name[40]; int book_price; char book_id[20];};int main(){ struct Book C = { "C语言运算符详解" ,49, "9635419049866812" }; printf("%s\n", C.book_name); printf("%s\n", (&C)->book_name); printf("%d\n", C.book_price); printf("%d\n", (&C)->book_price); printf("%s\n", C.book_id); printf("%s\n", (&C)->book_id);
return 0;}
复制代码


2.12 下标运算符

[ ]:作为数组下标,可以用来访问数组元素。


#include <stdio.h>
int main(){ int a[10] = { 1,2,3,4,5,6,7,8,9,0 }; int i = 0; for (i = 0; i < 10; i++) { printf("%d ", a[i]);//[]作为下标,访问数组元素
} return 0;}
复制代码


2.13 其他运算符

如函数调用符( )。


printf("%...", ...);scanf("%...", &...);//等等
复制代码

3.C 语言中的表达式

3.1 关系,逻辑,条件表达式

具体内容见博主另一篇文章:C语言的选择与轮回——选择结构与循环结构1.3 表达式部分。

3.2 隐式类型转换和算术转换

3.2.1 隐式类型转换

C 的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升整型提升的意义:表达式的整型运算要在 CPU 的相应运算器件内执行,CPU 内整型运算器(ALU)的操作数的字节长度一般就是 int 的字节长度,同时也是 CPU 的通用寄存器的长度。因此,即使两个 char 类型的相加,在 CPU 执行时实际上也要先转换为 CPU 内整型操作数的标准长度。通用 CPU(general-purpose CPU)是难以直接实现两个 8 比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转 换为 int 或 unsigned int,然后才能送入 CPU 去执行运算。


整形提升是按照变量的数据类型的符号位来提升的//负数的整形提升 char c1 = -1; 变量c1的二进制位(补码)中只有8个比特位: 1111111 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为1 提升之后的结果是: 11111111111111111111111111111111 //正数的整形提升 char c2 = 1; 变量c2的二进制位(补码)中只有8个比特位: 00000001 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为0 提升之后的结果是: 00000000000000000000000000000001 //无符号整形提升,高位补0 
复制代码

3.2.2 算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。也称自动类型转换


如果一个运算符两侧的数据类型不同,则先自动进行类型转换,使二者成为同一种类型,然后进行运算。整型、实型、字符型数据间可以进行混合运算。规律为:


💡+、-、*、/运算的两个数中有一个数为 float 或 double 型,结果是 double 型,因为系统将所有 float 型数据都先转换为 double 型,然后进行运算。💡如果 int 型与 float 或 double 型数据进行运算,先把 int 型和 float 型数据转换为 double 型,然后进行运算,结果是 double 型。💡字符(char)型数据与整型数据进行运算,就是把字符的 ASCII 代码与整型数据进行运算。如果字符型数据与实型数据进行运算,则将字符的 ASCII 代码转换为 double 型数据,然后进行运算 。


如果赋值运算符两侧的类型一致,则直接进行赋值。如果赋值运算符两侧的类型不一致,但都是基本类型时,在赋值时要进行类型转换。类型转换是由系统自动进行的,转换的规则是:


💡将浮点型数据(包括单、双精度)赋给整型变量时,先对浮点数取整,即舍弃小数部分,然后赋予整型变量,存在精度丢失。💡将整型数据赋给单、双精度变量时,数值不变,但以浮点数形式存储到变量中。💡将一个 double 型数据赋给 float 变量时,先将双精度数转换为单精度,即只取 6~7 位有效数字,存储到 float 型变量的 4 个字节中。应注意双精度数值的大小不能超出 float 型变量的数值范围;将一个 float 型数据赋给 double 型变量时,数值不变,在内存中以 8 个字节存储,有效位数扩展到 15 位。💡字符型数据赋给整型变量时,将字符的 ASCII 代码赋给整型变量。💡将一个占字节多的整型数据赋给一个占字节少的整型变量或字符变量时,只将其低字节原封不动地送到被赋值的变量(即发生“截断”)。

发布于: 刚刚阅读数: 5
用户头像

未见花闻

关注

还未添加个人签名 2021.11.15 加入

还未添加个人简介

评论

发布
暂无评论
C语言中奇妙又有趣的符号——C语言运算(操作)符_6月月更_未见花闻_InfoQ写作社区