learn_C_deep_10 深刻认识C语言重点操作符(下)

简介: learn_C_deep_10 深刻认识C语言重点操作符

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;
}
相关文章
|
1天前
|
存储 编译器 C语言
C语言---操作符详解(1)
C语言---操作符详解
|
8天前
|
存储 C语言 索引
【C语言基础】:操作符详解(二)
【C语言基础】:操作符详解(二)
|
16天前
|
编译器 C语言
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)二
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)二
21 3
|
17天前
|
存储 C语言
C语言:详解操作符(下)
C语言:详解操作符(下)
15 3
|
16天前
|
C语言
C语言学习记录——操作符习题、算数转换习题,多解法&优解法&单选题
C语言学习记录——操作符习题、算数转换习题,多解法&优解法&单选题
12 1
|
16天前
|
存储 编译器 C语言
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)一
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)一
18 1
|
1天前
|
编译器 C语言 C++
C语言---操作符详解(2)
C语言---操作符详解
|
8天前
|
存储 编译器 C语言
【C语言基础】:操作符详解(一)
【C语言基础】:操作符详解(一)
TU^
|
8天前
|
存储 C语言
C语言之操作符
C语言之操作符
TU^
10 0
|
17天前
|
存储 编译器 C语言
C语言:详解操作符(上)
C语言:详解操作符(上)