1.算数运算操作符
C语言中的算术操作运算符包括:
(1)加法运算符(+):用于求两个数的和,如a + b。
(2)减法运算符(-):用于求两个数的差,如a - b。
(3)乘法运算符(*):用于求两个数的积,如a * b。
(4)除法运算符(/):用于求两个数的商,如a / b。
(5)取余运算符(%):用于求两个数的余数,如a % b。
加、减、乘运算符没什么需要注意的地方,但是/和%需要注意一下。
除法运算符(/):分为整数除法和浮点型除法:
整数除法:1/2=0
浮点型除法:1.0/2、1/2.0、1.0/2.0 均等于0.5
取余运算符(%):取余操作数的两端都必须是整数。
2.移位操作符
C语言中的移位操作符用于对二进制数进行左移和右移操作。
需要注意的是,整数在计算机中是以补码形式存放的,这里就涉及到整数的原码、反码和补码的知识。
在C语言中,二进制数的原码、反码和补码是表示同一个数的不同方式。
原码是最基本的二进制表示形式,即将数值的绝对值用二进制表示,正数的原码与其二进制表示相同,负数的原码则将符号位置为1,其余位表示绝对值。
反码是在原码的基础上将负数的符号位之后的每一位取反,即0变为1,1变为0。
补码是在反码的基础上将负数的符号位之后的每一位加1。
通过上述定义,可以得出以下关系:
-
正数的原码、反码和补码相同,都等于其二进制表示。 例如,正数3的二进制表示为00000011,其原码、反码和补码均为00000011。
-
负数的反码等于其原码的符号位不变,其他位取反。 例如,负数-3的原码为10000011,其反码为11111100。
-
负数的补码等于其反码的基础上加1。 例如,负数-3的反码为11111100,其补码为11111101。
使用补码表示负数的好处是可以实现负数的加减运算,且减法可以通过加法来实现。这是因为在补码中,负数与正数的加法运算可以直接使用二进制加法实现,而不需要考虑符号的影响。同时,补码还能够用来表示比原码范围更大的数值,因为补码不仅可以表示负数,还可以表示0和正数。
下面介绍移位操作符:
- 左移操作符(<<):将一个数的二进制表示向左移动指定的位数,右边空缺位用0填充。例如,a << b将a左移b位。
- 右移操作符(>>):将一个数的二进制表示向右移动指定的位数。对于正数,左边空缺位用0填充;对于负数,左边空缺位用1填充,即进行带符号右移。例如,a >> b将a右移b位。
例如:
cs
#include<stdio.h>
int main()
{
int a = 7;
int b = a >> 1;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
运行结果如下:
移位操作符通常用于对一个数进行位操作,移动的位数可以是常量或变量。需要注意的是,移位操作符只针对整数,浮点数不能进行移位操作。
3.位操作符
在C语言中,位操作符是用来操作二进制位的运算符。位操作符可以对整型数据进行位级操作,即对数据的二进制表示进行操作。下面介绍C语言中常用的位操作符及其功能:
-
位与(&):对两个操作数的每个对应位执行逻辑与操作,如果两个位都为1,则结果为1,否则为0。
-
位或(|):对两个操作数的每个对应位执行逻辑或操作,如果两个位中至少有一个位为1,则结果为1,否则为0。
-
位异或(^):对两个操作数的每个对应位执行逻辑异或操作,如果两个位相同,则结果为0,否则为1。
-
位取反(~):对操作数的每个位执行逻辑非操作,即将每个位取反。
需要注意的是,位操作符在不同的机器上可能表现出不同的行为,因为其结果受到底层硬件实现的影响。此外,位操作符一般只适用于整型数据,对于浮点数等其他类型的数据,位操作符的行为是未定义的。
以位与(&)为例:
cs
#include<stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a & b;
//a、b、c均为整型,占4个字节,一个字节为8个比特位(位)
//0000 0000 0000 0000 0000 0000 0000 0011 a的补码(原码、反码)
//1000 0000 0000 0000 0000 0000 0000 1001 b的原码
//1111 1111 1111 1111 1111 1111 1111 0110 b的反码
//1111 1111 1111 1111 1111 1111 1111 0111 b的补码
//0000 0000 0000 0000 0000 0000 0000 0011 a的补码(原码、反码)进行&运算
//0000 0000 0000 0000 0000 0000 0000 0011 因为符号位为0,所以为正数3
printf("c=%d\n", c);
return 0;
}
结果如下所示:
这里需要注意的是位异或(^)操作符
我们以一下例子讲解知识点
例:规定在不创建第3个变量的情况下交换两个变量的值
代码如下:
cs
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d,b=%d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
结果如下:
异或操作符的规则是:如果两个操作数的对应位相同(都为0或都为1),则结果的对应位为0;如果两个操作数的对应位不同,则结果的对应位为1。
即0^a=a;1^a=0;此外,异或操作符还满足交换律,即a^b^a=a^a^b=0^b=b.
4.单目操作符
在C语言中,单目操作符是指只有一个操作数的操作符。以下是C语言中常见的单目操作符:
-
自增操作符(++):将操作数的值增加1。例如:++x 将 x 的值增加1。
-
自减操作符(--):将操作数的值减少1。例如:--x 将 x 的值减少1。
-
正号操作符(+):返回操作数的值,通常用于表示正数。
-
负号操作符(-):返回操作数的负值,即取相反数。
-
逻辑非操作符(!):对操作数进行逻辑取反操作,如果操作数为真,则返回假;如果操作数为假,则返回真。
-
按位取反操作符(~):对操作数进行按位取反操作,即将二进制码的每一位取反。注意:取反操作符与上面的位操作符相结合可以实现对一个二进制数中的某一位进行取反操作
-
强制类型转换操作符((type)):用于将一个表达式强制转换为指定的数据类型。例如将浮点数强制类型转换为整型,如:int a = (int)3.14.
-
解引用操作符(*):用于访问指针所指向的内存地址的值。
-
取址操作符(&):返回操作数的内存地址。
-
sizeof操作符:返回变量或数据类型的字节大小。
5.关系操作符
在C语言中,关系操作符用于比较两个值之间的关系,并返回一个布尔值(0或1)表示比较结果的真假。以下是C语言中常见的关系操作符:
-
相等操作符(==):判断两个操作数是否相等,如果相等则返回真(1),否则返回假(0)。
-
不等操作符(!=):判断两个操作数是否不相等,如果不相等则返回真(1),否则返回假(0)。
-
大于操作符(>):判断左侧操作数是否大于右侧操作数,如果是则返回真(1),否则返回假(0)。
-
小于操作符(<):判断左侧操作数是否小于右侧操作数,如果是则返回真(1),否则返回假(0)。
-
大于等于操作符(>=):判断左侧操作数是否大于或等于右侧操作数,如果是则返回真(1),否则返回假(0)。
-
小于等于操作符(<=):判断左侧操作数是否小于或等于右侧操作数,如果是则返回真(1),否则返回假(0)。
这些关系操作符通常用于条件语句(如if语句、while循环等)中,用于判断不同变量或表达式之间的关系。例如,可以使用等于操作符来判断两个数是否相等,使用大于操作符来判断一个数是否大于另一个数等。
注意,在使用关系操作符时,比较的两个操作数必须是兼容的数据类型。
6.逻辑操作符
在C语言中,逻辑操作符用于组合和比较布尔值(真值)表达式。C语言中常用的逻辑操作符有以下几种:
-
逻辑与(&&):当两个操作数都为真时,结果为真;否则,结果为假。
-
逻辑或(||):当两个操作数中至少有一个为真时,结果为真;否则,结果为假。
-
逻辑非(!):用于取反操作,将真值表达式的结果取反,如果表达式为真,则结果为假;如果表达式为假,则结果为真。
逻辑操作符常用于控制流程和条件判断语句中,例如if语句和while循环语句。在使用逻辑操作符时,需要注意以下几点:
-
逻辑与和逻辑或操作符具有短路特性。即当确定结果后,后续的表达式将不会再进行求值。例如,对于逻辑与操作符(&&),如果第一个操作数为假,则不会再对第二个操作数进行求值;对于逻辑或操作符(||),如果第一个操作数为真,则不会再对第二个操作数进行求值。
-
逻辑操作符具有优先级。逻辑非(!)具有最高优先级,其次是逻辑与(&&),最后是逻辑或(||)。为了避免混淆,可以使用括号来明确优先级。
示例代码:
cs
#include<stdio.h>
int main()
{
int a = 0, b = 1, c = 2;
int i = a++ && ++b && c++;
printf("a=%d\nb=%d\nc=%d\n", a, b, c);
return 0;
}
运行结果如下:
这里可以看到,进行逻辑逻辑与(&&)运算时,第一个操作时为a++,先用后加,所以a=0为假,则不用进行后面的判断,之后a自增1,所以a=1。
7.条件操作符
在C语言中,条件操作符(也称为三元操作符)是一种特殊的操作符,用于简化条件判断的语法。它的语法形式为:条件 ? 表达式1 : 表达式2。
条件操作符由三个部分组成:
-
条件:一个表达式,通常是一个关系表达式,用于判断真假。
-
表达式1:当条件为真时,返回的结果。
-
表达式2:当条件为假时,返回的结果。
条件操作符的使用方法如下:
如果条件为真,返回表达式1的值,否则返回表达式2的值。根据条件的结果,条件操作符会返回表达式1或表达式2的值。
示例代码:
c
int a = 5;
int b = 10;
int max = (a > b) ? a : b; // 将较大的数赋值给max变量
printf("Max: %d", max); // 输出结果为10,表达式b的值
在上述示例中,如果a大于b,则条件为真,返回a的值;否则,条件为假,返回b的值。由于b的值为10,所以max变量被赋值为10。最后,输出结果为10。
条件操作符可以简化条件判断语句的编写,使代码更加简洁和易读。但过度使用条件操作符可能会使代码难以理解,因此需要谨慎使用。
8.逗号表达式
在C语言中,逗号操作符可以用来将多个表达式组合成一个表达式,它的语法形式为:表达式1, 表达式2。
逗号操作符的使用方法如下:
-
逗号操作符的求值顺序是从左到右。首先计算表达式1的值,然后计算表达式2的值,并返回表达式2的值作为整个逗号表达式的值。
-
逗号操作符主要用于以下两种情况:
-
在for循环的表达式部分使用逗号操作符,可以在一个表达式中包含多个表达式,并且在循环迭代之前,所有的表达式都会被求值。
-
在函数调用参数列表中使用逗号操作符,可以将多个表达式作为参数传递给函数,并且这些表达式可以按照预期的顺序进行求值。
-
示例代码:
c
int a = 5, b = 10, c = 15;
int result = (a++, b++, c++); // 逗号操作符用于在一个表达式中递增a、b和c的值
printf("Result: %d", result); // 输出结果为15,逗号表达式的值为c的值
在上述示例中,逗号操作符被用于在一个表达式中递增变量a、b和c的值。表达式(a++, b++, c++)
首先递增a的值,然后递增b的值,最后递增c的值。由于c的值为15,所以逗号表达式的值为15。最后,输出结果为15。
9.下标引用和结构成员操作符
9.1下标引用操作符
在C语言中,下标引用操作符(也称为数组下标操作符)用于访问数组中的元素。
它的语法形式为:数组名[下标]。
下标引用操作符的使用方法如下:
-
数组名是一个数组变量的名称,用于表示要访问的数组。
-
下标是一个整数表达式,表示要访问的数组元素的位置。下标从0开始,代表数组的第一个元素,依次递增。
-
下标引用操作符将返回数组中指定下标位置的元素,并且可以对该元素进行读取或修改操作。
示例代码:
c
int arr[] = {1, 2, 3, 4, 5};
int firstElement = arr[0]; // 获取数组的第一个元素,值为1
arr[2] = 10; // 修改数组的第三个元素的值为10
printf("First element: %d\n", firstElement); // 输出结果为1
printf("Modified array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
// 输出结果为: Modified array: 1 2 10 4 5
在上述示例中,通过下标引用操作符,我们可以访问数组arr
中的元素。arr[0]
表示访问数组的第一个元素,arr[2]
表示访问数组的第三个元素。可以使用下标引用操作符读取数组元素的值,也可以使用下标引用操作符修改数组元素的值。
需要注意的是,使用下标引用操作符时,下标的值应该在数组的有效范围内,否则可能导致访问越界错误。在使用下标引用操作符时,也可以使用变量或表达式作为下标的值,以实现动态访问数组元素的功能。
注:下标引用操作符([ ])不仅可以写成:数组名[下标],也可以写成:下标[数组名]的形式,只是后一种写法不常用,了解即可。
代码示例:
cs
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };
printf("arr[1]=%d\narr[2]=%d\n", arr[1], 2[arr]);
return 0;
}
运行结果如下:
9.2结构体成员操作符
在C语言中,结构体是一种自定义的复合数据类型,它允许我们将不同类型的变量组合成一个单元,以便更方便地管理和操作相关的数据。
结构体成员操作符(也称为结构体成员选择操作符)用于访问结构体变量中的成员。
它的语法形式为:
1.结构体变量名.成员名。
2.结构体指针->成员名。
结构体成员操作符的使用方法如下:
-
结构体变量名是一个结构体变量的名称,用于表示要访问的结构体。
-
成员名是一个标识符,表示要访问的结构体成员的名称。
-
结构体成员操作符将返回结构体变量中指定成员的值,并且可以对该成员进行读取或修改操作。
结构体指针成员操作符的使用方法如下:
-
结构体指针是一个指向结构体的指针变量,用于表示要访问的结构体。
-
成员名是一个标识符,表示要访问的结构体成员的名称。
-
结构体指针成员操作符将返回结构体指针所指向的结构体中指定成员的值,并且可以对该成员进行读取或修改操作。
示例代码:
c
struct Person {
char name[20];
int age;
float height;
};
int main() {
struct Person person1;
strcpy(person1.name, "John");
person1.age = 25;
person1.height = 1.75;
printf("Name: %s\n", person1.name);
printf("Age: %d\n", person1.age);
printf("Height: %.2f\n", person1.height);
return 0;
}
在上述示例中,我们定义了一个名为Person
的结构体,它包含了三个成员变量:name
、age
、height
。在main()
函数中,我们声明了一个名为person1
的结构体变量,并使用结构体成员操作符.
访问结构体的成员。通过person1.name
,我们可以访问Person
结构体中的name
成员,并使用strcpy()
函数将字符串"John"赋值给该成员。通过person1.age
和person1.height
,我们可以分别访问Person
结构体中的age
和height
成员,并对其进行读取和输出。
示例代码:
c
struct Person {
char name[20];
int age;
float height;
};
int main() {
struct Person person1;
struct Person *ptrPerson;
ptrPerson = &person1; // 将指针指向结构体变量
strcpy(ptrPerson->name, "John"); // 使用指针操作符访问结构体成员
ptrPerson->age = 25;
ptrPerson->height = 1.75;
printf("Name: %s\n", ptrPerson->name);
printf("Age: %d\n", ptrPerson->age);
printf("Height: %.2f\n", ptrPerson->height);
return 0;
}
在上述示例中,我们定义了一个名为Person
的结构体,和一个指向该结构体的指针ptrPerson
。通过使用结构体指针成员操作符->
,我们可以通过指针访问结构体的成员。在示例中,通过ptrPerson->name
访问结构体变量person1
的name
成员,并使用strcpy()
函数将字符串"John"赋值给该成员。通过ptrPerson->age
和ptrPerson->height
,我们可以分别访问person1
的age
和height
成员,并对其进行读取和输出。
需要注意的是,通过结构体指针成员操作符->
访问结构体成员时,必须确保指针指向的结构体已经被正确初始化或赋值,否则可能导致访问未定义的内存地址。
示例代码:
cs
#include<stdio.h>
struct Stu
{
char name[20];
int age;
double score;
};
void set_stu(struct Stu* ps)
{
//strcpy((*ps).name, "zhangsan");
//strncpy_s((*ps).name,10, "zhangsan",10);
//(*ps).age = 20;
//(*ps).score = 99.9;
strncpy_s(ps->name, 10, "zhangsan", 10);
ps->age = 20;
ps->score = 99.9;
}
void print_stu(struct Stu ss)
{
printf("%s %d %1f\n", ss.name, ss.age, ss.score);
}
int main()
{
struct Stu s = { 0 };
set_stu(&s);
print_stu(s);
return 0;
}
运行结果如下:
注:strcpy_s()
函数是C语言中的一个字符串函数,用于将一个字符串复制到另一个字符串中。至于为什么不用strcpy
函数,详细解释请看我之前写的文章:关于使用VS 2022版本中strcpy()函数报错问题_vs2022字符串复制函数-CSDN博客