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天前
|
存储 Linux C语言
C语言初阶⑥(操作符详解)编程作业(算数转换)(下)
C语言初阶⑥(操作符详解)编程作业(算数转换)
7 1
|
3天前
|
存储 C语言 索引
C语言初阶⑥(操作符详解)编程作业(算数转换)(上)
C语言初阶⑥(操作符详解)编程作业(算数转换)
11 0
|
7天前
|
C语言
C语言——算术操作符
C语言——算术操作符
10 0
|
8天前
|
编译器 C语言
C语言操作符2
C语言操作符2
5 0
|
8天前
|
C语言
C语言之操作符1
C语言之操作符1
22 0
|
8天前
|
编译器 C语言
操作符详解(C语言基础深入解析)
操作符详解(C语言基础深入解析)
|
8天前
|
C语言
条件操作符和逻辑操作符(C语言零基础教程)
条件操作符和逻辑操作符(C语言零基础教程)
|
8天前
|
存储 编译器 C语言
爱上C语言:操作符详解(下)
爱上C语言:操作符详解(下)
|
8天前
|
编译器 C语言
爱上C语言:操作符详解(上)
爱上C语言:操作符详解(上)
|
8天前
|
算法 测试技术 C语言
【C语言】异或(^)操作符
【C语言】异或(^)操作符
18 0