问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深度探索 C 语言操作符:从基础到实战应用

创作时间:
作者:
@小白创作中心

深度探索 C 语言操作符:从基础到实战应用

引用
CSDN
1.
https://blog.csdn.net/2402_86350387/article/details/145444872

C语言操作符是编程中不可或缺的一部分,深入理解和熟练运用它们,能够帮助我们编写出更加高效、稳定的程序。本文将从基础到实战应用,逐步展开,帮助读者全面、深入地理解C语言操作符。

一、操作符的分类全景

C语言的操作符种类丰富,涵盖算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式,以及下标引用、函数调用和结构成员操作符等。它们各司其职,在不同的编程场景中发挥着关键作用。

二、算术操作符:数值运算的基石

算术操作符是编程中最常用的操作符之一,包括+(加法)、-(减法)、*(乘法)、/(除法)和%(取余)。除了%操作符仅适用于整数运算外,其他操作符对整数和浮点数均适用。

在进行除法运算时,若两个操作数都是整数,执行的是整数除法,结果会舍去小数部分。例如:

int result = 7 / 3;

上述代码中,7 / 3的结果为2,因为整数除法会舍弃小数部分。

若操作数中有浮点数,则执行浮点数除法,结果会保留小数部分:

float result = 7.0 / 3;

在这段代码里,7.0 / 3的结果为2.333333,这体现了浮点数除法的特性。

%操作符用于计算两个整数相除后的余数,例如:

这里7 % 3的结果是1,即7除以3的余数。

三、移位操作符:二进制世界的舞者

移位操作符分为左移操作符<<和右移操作符>>,它们直接对二进制位进行操作。左移操作符的规则是左边抛弃,右边补0。例如,将整数5(二进制表示为:00000000000000000000000000000101)左移一位:

int shiftedLeft = 5 << 1;

执行代码后,shiftedLeft的值为10,因为左移一位后,二进制变成00000000000000000000000000001010,转换为十进制就是10。

右移操作符的移位规则较为复杂,分为逻辑移位和算术移位。

  • 逻辑移位:左边用0填充,右边丢弃;
  • 算数右移:左边用原数的符号位填充,右边丢弃。

例如,对于负数-1(补码为11111111111111111111111111111111):

int shiftedRight = -1 >> 1;

警告⚠: 对于移位运算符,不要移动负数位,这个是标准未定义的

四、位操作符:深入二进制的核心

位操作符包括按位与&、按位或|和按位异或^,操作数必须是整数。这些操作符在处理二进制数据时非常有用。

按位与操作可以用于判断一个数的某些位是否为1。例如,判断一个整数的最低位是否为1:

int num = 7;
if (num & 1) {
    printf("最低位是1\n");
}

在这段代码中,num & 1将num的二进制表示与00000000000000000000000000000001进行按位与操作。如果结果不为0,则说明num的最低位是1。

练习1 :不能创建临时变量(第三个变量),实现两个数的交换:

int a = 5, b = 10;
a = a ^ b;
b = a ^ b;
a = a ^ b;

这段代码利用按位异或的特性,巧妙地实现了两个数的交换,避免了使用额外的临时变量,提高了代码的效率。

练习2 :求一个整数存储在内存中的二进制中1的个数

五、赋值操作符:数据传递的纽带

赋值操作符=用于将右侧的值赋给左侧的变量,它还支持连续赋值。例如:

int a, b, c;
a = b = c = 10;

在这段代码中,a = b = c = 10从右向左依次赋值,最终a、b、c的值都为10。

此外,还有复合赋值符,如+=、-=、*=、/=等,这些操作符可以简化代码。例如,a += 5等价于a = a + 5:

int a = 10;
a += 5;

使用复合赋值符不仅代码更简洁,还能提高编程效率。

六、单目操作符:功能强大的“独行侠”

单目操作符只需要一个操作数,功能多样。例如,逻辑反操作!可以将真(非0)变为假(0),将假变为真:

int num = 5;
if (!num) {
    printf("num是0\n");
} else {
    printf("num不是0\n");
}

sizeof操作符用于计算操作数的类型长度(以字节为单位):

int num = 10;
printf("num占用的字节数:%d\n", sizeof(num));
printf("char类型占用的字节数:%d\n", sizeof(char));

这里sizeof(num)返回num所占用的字节数,sizeof(char)返回char类型占用的字节数。不同系统下,这些值可能会有所不同,但在常见的32位和64位系统中,int通常为4字节,char为1字节。

七、关系操作符和逻辑操作符:程序逻辑的导航仪

关系操作符用于比较两个值的大小关系,包括>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、!=(不等于)和==(等于)。在使用时,要特别注意==和=的区别,避免因写错而导致逻辑错误。例如:

int a = 5, b = 10;
if (a > b) {
    printf("a大于b\n");
} else {
    printf("a不大于b\n");
}

这段代码通过关系操作符判断a和b的大小关系,并根据判断结果执行相应的代码块。

逻辑操作符包括逻辑与&&和逻辑或||,用于组合多个条件。逻辑与操作只有当所有条件都为真时,结果才为真;逻辑或操作只要有一个条件为真,结果就为真。例如:

int a = 5, b = 10;
if (a > 0 && b > 0) {
    printf("a和b都大于0\n");
}

在这段代码中,&&和||分别组合了多个条件,控制程序的执行流程。

八、条件操作符和逗号表达式:灵活编程的秘籍

条件操作符exp1? exp2 : exp3是一种简洁的选择结构,根据exp1的真假来决定执行exp2还是exp3。例如,求两个数中的较大值:

int a = 5, b = 10;
int max = a > b ? a : b;

这段代码使用条件操作符简洁地求出了a和b中的较大值(如果a>b为真就执行a,反之,执行b)

逗号表达式exp1, exp2, exp3, …expN从左向右依次执行,整个表达式的结果是最后一个表达式的结果。例如:

int a = 1, b = 2, c;
c = (a++, b++, a + b);

在这段代码中,逗号表达式先执行a++和b++,然后计算a + b的值并赋给c。最终c的值为5,因为a++和b++执行后,a变为2,b变为3,a + b即为5。

九、下标引用、函数调用和结构成员操作符:C语言中的重要工具

在C语言丰富的操作符家族中,下标引用、函数调用和结构成员操作符是进行数据访问和函数执行的关键元素,它们各自有着独特的功能和使用方式。

1、下标引用操作符[]

下标引用操作符用于访问数组中的元素。它的操作数为一个数组名和一个索引值。

在C语言中,数组是一种重要的数据结构,用于存储多个相同类型的数据。通过下标引用操作符,可以方便地访问数组中的特定元素。例如:

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int value = arr[9];

在上述代码中,[]的两个操作数分别是数组名arr和索引值9。需要注意的是,C语言中的数组索引是从0开始的,这意味着对于一个长度为n的数组,合法的索引范围是0到n - 1。如果使用了超出这个范围的索引,会导致未定义行为,可能引发程序崩溃或产生错误的结果。

此外,下标引用操作符还可以与指针结合使用。在C语言中,数组名在很多情况下会被隐式转换为指向数组首元素的指针。因此,arr[i]和*(arr + i)是等价的,它们都表示访问数组arr中索引为i的元素。例如:

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *ptr = arr;
int value = *(ptr + 9);

2、函数调用操作符()

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

函数是C语言中实现模块化编程的重要手段,通过函数调用操作符,可以执行预先定义好的函数逻辑。例如:

int add(int a, int b) {
    return a + b;
}

int result = add(3, 5);

在上述代码中,add是函数名,作为函数调用操作符()的第一个操作数,3和5是传给add函数的参数。函数调用操作符会触发函数的执行,函数执行完毕后会返回一个值(在这个例子中,add函数返回两个参数的和),可以将这个返回值赋值给变量或者进行其他操作。

函数调用操作符的灵活性很高,不仅可以传递常量作为参数,还可以传递变量、表达式等。同时,函数也可以没有参数,例如:

void printHello() {
    printf("Hello, World!\n");
}

printHello();

在这个例子中,printHello函数没有参数,调用时只需要使用函数名和空的函数调用操作符()即可。

3、结构成员操作符.和->

结构是C语言中用于将不同类型的数据组合在一起的一种自定义数据类型。结构成员操作符用于访问结构中的成员。

'.'操作符:当使用结构变量来访问成员时,使用.操作符。例如:

struct Student {
    char name[50];
    int age;
};

struct Student stu = {"Alice", 20};
printf("Name: %s, Age: %d\n", stu.name, stu.age);

在上述代码中,stu是struct Student类型的变量,通过.操作符,stu.name访问结构体中的name成员,stu.age访问age成员。

'->'操作符:当使用指向结构的指针来访问成员时,使用->操作符。例如:

struct Student {
    char name[50];
    int age;
};

struct Student stu = {"Alice", 20};
struct Student *ptr = &stu;
printf("Name: %s, Age: %d\n", ptr->name, ptr->age);

在这个例子中,ptr是指向struct Student类型的指针,通过->操作符,ptr->name和ptr->age分别访问结构体stu中的name和age成员。实际上,ptr->name等价于(*ptr).name,->操作符提供了一种更简洁的方式来通过指针访问结构体成员。

下标引用、函数调用和结构成员操作符在C语言编程中扮演着不可或缺的角色。熟练掌握它们的使用方法,能够帮助开发者更加高效地进行数据处理和函数调用,构建出结构清晰、功能强大的程序。

十、表达式求值:背后的规则与陷阱

表达式求值的顺序受到操作符优先级、结合性以及是否控制求值顺序等因素的影响。在进行表达式求值时,还可能涉及隐式类型转换和算术转换。

隐式类型转换是为了保证整型运算的精度,会将字符和短整型操作数在使用前转换为普通整型。例如:

char a = 'A';
char b = 'B';
int result = a + b;

字符'A'和'B'在参与a + b运算时,由于C语言的整型算术运算总是至少以缺省整型类型(通常为int类型的精度)来进行,所以会先进行整型提升。'A'的ASCII码值是65,'B'的ASCII码值是66,它们在进行整型提升时,会将其对应的ASCII码值按照int类型的长度进行扩展:

  • 对于有符号char类型(很多编译器下默认char是有符号的),如果char类型的最高位(符号位)为0,就在高位补0;如果最高位为1,就在高位补1(有符号位补符号位)。
  • 对于无符号char类型,无论原char类型数据的最高位是什么,都在高位补0(没符号位补0)。

这里'A'和'B'的ASCII码值对应的二进制最高位都是0,所以整型提升后高位补0,然后再进行加法运算,65 + 66 = 131,最终result的值为131。

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

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算

但要注意,不合理的算术转换可能会导致精度丢失等问题。例如:

float f = 3.14;
int i = f;

在这段代码中,float类型的f转换为int类型时,小数部分会被截断,导致精度丢失。

有些表达式由于操作符优先级和结合性无法确定唯一的计算路径,结果是不可预测的,属于有问题的表达式。例如:

int i = 0;
int result = i++ + ++i;

在不同的编译器中,这段代码的结果可能不同,因为i++和++i的执行顺序无法通过操作符的优先级和结合性确定。在编写代码时,应尽量避免写出这样的表达式,确保程序的正确性和稳定性。

C语言操作符是编程中不可或缺的一部分,深入理解和熟练运用它们,能够帮助我们编写出更加高效、稳定的程序。希望通过本文的介绍,大家对C语言操作符有更全面、更深入的认识,在编程实践中能够灵活运用这些知识,解决各种实际问题。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号