learn_C_deep_10 深刻认识C语言重点操作符(上):https://developer.aliyun.com/article/1424645
单引号和双引号
在 C 语言中,单引号和双引号都是用于表示字面量的符号。
1. 单引号:单引号括起来的字符表示字符字面量,比如 'a', 'b', '1' 等。在程序中使用时,单引号里的字符会被编译器转换成该字符的 ASCII 码或 Unicode 码,如 'a' 对应的 ASCII 码是 97,可以用整型变量存储。
示例:char c = 'a';
2. 双引号:双引号括起来的内容表示字符串字面量,比如 "hello","world" 等。在程序中使用时,双引号里的字符串会被编译器转换成一个字符数组,以空字符 '\0' 结尾。
示例:char s[] = "hello world";
需要注意的是,单引号和双引号在 C 语言中是有区别的,不能混淆。在 C 语言中,两者不能互换使用。
//单引号是字符,双引号是字符串 #include <stdio.h> //不同编译器,会有细微的差别 int main() { printf("%d\n", sizeof(1)); printf("%d\n", sizeof("1")); //C99标准的规定,'a'叫做整型字符常量(integer character constant),被看成是int型 printf("%d\n", sizeof('1')); char c = '1'; printf("%d\n", sizeof(c)); return 0; }
这段代码主要演示了 C 语言中单引号和双引号的一些不同用法和特性。具体来说:
1. printf("%d\n", sizeof(1));:sizeof 运算符可以用于计算表达式或数据类型的大小,这里使用 sizeof(1) 计算整型常量 1 的大小,得到结果为 4,因为在大多数 C 语言实现中,整型常量都被视为 int 类型,占用 4 个字节。
2. printf("%d\n", sizeof("1"));:使用 sizeof("1") 计算字符串字面量 "1" 的大小,结果为 2,因为字符串字面量中除了字符本身还会包含一个结尾的空字符 '\0',所以实际大小为 2 个字节。
3. printf("%d\n", sizeof('1'));:使用 sizeof('1') 计算字符字面量 '1' 的大小,结果为 4,因为字符字面量 '1' 被视为整型字符常量,类型为 int,占用 4 个字节。
4. char c = '1'; printf("%d\n", sizeof(c));:定义一个 char 类型的变量 c,初始化为字符字面量 '1',使用 sizeof(c) 计算变量 c 的大小,结果为 1,因为 char 类型占用 1 个字节。这里的字符字面量 '1' 被视为整型字符常量,只不过把整型4个字节数据写到1个字节里,这里一直在发生截断。
需要注意的是,在不同的编译器和不同的标准下,对于整型字符常量和字符字面量的处理方式可能存在差异,建议按照标准的规定使用。下图分别是是gcc和vs编译环境下的结果。
'abcd'这样写有问题吗?
#include<stdio.h> #include<windows.h> int main() { ""; '';//error C2137: 空字符常量 "a"; 'a'; "abcd"; 'abcd';//? char c = 'abcd';//warning C4305: “初始化”: 从“int”到“char”截断 printf("%c\n", c);// d c = 'abcdef';// error C2015: 常量中的字符太多 c = 'abc'; printf("%c\n", c);// c return 0; }
这段代码主要演示了 C 语言中字符串和字符字面量的使用和一些细节问题。具体来说:
1. "" 和 "a":这是两个字符串字面量,分别表示空字符串和包含一个字符 'a' 的字符串。在编译时,C 语言编译器会将字符串字面量转换为字符数组,因此在程序中可以像使用字符数组一样使用字符串字面量。
2. '' 和 'a':这是两个字符字面量,分别表示字符 'a' 和空字符,即不含任何字符的字符字面量。需要注意的是,在 C 语言中,字符字面量必须包含一个字符,因此使用单引号括起来的空字符是错误的语法。
3. "abcd" 和 'abcd':这是正确的语法,字符串字面量 "abcd" 包含 5 个字符(包括结尾的空字符),而字符字面量 'abcd' 被定义为 int,4个字节可以存放数据。
4. char c = 'abcd';:这是合法的语法,但是编译器会发出警告(warning),提示将 int 类型的整数截断为 char 类型。实际上,在 C 语言中,字符常量的类型被定义为 int,因此使用 char 类型的变量存储字符常量时,会发生类型截断。
5. c = 'abcdef';:这是错误的语法,因为字符字面量 'abcdef' 包含了多个字符,已经超过了int类型的字节数。。
6. c = 'abc';:这是合法的语法,但是编译器会发出警告(warning),提示将 int 类型的整数截断为 char 类型。实际上,在 C 语言中,字符常量的类型被定义为 int,因此使用 char 类型的变量存储字符常量时,会发生类型截断。
需要注意的是,使用字符串和字符时,需要注意其长度和类型,避免出现类型不匹配或越界访问等问题。
特殊情况
为何计算机需要字符
计算机需要字符是因为字符是人类交流和存储信息的基本单位之一。在计算机的发展历史中,最初的计算机只能处理数值数据,无法处理字符和文本信息,这限制了计算机在人机交互和信息处理方面的应用。
在计算机中,所有的数据都是以二进制的形式存储和处理的,即由0和1两种状态来表示信息。但计算机所表示的信息类型很多,比如文字、图像、声音等,如果直接使用0和1来表示这些信息显然是不太现实的,因此我们需要一种将这些信息转化为二进制的方法,并且能够让计算机正确地将其解码回来的方法。
这时就出现了字符编码,它是一种将文字、数字、符号等字符转化为计算机可识别的二进制数的方法。常用的字符编码有ASCII码和Unicode码,它们可以将每一个字符都映射到一个独一无二的二进制数上。
例如,在ASCII码中,每个字符都被映射到一个包含7个二进制位的数字上,可以表示128种不同的字符,包括英文字母、数字和标点符号等。而在Unicode码中,每个字符则被映射到一个包含16个二进制位的数字上,可以表示超过13万种不同的字符,包括各种语言的文字、符号和表情等。
通过使用字符编码,计算机可以正确地将输入的文字、图像、声音等信息转化为二进制,并在需要的时候将其解码回来,实现信息的存储、处理和显示等功能。
逻辑运算符
C语言中的逻辑运算符有两种,分别为逻辑与(&&)和逻辑或(||)。它们的作用如下:
1.逻辑与(&&):如果两个操作数都为真,则表达式的值为真;否则,表达式的值为假。
2.逻辑或(||):如果两个操作数中至少有一个为真,则表达式的值为真;否则,表达式的值为假。
//级联两个(多个)逻辑表达式,必须同时为真,结果才为真 //与&(按位与)是不同的概念 #include <stdio.h> int main() { int i = 0; int j = 0; //前置++ i先自增,再使用 if ((++i < 0) && (++j > 0)) { printf("enter if!\n"); } printf("%d, %d\n", i, j); //1, 1 return 0; }
//级联两个(多个)逻辑表达式,必须至少一个为真,结果才为真 //与|(按位或)是不同的概念 #include <stdio.h> #include <windows.h> int main() { int i = 0; int j = 0; //发生短路:一个条件为真,已经不需要在看后续的条件的情况 if ((++i > 0) || (++j > 0)) { //注意更改条件 printf("enter if!\n"); } printf("%d, %d\n", i, j); // 1, 0 system("pause"); return 0; }
短路
逻辑运算符有一个特点,即只要能够确定整个表达式的值,就会立即停止计算。这个特点称为“短路”,执行顺序是从左到右的。主要体现在以下两种情况:
1. 对于逻辑与(&&)来说,如果第一个操作数为假,则整个表达式的值一定为假,所以不会再去计算第二个操作数,直接返回假。
2. 对于逻辑或(||)来说,如果第一个操作数为真,则整个表达式的值一定为真,所以不会再去计算第二个操作数,直接返回真。
#include <stdio.h> int show() { printf("you can see me!\n"); return 1; } int main() { int a = 0; scanf("%d", &a); a == 10 && show(); return 0; }
这段代码的功能是判断用户输入的数是否等于10,如果等于,则调用函数show()。如果不等于,则不会调用该函数。
具体分析如下:
1. 定义了一个函数show(),其功能是输出一段文字并返回1。
2. 在主函数中,定义了一个整型变量a,从用户输入中获取值。
3. 判断a是否等于10,并用逻辑与运算符连接show()函数的调用。由于逻辑与(&&)具有短路特性,所以只有在a等于10时,才会调用show()函数。
4. 最后返回0,表示程序运行正常结束。
#include <stdio.h> int show() { printf("you can see me!\n"); return 1; } int main() { int a = 0; scanf("%d", &a); a == 10 || show(); return 0; }
这段代码的功能是判断用户输入的数是否等于10,如果不等于10,则调用函数show()。如果等于10,则不会调用该函数。
具体分析如下:
1. 定义了一个函数show(),其功能是输出一段文字并返回1。
2. 在主函数中,定义了一个整型变量a,从用户输入中获取值。
3. 判断a是否等于10,并用逻辑或运算符连接show()函数的调用。由于逻辑或(||)具有短路特性,当a不等于10时,才会调用show()函数。
4. 最后返回0,表示程序运行正常结束。
位运算符
C语言中的位运算符可以直接操作二进制数的每一位,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)。
1. 按位与(&):两个相应的二进制位都为1时,结果为1;否则,结果为0。
2. 按位或(|):两个相应的二进制位都为0时,结果为0;否则,结果为1。
3. 按位异或(^):两个相应的二进制位不同时,结果为1;否则,结果为0。
4. 按位取反(~):对操作数的每一位取反,即0变成1,1变成0。
#include <stdio.h> int main() { /* 2的补码:0000 0000 0000 0000 0000 0000 0000 0010 3的补码:0000 0000 0000 0000 0000 0000 0000 0011 */ //|:两个相应的二进制位都为0时,结果为0;否则,结果为1。 // 0000 0000 0000 0000 0000 0000 0000 0011 - 3 printf("%d\n", 2 | 3); //&:两个相应的二进制位都为1时,结果为1;否则,结果为0。 // 0000 0000 0000 0000 0000 0000 0000 0010 - 2 printf("%d\n", 2 & 3); //^:两个相应的二进制位不同时,结果为1;否则,结果为0。 // 0000 0000 0000 0000 0000 0000 0000 0001 - 1 printf("%d\n", 2 ^ 3); //~:对操作数的每一位(包括符号位)取反,即0变成1,1变成0。 // 1111 1111 1111 1111 1111 1111 1111 1111 - 补码 // 1111 1111 1111 1111 1111 1111 1111 1110 - 反码 // 1000 0000 0000 0000 0000 0000 0000 0001 - 原码 - -1 printf("%d\n", ~0); return 0; }
&& vs &、|| vs |的区别
&& vs &
&&:级联的是多个逻辑表达式,需要的是真假结果
& :级联的是多个数据,逐比特位进行位运算
逻辑结果:吃了吗?
数据结果:吃了多少?
|| vs | 同上
关于^的理解
在C语言中,符号“^”是按位异或运算符。它是一个二元运算符,将两个操作数的二进制表示逐位比较,如果相应的位上一个操作数为1,另一个为0,则结果为1,否则结果为0。
// ^ : 逐比特位,相同为0,相异为1 //任何数和0异或,都是它本身 #include <stdio.h> int main() { /* 3的补码:0000 0000 0000 0000 0000 0000 0000 0011 0的补码:0000 0000 0000 0000 0000 0000 0000 0000 -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 */ //任何数和0异或,都是它本身 //0000 0000 0000 0000 0000 0000 0000 0011 //0000 0000 0000 0000 0000 0000 0000 0000 //0000 0000 0000 0000 0000 0000 0000 0011 - 3 printf("%d\n", 3 ^ 0); //0000 0000 0000 0000 0000 0000 0000 0000 //0000 0000 0000 0000 0000 0000 0000 0000 //0000 0000 0000 0000 0000 0000 0000 0000 - 0 printf("%d\n", 0 ^ 0); //1111 1111 1111 1111 1111 1111 1111 1101 //0000 0000 0000 0000 0000 0000 0000 0000 //1111 1111 1111 1111 1111 1111 1111 1101 - -3 printf("%d\n", -3 ^ 0); return 0; }
总结:任何数和0异或,都是它本身。
#include<stdio.h> // ^ : 支持结合律和交换律 //任何一个数和自身异或的结果都是0 int main() { /* 3的补码:0000 0000 0000 0000 0000 0000 0000 0011 1的补码:0000 0000 0000 0000 0000 0000 0000 0001 */ /* 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0000 0000 0010 - 3 ^ 1的结果 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0001 - 3 ^ 1 ^ 3的结果 - 1 */ printf("%d\n", 3 ^ 1 ^ 3); /* 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0000 - 3 ^ 3的结果 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0000 0000 0001 - 3 ^ 3 ^ 1的结果 - 1 */ printf("%d\n", 3 ^ 3 ^ 1); /* 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0000 0000 0010 - (3 ^ 1)的结果 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0000 0000 0000 0000 0001 - 3 ^ (3 ^ 1)的结果 - 1 */ printf("%d\n", 3 ^ (3 ^ 1)); return 0; }
1、^ : 支持结合律和交换律。
2、任何一个数和自身异或的结果都是0。
通过异或交换两个整数
常规写法
//容易出现溢出问题 #include<stdio.h> int main() { int a = 10; int b = 20; a = a + b; b = a - b; a = a - b; return 0; }
为什么会有溢出的问题呢?
在C语言中,当执行二进制位运算时,如果结果超过了变量的可表示范围,就会发生溢出。
假设我们有一个32位有符号整数,其最大值为2,147,483,647,最小为-2,147,483,648。现在假设我们要对其执行以下操作:
```
int a = 2147483647;
int b = a + 1;
```
由于a已经达到了整数的最大值,因此b的值将溢出。在这种情况下,b将是-2,147,483,648,这是整数的最小值。
这是因为有符号整数的最高位(即最左侧位)表示符号,0表示正数,1表示负数。因此,当最高位是1时,整数的值将为负数,且符号位不参与位运算。在这种情况下,当b的值达到2,147,483,648时,它的最高位将变为1,导致整数的值为负数。由于它的符号位不参与运算,因此结果是 -2147483648。
因此,在进行运算时,你需要确保结果不会超出变量所能表示的范围,否则会导致意外的结果。可以使用一些技巧,如类型转换或适当的移位来避免这种情况。
异或写法
#include<stdio.h> int main() { int a = 3; int b = 5; a = a ^ b; b = a ^ b; a = a ^ b; return 0; }
设置指定的比特位为1
#include<stdio.h> //任何数或0保持不变 //任何数或1结果为1 #define SETBIT(x,n) x|=(1<<(n-1)) void ShowBits(int x) { //按位与获取每一位值 int num = sizeof(x) * 8 - 1; while (num >= 0) { if (x & (1 << num)) { printf("1 "); } else { printf("0 "); } num--; } printf("\n"); } int main() { int x = 0; //设置指定的比特位为1 SETBIT(x, 5); //0000 0000 0000 0000 0000 0000 0001 0000 //显示所有的比特位 ShowBits(x); return 0; }
设置指定的比特位为0
#include<stdio.h> //任何数与0结果为0 0 & 0,0 & 1 - 0 - 0 //任何数与1保持不变 0 & 1,1 & 1 - 0 - 1 #define SETBIT(x,n) x&=(~(1<<(n-1))) void ShowBits(int x) { //按位与获取每一位值 int num = sizeof(x) * 8 - 1; while (num >= 0) { if (x & (1 << num)) { printf("1 "); } else { printf("0 "); } num--; } printf("\n"); } int main() { int x = 2; //设置指定的比特位为1 SETBIT(x, 2); //0000 0000 0000 0000 0000 0000 0000 0010 //& //1111 1111 1111 1111 1111 1111 1111 1101 取反 //0000 0000 0000 0000 0000 0000 0000 0000 //显示所有的比特位 ShowBits(x); return 0; }