【C语言】操作符详解

本文详细介绍了C语言中的算数、移位、位、赋值、单目、关系、逻辑、条件、逗号表达式、数组操作、结构成员以及表达式求值的规则,包括隐式类型转换、整型提升和算数转换,以及操作符的优先级和结合性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.算数操作符 + - * / %

对于 + - * 就不多阐述了。
对于 /(除)操作符,若 / 有一边是浮点型,则会发生算数转换结果也是浮点型;当两边为整型时,计算结果向靠近0的方向取整
%(取模)操作符要求两边均为整型

printf("%d\n", 5 % 2);	//1
printf("%f\n", 5 / 2.5);//2.000000
printf("%d\n", 5 / 2);	//2——> 2.5向0的方向取整为2
printf("%d\n", -5 / 2);	//-2——> -2.5向0的方向取整为-2

2.移位操作符<< >>

移位操作符的操作数只能是整数,是对其补码的二进制位进行操作。
首先我们知道一个整数在内存中是以二进制的补码形式存储的。

正整数的原码反码补码相同是其本身,负整数的反码补码需要进行转换

在这里插入图片描述

左移操作符<<

规则:左边丢弃,右边补零

int a = 10;
a = a << 1;//20

在这里插入图片描述

左移几位,就是乘以2的几次方

右移操作符>>

右移操作符分为两种:

逻辑右移:左边补零,右边丢弃
算数右移:左边补符号位,右边丢弃

一般编译器中,右移是指算数右移

int a = -10;
a = a >> 1;//-5

在这里插入图片描述
由于-1的二进制反码均为1,所以-1算数右移几位,结果都是-1,而逻辑右移则会成为一个很大的正数
在这里插入图片描述

在vs2019中是算数右移

3.位操作符& | ^

位操作符有:& (按位与)  | (按位或)  ^ (按位异或)

位操作符也是对二进制的补码进行操作

按位与&

二进制位只要存在一个0,该位&的结果就是0,同时为1该位&的结果才为1

 int num = 10 & 3;

在这里插入图片描述

按位或 |

二进制位只要存在一个1,该位|的结果就是1,同时为0该位|的结果才为0

int num = 10 | 3;

在这里插入图片描述
按位异或 ^

二进制位相同为0,不同为1

int num = 10 ^ 3;

在这里插入图片描述
仔细思考可以得出:一个数异或其本身结果为0,任意一个数异或0结果还是其本身

练习题:

不创建临时变量来实现两个数的交换(这两个数可能会很大)

可以用过两个变量相加再相减实现交换

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	a = a + b;
	b = a - b;
	a = a - b;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

但是注意题目中的提示,假如这两个数都很大,那么相加,最终很有可能存不下这么大的数据,最后导致数据丢失,造成结果不准确的后果。下面这种方法可以保证不会造成数据丢失。

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

在这里插入图片描述

4.赋值操作符

赋值操作符=可以改变变量的值

int a = 10;
int b = 7;
b = 250;
a = b + 1;

复合赋值符:+=-=*=/=%=>>=<<=&=|=^=

a += 10;
a = a + 10;

a >>= 1;
a = a >> 1;

这两种意思是一样的,其他复合赋值符也是同样的用法。

5.单目操作符

! 			逻辑反操作
- 			负值
+ 			正值
& 			取地址
sizeof 		操作数的类型长度(以字节为单位)
~ 			对一个数的二进制按位取反
-- 			前置、后置--
++ 			前置、后置++
* 			间接访问操作符(解引用操作符)
(类型) 		强制类型转换

在这里插入图片描述

! 逻辑反操作符

非零(真)变为0(假),0(假)变为1(真)

sizeof操作符

sizeof 后面可以加括号也可以不加,sizeof不是函数而是一个运算符

在这里插入图片描述

前置和后置++、–

前置++:先+1, 再使用
后置++:先使用,再+1
--同理

在这里插入图片描述

*解引用操作符

int a = 10;
int* pa = &a;
printf("%d\n", a);
printf("%d\n", *pa);

pa存放a的地址,*(pa)就是通过地址访问其中的内容,也就是a

强制类型转换

int a = 10;
double b = (double)a;

将int类型的a强制类型转换成double类型,再赋值给b

6.关系操作符

>>=<<=!===

注:=== 一定要区分清楚,千万不要写错

在这里插入图片描述
表达式成立则为1(真),不成立则为0(假)

7.逻辑操作符&& ||

逻辑与&& :两边都为真则为真,一边为假则为假
逻辑或||:一边为真则为真,两边都为假则为假

逻辑与&&

#include<stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3;
	i = a++ && ++b && c++;
	printf("%d %d %d %d %d\n", a, b, c, d, i);
	return 0;
}

a b c i 结果分别为1 2 3 0

结果分析:在执行a++时,后置++先使用再+1,所以a++的结果为0(假)。逻辑与&&只要有一边为假则为假,所以不管后面的表达式的真假,都不用再往后执行了,因为没有意义,所以程序只执行了a++,a的值+1,整个表达式为假,所以i 为0(假)。

逻辑或||

#include<stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3;
	i = a++ || ++b || c++;
	printf("%d %d %d %d %d\n", a, b, c, d, i);
	return 0;
}

a b c i 结果分别为1 3 3 1

结果分析:执行a++的结果为0(假),再执行++b,结果为3(真)。逻辑或||只要有一边为真则为真,所以不管后面的表达式的真假,都不用再往后执行了,整个表达式为真,所以i 为1(真)。

8.条件操作符

条件操作符是C语言中唯一的三目运算符,优先级高于赋值运算符,但是低于关系运算符和算术运算符。

表达式1 ? 表达式2 : 表达式3

表达式1为真则执行表达式2,表达式1为假则执行表达式3,执行后的结果就是整个条件表达式的值。

求两个数最大值

int a = 1, b = 2;
int c = a > b ? a : b;
printf("%d\n", c);

9.逗号表达式

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

表达式1, 表达式2, 表达式3,...表达式N
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//a = 12, b = 13, c = 13

10.下标引用、函数调用和结构成员

[]下标引用操作符

操作数:数组名 和 索引值

int arr[10];
arr[0] = 7;
//也可以这样写,但一般不这样写,只是演示这种写法也正确
0[arr] = 7;

() 函数调用操作符

这里不再详细说明,详情请移步另一篇文章C语言函数详解

接受一个或者多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

#include<stdio.h>
void test1()
{
	printf("hello\n");
}
void test2(char* str)
{
	printf("%s\n", str);
}
int main()
{
	char arr[] = "abcd";
	test1();
	test2(arr);
	return 0;
}

.->访问结构体成员

. 结构体.成员名
-> 结构体指针->成员名

#include<stdio.h>
struct S
{
	int a;
	int b;
};
int main()
{
	struct S s;
	struct S* ps = &s;
	s.a = 10;
	s.b = 20;
	ps->a = 100;
	ps->b = 200;
	return 0;
}

11.表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

隐式类型转换

在C 语言中,类型转换的方式一般可分为隐式类型转换显示类型转换(也称为强制类型转换)。其中隐式类型转换只由编译器自动进行,不需要程序员干预。
隐式类型转换通常有两种情况: 赋值转换和运算转换

C语言中,隐式类型转换遵循以下规则:

1.在对变量赋值时,若等号两边的数据类型不同,需要把右边表达式的类型转换为左边变量的类型,这可能导致降低精度,丢失的部分按四舍五入向前舍入。

2.若参与运算变量的类型不同,则先转换成同一类型,然后进行运算。

3.**转换按照数据长度增加的方向进行,以保证精度不降低。**比如 int 类型的数据和 double 类型的数据相加时,int 类型的数据就会被隐式地转换成 double 类型,然后再进行运算。

4.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

5.char类型和short类型参与运算时,必须先转换成int类型。

整型提升

C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的char类型和short类型操作数在使用之前被转换为int类型,这种转换称为整型提升。

整型提升:有符号补符号位,无符号则补 0。

示例一:

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

打印结果是 -126

在这里插入图片描述
示例二:

#include<stdio.h>
int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}

前面提到:char类型和short类型参与运算时,必须先转换成int型。
在这里插入图片描述
示例三:

#include <stdio.h>
 
int main()
{
	char a = 1;
	printf("%zd\n", sizeof(a));  // 1
	printf("%zd\n", sizeof(+a));  // 4
	printf("%zd\n", sizeof(-a));  // 4
	// char 类型的变量 a 作为 +/- 的操作数参与运算时,需要进行整型提升
	return 0;
}

a参与了+ -操作符的运算,整型提升成int类型

算数转换

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

long double
double
float
unsigned long int
long int
unsigned int
int

从上往下,等级依次降低,转换按照数据长度增加的方向进行,比如int和float类型进行运算,int类型会转换成float类型再进行运算。

注:算数转换可能会存在一些潜在问题,比如会有精度丢失

double pi = 3.14;
int num = pi; //3

示例:

在这里插入图片描述

这是因为sizeof的返回值类型是unsigned int,int类型的num隐式转换成unsigned int无符号整型,最高位不是符号位。
-1的原码:10000000 00000000 00000000 00000001 即2^31-1 要远比4大

CPU寄存器的比特位是统一的,将内存中的数据放入寄存器中就会发生隐式转换。
c语言的操作符对多个操作数进行操作时,必须保证其类型一致。

操作符的属性

复杂表达式的求值有三个影响的因素:

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序

两个相邻的操作符先执行优先级高的。如果两者的优先级相同,取决于他们的结合性。

操作符优先级从上往下依次递减,重点记住以下这几个:

操作符描述用法示例结果类型结合性是否控制求值顺序
()聚组(表达式)与表达式相同N/A
[ ]下标引用rexp[rexp]rexpL-R
.访问结构成员lexp.member_namerexpL-R
->访问结构指针成员lexp->member_namerexpL-R
++后缀自增lexp ++rexpL-R
后缀自减lexp - -rexpL-R
!逻辑反! rexprexpR-L
~按位取反~ rexprexpR-L
++前缀自增++ lexprexpR-L
- -前缀自减- - lexprexpR-L
*间接访问* rexplexpR-L

示例:

int* p1[10] = { 0 };  //指针数组
int (*p2)[10] = { 0 };//数组指针

因为[]的优先级比*的优先级高,所以p1先与[ ]结合,是数组类型,再与 *\ 结合,所以是指针数组,数组中有10个元素,每个元素是int* 类型。
()的优先级最高,所以p2先与*结合,是指针类型,再与[ ]结合,所以是数组指针,这个指针指向一个数组,指向的数组有10个元素,每个元素是int类型。

详细内容等后面指针篇再整理。

问题表达式:

c + --c;

操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,在不同编译环境下结果不同。

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

同上,这种具有歧义的表达式在不同编译环境下的结果不同,这段代码中的第一个 +在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序。

总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的,我们要避免写出这种问题表达式。

内容概要:本文深入探讨了折扣店快消品定价的研究,涵盖快消品与折扣店行业的概述、定价影响因素、定价策略、定价方法、定价技巧及案例分析。快消品行业涉及日常生活用品、食品、饮料等,具有高频次和重复购买的特点。折扣店市场规模不断扩大,主要参与者包括赵一鸣、好特卖等。影响定价的因素包括成本(生产、库存、物流)、市场需求(规模、购买力、需求弹性)、竞争(对手定价策略、市场份额)、产品特性(差异化、品牌形象)、渠道与分销成本、政府政策等。定价策略分为成本导向(成本加成、目标收益)、需求导向(理解价值、需求差异)和竞争导向(随行就市、投标定价)。定价方法包括市场调研、成本加成、竞争比较和价值定价。定价技巧涵盖高价撇脂、渗透定价、价格歧视和捆绑定价。最后通过好特卖、嗨特购、零食很忙的案例分析,展示了不同折扣店的定价策略及其效果。 适合人群:从事快消品行业、折扣店运营的管理人员及对定价策略感兴趣的商业人士。 使用场景及目标:①帮助折扣店管理者了解定价的基本理论和实际操作方法;②为快消品企业提供定价决策支持,优化定价策略;③通过案例分析,提供实际操作中的经验和教训。 其他说明:折扣店在制定快消品定价时,应综合考虑多种因素,灵活运用不同的定价策略和技巧,以应对市场变化,提升竞争力。建议企业定期监控市场动态,分析消费者反馈,不断优化定价方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值