C语言操作符全面精讲:算术、移位与逻辑运算详解
C语言操作符是构建程序逻辑的基础。本文将系统梳理算术、移位、位、赋值、单目、关系、逻辑、条件及逗号表达式等核心操作符的语法规则与注意事项,并结合典型代码案例与面试题,解析整除与取模、左右移位差异、异或特性及逻辑短路机制,帮助开发者编写更规范高效的C代码。
算数、移位与位操作详解
C语言的操作符主要分为十大类,其中算数、移位和位操作涉及数据的底层处理,需特别注意数据类型的限制与运算规则。
第1步:掌握算术操作符与取模规则
目的:明确整数与浮点数在加减乘除中的行为差异,避免类型错误。
入口:源代码编辑环境。
动作:
- 基本运算:
+、-、*、/、%。除%外,其他操作符可作用于整数和浮点数。 - 除法行为:若两个操作数均为整数,执行整数除法(截断小数部分);若任一操作数为浮点数,执行浮点数除法。
- 取模限制:
%的两个操作数必须为整数,返回整除后的余数。
第2步:理解移位操作符与二进制表示
目的:利用移位实现高效的乘除运算,并理解符号位的处理方式。
入口:源代码中定义整型变量。
动作:
- 左移 (
<<):左边丢弃,右边补0。例如10 << 1相当于10 * 2。 - 右移 (>>):分为逻辑右移和算术右移。C标准未严格规定编译器的默认行为,但大多数编译器对负数采用算术右移(符号位填充)。
- 注意事项:不要移动负数位(如
),这是标准未定义的行为。
位操作符实战
位操作符包括按位与 (&)、按位或 (|) 和按位异或 (^),操作数必须为整数。以下是典型的位操作练习与面试题解法。
int main() {
//& 按(2进制)位与
//| 按(2进制)位或
//^ 按(2进制)位异或
int a = 3;
int b = -5;
int c = a & b;
//00000000 00000000 00000000 00000011 3的补码
//10000000 00000000 00000000 00000101 -5 原码
//11111111 11111111 11111111 11111010 反码
//11111111 11111111 11111111 11111011 补码
//00000000 00000000 00000000 00000011 3的补码
//00000000 00000000 00000000 00000011 //按位与 全1为1 其他位0
printf("%d\n", c);
int d = a | b;
//11111111 11111111 11111111 11111011 补码
//00000000 00000000 00000000 00000011 3的补码
//11111111 11111111 11111111 11111011 补码
//10000000 00000000 00000000 00000100
//10000000 00000000 00000000 00000101
printf("%d\n", d);
int e = a ^ b;
//11111111 11111111 11111111 11111011 补码
//00000000 00000000 00000000 00000011 3的补码
//11111111 11111111 11111111 11111000
//10000000 00000000 00000000 00000111
//10000000 00000000 00000000 00001000
printf("%d\n", e);
return 0;
}一道经典的面试题是利用异或 (^) 的特性在不创建临时变量的情况下交换两个整数的值。
int main() {
int a = 3;
int b = 5;
//方法二
a = a ^ b;//3^5
b = a ^ b;//3^5^5=3
a = a ^ b;//3^5^3=5
printf("交换后a=%d,b=%d\n", a, b);
return 0;
}
此外,计算整数二进制中1的个数也是常见考点。通过将数字右移并与1进行按位与操作,可以逐位统计:
int main() {
int m = 15;
int i = 0;
int count = 0;
while (i < 32)
{
if (((m >> i) & 1) == 1)
count++;
i++;
}
printf("count:%d\n",count);
return 0;
}赋值、单目与自增自减操作符
赋值操作符不仅用于初始化,还支持复合赋值(如 +=, <<= 等),能简化代码并提高可读性。在使用连续赋值时,建议分解为单步操作以便于调试。
第3步:辨析单目操作符与sizeof陷阱
目的:正确获取数据类型大小,区分前置与后置自增/自减的执行顺序。
入口:使用 sizeof 关键字。
动作:
- sizeof:是一个单目操作符而非函数,返回操作数占用的字节数。注意
sizeof(a)合法,但sizeof int语法错误。 - 数组参数退化:在函数传参时,数组名退化为指针。因此,在函数内部使用
sizeof(arr)得到的是指针的大小(通常4或8字节),而非数组总长度。
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//(2)//这里是指针,因为传过来的是地址要拿指针接收 在32为机器下 字节为4
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//(4)//同理//这里是指针,因为传过来的是地址要拿指针接收 在32为机器下 字节为4
}
int main()
{
int arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr));//(1)//40
printf("%d\n", sizeof(ch));//(3)//10
test1(arr);
test2(ch);
return 0;
}第4步:掌握前置与后置递增/递减
目的:准确预测变量在表达式中的值变化。
入口:包含自增/自减的代码逻辑。
动作:
- 前置 (++a/--a):先自增/自减,再使用新值。
- 后置 (a++/a--):先使用当前值,再进行自增/自减。
//前置++和--
#include <stdio.h>
int main()
{
int a = 10;
int x = ++a;
//先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。
int y = --a;
//先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
return 0;
}
关系、逻辑与条件表达式应用
第5步:识别逻辑短路效应与优先级陷阱
目的:防止因运算符优先级混淆导致的逻辑错误,利用短路特性优化性能。
入口:包含逻辑与(&&)或逻辑或(||)的条件语句。
动作:
- 逻辑与短路:若左侧为假,右侧不再执行。
- 逻辑或短路:若左侧为真,右侧不再执行。
- 常见错误:混淆
=(赋值)与==(判断);混淆&(按位与)与&&(逻辑与)。
以下面试题展示了逻辑与的短路行为:
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0; }
//程序输出的结果是什么?
由于 a++ 初始为0(假),后续 ++b 和 d++ 均未执行。同理,逻辑或遇到真值即停止:
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
//程序输出的结果是什么?
第6步:使用条件与逗号表达式精简代码
目的:将简单的分支结构转化为紧凑表达式。
入口:需要判断赋值的场景。
动作:
- 条件操作符:
exp1 ? exp2 : exp3。例如找出最大值可用return a > b ? a : b;。 - 逗号表达式:从左向右执行,整个表达式的值为最后一个子表达式的结果。可用于循环控制或连续赋值,但不建议过度嵌套以降低可读性。
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
// c是多少?
//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
a = get_val();
count_val(a);
while (a > 0) {
//业务处理
a = get_val();
count_val(a);
}
//如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0) {
//业务处理
}
数组、函数调用与结构体成员访问
最后三类操作符涉及高级数据结构访问,虽看似简单,但却是C语言区别于其他语言的特有语法。
第7步:正确使用下标、括号与箭头操作符
目的:确保能通过指针或索引准确读写内存中的数据。
入口:定义数组、函数与结构体。
动作:
- 下标引用 [
[]]:虽然语法允许[9]arr,但强烈建议使用标准的arr[9]以保持代码直观。 - 函数调用
():第一个操作数为函数名,其余为参数列表。 - 结构成员访问:直接访问使用点号
.(如stu.age),通过指针访问使用箭头->(如pStu->age)。
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu) {
stu.age = 18;
}
void set_age2(struct Stu* pStu) {
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;//结构成员访问
stu.age = 20;//结构成员访问
set_age1(stu);
pStu->age = 20;//结构成员访问
set_age2(pStu);
return 0;
}以上就是C语言操作符的全面精讲与实用技巧,更多关于C语言基础语法与进阶开发资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









