C语言复习 - 操作符

算术操作符

+         -         *         /         %

1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。

2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

移位操作符

左移操作符:左移操作符(<<)是一种位操作符,用于将一个数的二进制表示向左移动指定的位数。在左移操作中,低位补零。

int x = 5; int y = 2; int result = x << y;

  • 数字 5 的二进制表示是 101。
  • 将 101 左移 2 位,得到 10100,对应的十进制数是 20。

右移操作符:右移操作符(>>)是一种位操作符,用于将一个数的二进制表示向右移动指定的位数。在右移操作中,高位的空位通常由原来的最高位填充(称为“符号位扩展”),但对于无符号数,通常用零填充。

#include <stdio.h>

int main() {
    int x = -8; // 11111111111111111111111111111000
    unsigned int y = 8; // 00000000000000000000000000001000
    int result_signed = x >> 2; // 11111111111111111111111111111110 (-2)
    unsigned int result_unsigned = y >> 2; // 00000000000000000000000000000010 (2)
    printf("%d\n", result_signed);
    printf("%u\n", result_unsigned);
    return 0;
}
  • 对于有符号整数 -8,其二进制表示是 11111111111111111111111111111000。
    • 将其右移 2 位,得到 11111111111111111111111111111110,对应的十进制数是 -2。
  • 对于无符号整数 8,其二进制表示是 00000000000000000000000000001000。
    • 将其右移 2 位,得到 00000000000000000000000000000010,对应的十进制数是 2。

需要注意的是,对于负数的右移,有些编译器可能使用算术右移(将符号位扩展到高位),有些编译器可能使用逻辑右移(在高位补零)。

位操作符

& 按位与

对两个操作数的每一位执行逻辑与操作。只有在两个操作数的对应位都为1时,结果的对应位才为1,否则为0。 

| 按位与

对两个操作数的每一位执行逻辑或操作。只要两个操作数的对应位中有一个为1,结果的对应位就为1,否则为0。 

^ 按位异或

对两个操作数的每一位执行逻辑异或操作。如果两个操作数的对应位不同,则结果的对应位为1,否则为0。

A^0 = A    A^A = 0   A^(B^C) = (A^B)^C

A ^ 0 = A:对任何数 A 进行异或 0,结果都是 A 本身。这是因为异或 0 的操作不会改变 A 的任何位,相当于保持原值。

A ^ A = 0:对同一个数 A 进行异或自身,结果总是 0。这是因为 A 的每一位与自身对应位异或得到 0。

A ^ (B ^ C) = (A ^ B) ^ C:异或操作满足结合律。无论括号内的操作先进行哪个,结果都是相同的。这也是异或操作可以无括号进行的原因之一。

妙用:一个是交换两个数字,另一个是找到一个数组中只出现一次的数字。

复合赋值符

  1. +=:加法赋值运算符,用于将右操作数的值加到左操作数,并将结果赋给左操作数。例如:a += b 相当于 a = a + b

  2. -=:减法赋值运算符,用于将右操作数的值从左操作数中减去,并将结果赋给左操作数。例如:a -= b 相当于 a = a - b

  3. *=:乘法赋值运算符,用于将左操作数乘以右操作数,并将结果赋给左操作数。例如:a *= b 相当于 a = a * b

  4. /=:除法赋值运算符,用于将左操作数除以右操作数,并将结果赋给左操作数。例如:a /= b 相当于 a = a / b

  5. %=:取模赋值运算符,用于将左操作数除以右操作数的余数赋给左操作数。例如:a %= b 相当于 a = a % b

  6. >>=:右移赋值运算符,将左操作数向右移动右操作数指定的位数,并将结果赋给左操作数。例如:a >>= b 相当于 a = a >> b

  7. <<=:左移赋值运算符,将左操作数向左移动右操作数指定的位数,并将结果赋给左操作数。例如:a <<= b 相当于 a = a << b

  8. &=:按位与赋值运算符,将左操作数与右操作数执行按位与操作,并将结果赋给左操作数。例如:a &= b 相当于 a = a & b

  9. |=:按位或赋值运算符,将左操作数与右操作数执行按位或操作,并将结果赋给左操作数。例如:a |= b 相当于 a = a | b

  10. ^=:按位异或赋值运算符,将左操作数与右操作数执行按位异或操作,并将结果赋给左操作数。例如:a ^= b 相当于 a = a ^ b

单目操作符

! 逻辑反操作

- 负值

+ 正值

& 取地址

sizeof 操作数的类型长度(以字节为单位)

#include <stdio.h>

void test1(int arr[]) {
    printf("%d\n", sizeof(arr)); // (2)
}

void test2(char ch[]) {
    printf("%d\n", sizeof(ch)); // (4)
}

int main() {
    int arr[10] = {0};
    char ch[10] = {0};

    printf("%d\n", sizeof(arr)); // (1)
    printf("%d\n", sizeof(ch)); // (3)

    test1(arr);
    test2(ch);

    return 0;
}
(1) 和 (3):在 main() 函数中,sizeof(arr) 和 sizeof(ch) 分别是数组 arr 和 ch 的大小。因为它们都是数组,在 main() 函数中,它们返回的是数组的总字节数,即 10 * sizeof(int) 和 10 * sizeof(char)。

(2) 和 (4):在 test1() 和 test2() 函数中,参数 arr[] 和 ch[] 都被视为指针,因此 sizeof(arr) 和 sizeof(ch) 都返回指针的大小。在大多数情况下,指针的大小是 4 或 8 字节,具体取决于系统的架构。

~ 对一个数的二进制按位取反

-- 前置、后置--

++ 前置、后置++

前置#号:在变量前使用#号,表示先改变变量的值,然后返回改变后的值。

后置#号:在变量后使用#号,表示先返回变量的值,然后再改变变量的值。

* 间接访问操作符(解引用操作符)

(类型) 强制类型转换

关系操作符

>         >=         <         <=        !=           ==

逻辑操作符

&&        || 

int main() {
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;
    //i = a++ || ++b || d++;
    printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
    return 0;
}

对于逻辑或 && 操作符,如果左操作数为假,则右操作数不会被求值首先,a++ 被计算。a 的值是0,然后 a 的值增加为1。但是,a++ 返回的是0,因为它是先使用再增加的后缀自增运算符。因此,a++ 返回的是假值,即0。因为 a++ 返回了假值,所以整个表达式的结果已经确定为假。因此,++bd++ 不会被计算。最终,i 被赋值为0。 

对于逻辑或 || 操作符,如果左操作数为真,则右操作数不会被计算,因为整个表达式的结果已经确定为真。在这里,a++ 的结果为0,因此左操作数为假,接着计算 ++bb 的值会增加为3,因此整个表达式结果为真。所以 d++ 不会被计算,d 的值保持不变为4。 

条件操作符

条件操作符(也称为三元条件运算符)是 C 语言中的一种特殊运算符,它可以在一个表达式中根据条件选择不同的值。它的一般形式如下: 

condition ? expression1 : expression2

其中,condition 是一个条件表达式,如果为真(非零),则整个表达式的值为 expression1,否则为 expression2

逗号表达式

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

表达式求值

整形提升

在 C 语言中,整型提升是一种类型转换规则,它确保在进行整型算术运算时,所有参与运算的值都至少被转换为普通整型(int)的精度。

具体来说,整型提升遵循以下规则:

  1. 如果表达式中的操作数包含 char、signed char、unsigned char 或者 short 类型,则它们在参与运算之前会被提升为 int 类型。
  2. 如果表达式中的操作数包含 char、signed char、unsigned char、short、unsigned short,以及它们的等效类型,则它们会被提升为 int 或者 unsigned int 类型(根据系统的实现情况而定)。
  3. 如果表达式中的操作数包含 float 类型和 double 类型,则 float 类型的操作数会被提升为 double 类型。

整型提升确保了表达式中的所有操作数具有相同的类型,从而避免了在混合类型表达式中出现精度丢失或不一致的情况。这种规则使得 C 语言中的整型算术运算更加可靠和一致。

int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}

c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节.

表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

操作符的属性
  1. 操作符的优先级:操作符的优先级决定了在表达式中哪些操作符先于其他操作符执行。例如,在表达式 a + b * c 中,乘法操作符 * 的优先级高于加法操作符 +,因此先执行乘法运算,再执行加法运算。

  2. 操作符的结合性:操作符的结合性定义了当表达式中有相同优先级的多个操作符时,它们的求值顺序。例如,加法和减法操作符的结合性是从左到右(左结合性),因此在表达式 a + b - c 中,先执行 a + b,然后再执行减法运算。而赋值操作符 = 的结合性是从右到左(右结合性),因此在表达式 a = b = c 中,先执行 b = c,然后再将结果赋值给 a

  3. 是否控制求值顺序:有些操作符不确定其操作数的求值顺序。例如,逻辑与 && 和逻辑或 || 操作符,它们具有短路特性,即在第一个操作数确定整个表达式的值之后,不再对第二个操作数求值。这意味着在表达式 a && b 中,如果 a 的值为假,那么 b 将不会被求值。

 

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

C 语言标准并没有定义在同一个表达式中多次修改同一个变量的行为。因此,这个表达式是未定义的行为(UB),在不同的编译器和不同的情况下,可能会产生不同的结果。这段代码中的第一个 + 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序

在某些编译器中,可能会将 (++i) + (++i) + (++i) 解释为 2 + 3 + 4,因为每次 ++i 都会使 i 增加1。但是在另一些编译器中,可能会产生不同的结果,甚至可能会导致程序崩溃。

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值