c语言分层理解(c语言操作符)(2)

简介: 1. 操作符分类操作符有这么几类:算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用、结构成员。

6. 单目操作符

6.1 单目操作符介绍

!(逻辑反操作)

-(负值)

+(正值)

&(取地址)

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

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

–(前置、后置–)

++(前置、后置++)

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

(类型)(强制类型转换)

这里对!(逻辑反操作)解释一下:

#include <stdio.h>
int main()
{
  int i = 0;
  if (!i)//这里的意思是明确的真假
  {
    printf("hehe\n");
  }
  if (i == 0)//而这里的i在判断条件中相当于一个数值
  {
    printf("haha\n");
  }
  return 0;
}

对&(取地址操作符)和*(解引用操作符)解析:

    int arr[10];
    &arr;//取的是整个数组的地址
    //也可以&函数;

提一句:首元素不表示首元素地址的两种情况:

  1. sizeof(数组名),这里的数组名是整个数组的大小空间。
  2. &数组名,这里的数组名是整个数组的大小。
    int a = 10;
    int *p = &a;
    *p = 20;
    //最终a别改为20

通常解引用操作符于指针有关,后期在对其进行理解,这里不在对其过多理解。

对sizeof操作符进行解释(直接看现象):


b8fcd98f945fe99f6a1246da5d23a866.png

这里在打印指针的时候不管是什么类型在32位平台下都是4个字节的大小,在64位平台下都是8个字节的大小。

再看一例;

ff228c75a436a351966ca69b53a6152c.png

这里错误的原因是sizeof的()应该输入表达式。

再看一例:

d5948ea26a53b583ffc077f5e00d0b04.png

这里我们观察到sizeof(s = a + 2)=2;说明计算的是s的空间大小。

6.2 sizeof和数组

还是一样通过现象看本质:


bedd416687118a1ed239af464bd3b82f.png

再看一例:


baf721d9fe6a22cf32039ceaa235e2a9.png

函数传参,传数组名,这里的数组名就是地址,所以在32为平台下是4个字节的大小。

对~(对一个数的二进制按位取反)解析:

#include <stdio.h>
int main()
{
  int a = 10;
  //a=00000000000000000000000000001010
  //~a=11111111111111111111111111110101
  //最高符号位是1,所以是补码
  //11111111111111111111111111110100(~a的反码)
  //10000000000000000000000000001011=-11(~a的原码)
  printf("%d\n", ~a);
  return 0;
}

对~这个符号了解后再来看个实例:

#include <stdio.h>
//把一个数的第n二进制位改为1,在改为0.
int main()
{
  int a = 10;
  //00000000000000000000000000001010
  //假设对第四二进制位修改
  //就是用1对其进行左移操作并用按位或进行操作
  int n = 0;
  scanf("%d", &n);
  ///把一个数的第n二进制位改为1
  a = a | (1 << (n - 1));
  printf("%d\n", a);
  //把一个数的第n二进制位改为0
  a = a & ~(1 << (n - 1));
  printf("%d\n", a);
  return 0;
}

a5f1432ed96d30114550da2a167db975.png

对前置++和后置++以及前置–和后置–解析:

int main()
{
  int a = 3;
  int b = ++a;
  //b = ++a等价于a=a+1,b=a;(先自身++后使用))
  printf("b=%d\n", b);//b=4
  return 0;
}
int main()
{
  int a = 3;
  int c = a++;
  //c = a++等价于c=a,a=a+1;(先使用后自身++)
  printf("c=%d\n", c);//c=3
  return 0;
}
int main()
{
  int g = 2;
  int d = --g;
  //int d = --g;//等价于g=g-1,d=g;(先自身--后使用)
  printf("d=%d\n", d);//d=1
}
int main()
{
  int g = 2;
  int e = g--;
  //e = g--等价于e=g,g=g-1;(先使用后自身--)
  printf("e=%d\n", e);//e=2
}
#include <stdio.h>
int main()
{
  float a = 3.14f;
  int b = (int)a;//float类型强制转化为int类型
  return 0;
}

7. 关系操作符

>,>=,<,<=,!=,==

注意:编译过程中==和=不小心写错,导致错误。

8. 逻辑操作符

&& 逻辑与
|| 逻辑或

逻辑操作符就是判断真假,非0为真,0为假。

看例题:

#include <stdio.h>
int main()
{
  int i = 0, a = 0, b = 2, c = 3, d = 4;
  i = a++ && ++b && d++;
  printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
  return 0;
}//最终输出结果是1234

这里的逻辑与发生短路,逻辑与要从左到右依次执行,碰到0就不用在执行其后面的,这里a++使用的时候是0,在对其进行本身++操作,所以打印a=1,a++使用的时候是0,逻辑与操作在碰到假是时,其后面不再执行,所以后面b,c,d的值都没有变。

#include <stdio.h>
int main()
{
  int i = 0, a = 0, b = 2, c = 3, d = 4;
  i = a++||++b||d++;
  printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
  return 0;
}最终输出结果是1334

这里也是短路问题,从左到右一次执行,逻辑或在碰到真时不在执行其后面表达式,所以a++使用时候是0,然后就要再看++b,而++b是非0为真,所以不在看d++表达式。


总结:逻辑与符号和逻辑或符号都是从左到右依次执行,而逻辑与操作符号是看表达式是否为假,为假就不再看其后的表达式,为真则继续执行,知道碰到为假的表达式。逻辑或符号是只要碰到为真,则不在执行。

9. 条件操作符

exp1 ? exp2 : exp3

有时if else语句不如条件操作符简洁。

if(a>1)
{
    b=3;
}
else
{
    b=1;
}

上面的这段if else语句其实可以用条件操作符,b=(a>1?3:1);

10. 逗号表达式

exp1,exp2,exp3,exp4…

逗号表达式从左到右依次执行,整个表达式结果是最后一个表达式结果。

#include <stdio.h>
int main()
{
  int a = 1;
  int b = 2;
  int c = (a > b, a = b + 10, a, b = a + 1);
  printf("%d\n", c);
  return 0;
}//最终输出13.

11. 下标引用、函数调用、结构成员

11.1下标引用操作符([])

int arr[10];//这个[]是创建数组
arr[9]=10;//这个[]是下标引用操作符,它的操作数是9和arr。

11.2函数调用操作符

#include <stdio.h>
void test1()
{
  printf("hehe\n");
}
void test2(const char* str)
{
  printf("%s\n", str);
}
int main()
{
  test1(); //实用()作为函数调用操作符。
  test2("hello bit.");//实用()作为函数调用操作符。
  return 0;
}

11.3结构成员

a9fd94f2450693f7ac7fc07e65339d1b.png

12. 表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

12.1隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型

提升。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purposeCPU)是难以直接实现两个8比特字节直接相加运算虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

也就是char类型和short类型在运算时首先都要转化为int类型的再进行计算。

无符号整型提升,高位直接补0,有符号高位补符号位。

 例如:

#include <stdio.h>
int main()
{
  char a = 5;
  //00000000000000000000000000000101=5
  //CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度
  //所以是32位,但是a变量是char类型的所以发生截断
  //00000101=a  
  char b = 126;
  //00000000000000000000000001111110=126
  //01111110=b(发生截断)
  char c = a + b;
  //整型提升
  //(a的最高符号位是0,所以在补齐的时候补0)
  //(b的最高符号位是0,所以在补齐的时候补0)
  //a+b=00000000000000000000000000000101+00000000000000000000000001111110
  //00000000000000000000000010000011=c
  //10000011=c(发生截断)
  printf("%d\n", c);
  //%d是十进制的方式打印有符号整数
  //c的最高符号位是1,所以在补齐的时候补1
  //11111111111111111111111100000011(补码)
  //11111111111111111111111100000010(反码)
  //10000000000000000000000011111101=-125(原码)
  return 0;
}

再看一例:

#include <stdio.h>
int main()
{
  char a = 0xb6;
  //10110110=a
  //整型提升后
  //11111111111111111111111110110110(a最高符号位是1,所以补1)
  short b = 0xb600;
  //1011011000000000=b
  //整型提升后
  //11111111111111111011011000000000
  int c = 0xb6000000;
  if (a == 0xb6)//会发生整型提升不会再等于0xb6
    printf("a");
  if (b == 0xb600)//会发生整型提升
    printf("b");
  if (c == 0xb6000000)//int类型不会发生整型提升
    printf("c");
  //最终输出c
  return 0;
}
#include <stdio.h>
int main()
{
  char c = 1;
  printf("%zu\n", sizeof(c));
  printf("%zu\n", sizeof(+c));
  printf("%zu\n", sizeof(-c));
  //printf("%zu\n",sizeof(c+1));//最终输出也是4.
  return 0;
}
//只要short和char类型的运算,就会发生整型提升。

输出结果:


1ff5254927e2851b8986a71cf1e9cfa0.png

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

12.2算术转换

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


2b33c721497a237c0d10b68f29f39a06.png

float f = 3.14;//由double类型转换为float
int num = f;//由float类型转换为int类型

由高精度类型向低精度类型转化会有精度丢失问题的出现。在这里注意只有short类型和char类型转换为int类型时才是整型提升(原因是CPU内整型运算器(ALU)的操作数的字节长度,一般就是int的字节长度,同时也是CPU的通用寄存器的长度),而int类型转化为double等高精度不叫做整型提升,运算时也不遵循整型提升的运算规则。


举一个int类型转换为double和float类型的例子:


b6dc6d81a6af8152f46ce13a8fbf326f.png

可见只是变为了浮点数并补0.













相关文章
|
3月前
|
存储 C语言 索引
【C语言篇】操作符详解(下篇)
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。
72 0
|
3月前
|
程序员 编译器 C语言
【C语言篇】操作符详解(上篇)
这是合法表达式,不会报错,但是通常达不到想要的结果, 即不是保证变量 j 的值在 i 和 k 之间。因为关系运算符是从左到右计算,所以实际执⾏的是下⾯的表达式。
238 0
|
17天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
31 10
|
1月前
|
存储 编译器 C语言
【C语言】简单介绍进制和操作符
【C语言】简单介绍进制和操作符
154 1
|
1月前
|
存储 编译器 C语言
初识C语言5——操作符详解
初识C语言5——操作符详解
164 0
|
3月前
|
C语言
C语言操作符(补充+面试)
C语言操作符(补充+面试)
45 6
|
3月前
|
存储 编译器 C语言
十一:《初学C语言》— 操作符详解(上)
【8月更文挑战第12天】本篇文章讲解了二进制与非二进制的转换;原码反码和补码;移位操作符及位操作符,并附上多个教学代码及代码练习示例
56 0
十一:《初学C语言》—  操作符详解(上)
|
4月前
|
C语言
五:《初学C语言》— 操作符
本篇文章主要讲解了关系操作符和逻辑操作符并附上了多个代码示例
43 1
五:《初学C语言》—  操作符
|
5月前
|
C语言
C语言逻辑操作符的短路问题
C语言逻辑操作符的短路问题
|
5月前
|
算法 C语言
【C语言】:巧用移位操作符,位操作符解决问题
【C语言】:巧用移位操作符,位操作符解决问题
35 0