【C语言】C语言操作符的分类及应用【超详细讲解】

指令系统的每一条指令都有一个操作符,它表示该指令应进行什么样性质的操作,不同的指令用操作符这个字段的不同编码来表示,每个编码分别代表一种指令。这篇文章主要给大家介绍了关于C语言中操作符的相关资料,需要的朋友可以参考一下。

目录

算术操作符

移位操作符

位操作符

关于位操作符和移位操作符的应用示例:

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符,(三目操作符)

逗号表达式

下标引用操作符 

函数调用操纵符

隐式类型转换

操作符的属性

 操作符应用实例:

算术操作符

主要是 ( +   -   *   /    %) 五种算数操作符。

算术操作符是比较简单的操作符,包括加减乘除(+  -  *  /)取模(%),加减乘和数学中的加减乘一样,而两个数相除我们得到的是它们的商的整数部分,取模(%)得到的是它们的余数

  • +  加法
  • -   减法
  • *   乘法
  • /   除法(取商)  7 / 2 = 3 ( …… 1)     1 / 2 --> 0
  • % 取模操作符 :计算的的整除后的余数,操作数必须为整数   7 % 2 = 1;
  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2.  对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
  4. 当除数为小数时,int类型只会截取整数部分。 6 / 2.4 = 3 (系统看成 6 / 2 = 3)

移位操作符

移位操作符涉及到整数的原反补知识:http://t.csdn.cn/HflQh

  •   <<左移操作符:移动的是二进制
  •   >>右移操作符:移动的是二进制
int main()
{
int a = 7;
int b = a << 1;
printf("%d\n", a);//7   111   
printf("%d\n", b);//14   1110  
}
//a  内存中 0000 0000 0000 0000 0000 0000 0000 0111
//b  内存中a向左移动一位 左移操作,左边丢弃,右边补0
//b  内存中 0000 0000 0000 0000 0000 0000 0000 1110   (14)
// a自身不变,b复制了a的补码再做操作。

 左移操作符<<  具体规则为:

内存中向左移动一位 左移操作,左边丢弃,右边补0。

//b = -7 << 1, a = -7
//a  1111 1111 1111 1111 1111 1111 1111 1001
//b  1111 1111 1111 1111 1111 1111 1111 0010(补码)
//b  1111 1111 1111 1111 1111 1111 1111 0001(反码)
//b  1000 0000 0000 0000 0000 0000 0000 1110(原码) -14

所以无论正负,左移1,就是乘2

右移操作符
算数移位:右边丢弃,左边补原符号位
逻辑移位:右边丢弃,左边补0

是逻辑右移还是算数右移取决于编译器
基本上见过的都是算术右移。

int main()
{
	int a = -7;
	int b = a >> 1;
	printf("a=%d\n", a); // -7
	printf("b=%d", b);   // -4
	return 0;
}
//VS采用的是算术右移。
//对于移位运算符,不要移动负数位,也不能移动浮点数位,这是标准未定义的。

位操作符

  •  & - 按(2进制)位与
  •  | - 按(2进制)位或
  •  ^ - 按(2进制)位异或
int main()
{
	int a = 3; //补码 0000 0000 0000 0000 0000 0000 0000 0011
	int b = -5;//原码 1000 0000 0000 0000 0000 0000 0000 0101
	           //补码 1111 1111 1111 1111 1111 1111 1111 1011
	int c1 = a & b; // & : 两个同时为1才是1,否则为0
	int c2 = a | b; // | : 只要有1就为1,两个为0才为0
	int c3 = a ^ b; // ^ : 相同为0,相异为1
	printf("c1=%d\n", c1);  // 3
	printf("c2=%d\n", c2);  // -5
	printf("c3=%d\n", c3);  // -8
	return 0;
}

关于位操作符和移位操作符的应用示例:

例子1:

//不创建临时变量实现两个数的交换
//原本的方法1:(创建临时变量)
int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	c = a;
	a = b;
	b = c;
	return 0;
}
//不创建法2:
int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	a = a + b;
	b = a - b;
	a = a - b;
	return 0;
}
//因为整型的数有最大值,可能相加会溢出。
//从理论上讲是有问题的

//不创建的法3
int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	return 0;
}
//在实际应用中,法1(创建临时变量)用得最广泛
//法3 只适用于 整型,处理速度较慢

//两个相同的数字异或为0
 0 ^ a = 0
 a ^ a = 0
 3 ^ 3 ^ 5 = 5
 3 ^ 5 ^ 3 = 5
//注意:异或操作符支持交换律
//所以:	a = a ^ b; b = a ^ b; a = a ^ b; -->
          a = 3 ^ 5  b = 3 ^ 5 ^ 5 --> 3   a = 3 ^ 5 ^ 3 = 5;
// 而且这种算法不会溢出
// 很难想,但是像加法

例子2:

//编写代码要求实现:求一个整数储存在内存中的二进制中1的个数
// ----> 求补码的二进制中1的个数

// int a = 3;
// 0000 0000 0000 0000 0000 0000 0000 0011
// 2 

// a & 1
// 0000 0000 0000 0000 0000 0000 0000 0011
// 0000 0000 0000 0000 0000 0000 0000 0001
//& 后 为 0000 0000 0000 0000 0000 0000 0000 0001 可以判断最后一位的是0还是1
// 只需要右移操作 第二位就变成第一位了

int main()
{
	int a = 7;
	int amount = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((a & 1 )== 1)
			amount++;
		a = a >> 1;
	}
	printf("%d", amount);
	return 0;
}

例子3:

//编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同? 
//
//输入例子:
//
//1999 2299
//
//输出例子 : 7
#include<stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int c = a ^ b;
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((c & 1) == 1)
			count++;
		c >>= 1;
	}
	printf("%d", count);
	return 0;
}

例子4:

//获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
#include<stdio.h>
int main()
{                                                // 0000 0000 0000 0000 0000 0000 0000 0101 
	int a = 0;                                   // 1000 0000 0000 0000 0000 0000 0000 0001 
	scanf_s("%d", &a);
	int num = 1 << 30;
	printf("Even digit:");
	if (a >= 0)
		printf("0 ");
	else
		printf("1 ");
	for (int i = 0; i < 15; i++)
	{
		int b = (a & (num >> 1))/(num>>1);
		printf("%d ", b);
		num >>= 2;
	}
	num = 1 << 30;
	printf("\nOdd digit: ");
	for (int i = 0; i < 16; i++)
	{
		int b = (a & num)/num;
		//b = Squ2(b);
		printf("%d ", b);
		num >>= 2;
	}
	return 0;
}

例子5:

//写一个函数返回参数二进制中 1 的个数。

//比如: 15    0000 1111    4 个 1
int main()
{
	int a = 0;
	scanf_s("%d", &a);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((a & 1) == 1)
			count++;
		a >>= 1;
	}
	printf("count = %d", count);
	return 0;
}

赋值操作符

  •  “ = ”
int main()
{
    int a = 3; //初始化
    a = 3;     //赋值
    a = x = y + 1;//连续赋值,从右往左算 y+1的值赋给x x赋给a
    //不建议连续赋值,无法单步进行调试,可读性较差, 建议不要用这个。
    return 0;
}

复合赋值符号:

  • +=
  • -=
  • *=
  • /=
  • %=
  •  >>=
  •  <<=
  •  &=
  • |=
  • ^=

单目操作符

  • ! 逻辑反操作
    int main()
    {
    	int flag = 3;
    	//如果flag 为真(非0),if成立,进入条件语句
    	if(flag)
    	{ }
    	//如果flag 为假(0),!flag为真,if成立,进入条件语句
    	if (!flag)
    	{ }
    	return 0;
    }
  •  -负值

  • +正值

  • &取地址

     int main()
     {
    	 int a = 30;
    	 printf("%p\n", &a); //取的是变量在内存中的起始地址
    	 int n = sizeof(a); //计算的是a所占内存的大小,单位是字节
     	 int * p = &a; //p 就是指针变量 存储地址
    	 return 0;
    }
    
  • sizeof ,注意:是一个操作符,不是函数。计算的是变量、数组所占内存空间的大小,而strlen 是库函数,是用来求字符串的长度。

  • “ ~ ” 二进制位取反,包括符号位

  • //eg.
    int main()
    {
    	int a = 0;
    	// 0000 0000 0000 0000 0000 0000 0000 0000 
    	printf("%d\n", ~a);
    	// 1111 1111 1111 1111 1111 1111 1111 1111 补码
    	// 1111 1111 1111 1111 1111 1111 1111 1110 反码
    	// 1000 0000 0000 0000 0000 0000 0000 0001 原码
    	//     -----> -1
    	// 把其中几位变成1 可以这一位(1 << 几位) 或1 
    	// 把其中几位变成0 可以 0 按位与上去,其余全为1(a & ~(1<<4) ) 就可以
    	return 0;
    }
    

  • 前置++  先++ 后使用

  • 后置++  先使用,后++

  •  * 取地址操作符

    // 如 p 是存放 &a中a的地址,如果通过要通过p的地址找到a的地址,就是解引用p
    // *p  --> 通过p里面的值找到 这个地址的所在的空间,就是 a  
    void test1(int arr[])
    {
    	printf("%d\n", sizeof(arr)); //打印了一个指针的大小 4个字节(x86)/8个字节(x64)
    }
    void test2(int ch[])
    {
    	printf("%d\n", sizeof(ch)); //打印了一个指针的大小 4个字节(x86)/8个字节(x64)
    }
    int main()
    {
    	int arr[10] = { 0 };
    	char ch[10] = { 0 };
    	printf("%d\n", sizeof(arr)); //40
    	printf("%d\n", sizeof(ch));  //10
    	test1(arr);
    	test2(ch);
    	return 0;
    }

    关系操作符

  •   >

  • >=

  • <

  • <=

  • !=

  • ==

注意:赋值与判断相等的区别
注意:
int main()
{
	if ("abc" == "abcdefg") //字符串这样比是在比较首字符的地址
	{                       //需要用strcmp
	};
	return 0;
}

逻辑操作符

  • &&逻辑与
  • ||逻辑或
int main()
{
	int a = 3;
	int b = 5;
	int c = a && b;
	printf("%d\n", c);//1
	int d = 0;
	c = a && d;  //有一个0就为0
	printf("%d\n", c);//0
	c = a || b; //只要有一个为真则为真
	printf("%d\n", c);//1
	c = a || d;//1
	printf("%d\n", c);//1
	c = 0 || d;// 两个为0才为0
	printf("%d\n", c);//0
	return 0;
}

请看以下两个例子: 

int main()
{                 
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++; //只有a++算了,因为先使用a为0 不用算了
	i = a++ || ++b || d++;  // 0 || 要算第二个,如果第二个为真,不用算了
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	//  &&1 2 3 4     ||:1 3 3 4
	return 0;
}
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++; // 无论前后置都++了
	// i = a++||++b||d++;  //1 为真 后面根本不用算了,算到为真就好了
 
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	// &&2 3 3 5  ||:1 2 3 5
	return 0;
}

 可以得出结论:

  • &&左边为假,右边不计算
  • ||左边为真,右边不计算

条件操作符,(三目操作符)

表达式1?表达式2:表达式3
真         算        不算 
假         不算      算

int main()
{
	int a = 3;
	int b = 0;
	int max = (a > b ? a : b);
	if (a > 3)
		b = 3;
	else
		b = -3;
	b = (a > 3 ? 3 : -3);
	return 0;
}

逗号表达式

从左往右依次计算,整个表达式的结果是最后一个表达式的结果。

int main()
{
	int a = 3;
	int b = 5;
	int c = (a > b, a + b + 10, a, b = a + 1);//前面的计算不可以忽略
	return 0;
}

下标引用操作符 

  • [ ]
int main()
{
	int arr[10] = { 0 };
	arr[7] = 8;   //[]为下标引用操作符 []操作数为 arr 和 7 
	return 0;
}
//arr[7] --->  *(arr+7) arr+7就是跳过7个元素,指向了第八个元素,就是第八个元素
// ---->*(7+arr)  ---> 7[arr]  

函数调用操纵符

  •  ( ) 
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);//调用了,操作数为Add, a, b.
	return 0;
}

访问一个结构的成员

  •    .    结构体 . 成员名
  •   ->     结构体指针->成员名 
struct Stu
{
	char name[20];
	int age;
	double score;
};
void set_stu(struct Stu*ps)
{
	// 不行,ss.name是一个地址,不能把字符串放到地址里。ss.name = "zhangsan";
	//strcpy((*ps).name, "zhangsan");
	//(*ps).age = 20;
	//(*ps).score = 100.0;
	strcpy(ps->name, "zhangsan");
	ps->age = 20;
	ps->score = 100.0;
}
void print_stu(struct Stu *ps)
{
	printf("%s %d %.1f\n", (*ps).name, (*ps).age, (*ps).score);
}


int main()
{
	struct Stu s = { 0 };
	set_stu(&s);
	print_stu(&s);
	return 0;
}
//实参传给形参的时候,形参是实参的一份临时拷贝。
//当你把s传过去的时候,只传递的是实参的值,然后对形参进行改变
//我们应该传递s的地址过去
// 然后 *ps 找到s的地址 后面用 (*ps).name 找到成员name
// 也可以用 ps -> name 找到
//-> 拿到了结构体指针—>成员名   ->不需要解引用
//如果拿到了结构体对象用. 成员名     ps->age 等价于 (*ps).age

隐式类型转换


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

如何进行整型提升呢?
整型提升是按照变量的数据类型的符号位来提升的。 

int main()
{
	char c = -1; //-1是整数,32个比特位
	 //1000 0000 0000 0000 0000 0000 0000 0001
	 //1111 1111 1111 1111 1111 1111 1111 1110
	 //1111 1111 1111 1111 1111 1111 1111 1111 ->  -1 的 补码
	 //因为char类型只有一个字节,8比特位
	 //1111 1111
	 //把第一个看成是符号位,高位补充符号位。
	char d = 1;
	 //0000 0000 0000 0000 0000 0000 0000 0001
	 //0000 0001;
	 //高位补符号位0
     //如果为无符号数,直接补0
    return 0;
}

 

int main()
{
	char a = 5;
	// 0000 0000 0000 0000 0000 0000 0000 0101
	// 0000 0101
	char b = 126;
	// 0000 0000 0000 0000 0000 0000 0111 1110
	// 0111 1110
	char c = a + b;
	//整型提升
	// 0000 0000 0000 0000 0000 0000 0000 0101
	// 0000 0000 0000 0000 0000 0000 0111 1110
	// 0000 0000 0000 0000 0000 0000 1000 0011
	//回退 八个比特位
	// 1000 0011
	printf("%d", c);
	//整型提升
	// 1111 1111 1111 1111 1111 1111 1000 0011
	// 1111 1111 1111 1111 1111 1111 1000 0010
	// 1000 0000 0000 0000 0000 0000 0111 1101
	// -125!!
	return 0;
}
// char -128 - 127
int main()
{
	char a = 0xb6;
	//整型提升了
	//可能会改变值,因为符号位可能为1
	short b = 0xb600;
	//整型提升了
	//可能会改变值,因为符号位可能为1
	int c = 0xb60000;
	
	return 0;
}
//放在表达式中,整型就会提升,比如 +c
int main()
{
	char c = 1;
	printf("%d", sizeof(c)); //1
	printf("%d", sizeof(+c)); //4
	printf("%d", sizeof(-c));//4

	return 0;
}

比如float 和 double 一起运算,会向上转换
int 和double 遇到了 ,int 会提升成double,
都是向上转换。

操作符的属性


复杂表达式的求值有三个影响因素
1.优先级:相邻操作符的优先级
2.结合性:相邻操作符优先级相同的时候,结合性说了算
  L-R 从左向右 R-L 从右向左
3.是否控制求值顺序,如逻辑或,算到1就不用算了

但是面对一些表达式
他们的顺序没法控制,有多种解释方法
  c + --c
    2  1 
  一定是先算右边的 --c 
 但是左边的 c 什么时候准备呢
 如果c 等于2, 先--c 后准备c 则结果为2
 如果 先准备c 后--c 则结果为3
 
 a * b + c * d + e * f

#include <stdio.h>
int i;
int main()
{
	i--;
	if (i > sizeof(i))
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	return 0;
}
// 会输出>

 操作符应用实例:

KiKi学习了循环,BoBo老师给他出了一系列打印图案的练习,该任务是打印用“*”组成的X形图案。
输入描述:
多组输入,一个整数(2~20),表示输出的行数,也表示组成“X”的反斜线和正斜线的长度。
输出描述:
针对每行输入,输出用“ * ”组成的X形图案。

//找到规律是关键,看作一条正斜杠和反斜杠
#include <stdio.h>
int main()
{
	int n = 0;
	while (scanf_s("%d", &n) != EOF)
	{
		for (int i = 0; i < n; i++)  //外循环为行
		{
			for (int j = 0; j < n; j++) //内循环为列
			{
				if (i == j || i + j == n - 1)
					//最关键的地方,正斜线为[i][i]处是*, 反斜杠为[i][n-1-j]处是*,一行打印1个或2个*
					printf("*");
				else
					printf(" ");
			}
			printf("\n"); //打印完一行,换行
		}
	}
	return 0;
}

 

 描述
KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。
输入描述:
多组输入,一行有两个整数,分别表示年份和月份,用空格分隔。
输出描述:
针对每组输入,输出为一行,一个整数,表示这一年这个月有多少天。

int is_leap_year(int x)
{
	if (x % 400 == 0)
		return 1;
	else if (x % 4 == 0 && x % 100 != 0)
		return 1;
	else
		return 0;
}
int main()
{
	int year = 0;
	int month = 0;
	while (scanf_s("%d %d", &year, &month) != EOF)
	{
		if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
			printf("31");
		else if (month == 2)
		{
			int ret = is_leap_year(year);
			printf("%d", ret == 1 ? 29 : 28);
		}
		else
			printf("30");
	}
	return 0;
}

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

#include<stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	while (scanf_s("%d %d %d", &a, &b, &c) != EOF)
	{
		if (a + b > c && a + c > b && b + c > a)
		{
			if (a == b && b == c)
				printf("Equilateral triangle!\n");
			else if (a == b || b == c || a == c)
				printf("Isosceles triangle!\n");
			else
				printf("Ordinary triangle!\n");
		}
		else
			printf("Not a triangle!\n");
	}
	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值