C生万物 | 操作符汇总大全【庖丁解牛,精细讲解】—— 上

举报
烽起黎明 发表于 2023/02/27 17:11:54 2023/02/27
【摘要】 ✒C语言操作符汇总大全,全程精析讲解,拨开云雾见天明☀

一、算术操作符

因为MarkDown的语法,所以用图片的形式显示
在这里插入图片描述

  • 对于算术操作符而言有上面这五种,对于前面的【+】、【-】、【*】来说操作数可以是整数或者浮点数
  • 对于【/】来说,叫做整除,结果就是我们在数学中说到的。若是两遍都是整数,则执行执行整数除法;只有当两个操作符数中有一个数是小数的时候可以算作是浮点数除法
  • 对于【%】来说,叫做取余,结果就是我们在数学中说到的余数。它的两个操作数必须是整数

我们到VS中来验证一下

在这里插入图片描述
在这里插入图片描述


再来看看取余操作符

在这里插入图片描述
在这里插入图片描述

二、移位操作符

接下去我们来说说移位操作符,它是一个为操作符,操作的是二进制数,因为比较重要,所以单独拿出来做讲解

在这里插入图片描述

1、前言小知识 —— 原码、反码、补码

在讲述左移、右移操作符之前,要给读者先介绍一下原码、反码、补码,以便更好地理解二进制位的概念

  • 对于一个数值来说,可以由不同的进制来表示,在计算机内部,有【二进制、八进制、十进制、十六进制】
  • 假设一个十进制数15,它的八进制表示形式即为17,十六进制为F,二级制为1111。这个要涉及到每一位上的权重概念,这里的内容很多不好叙述,可以看看——> 进制转换(二进制、八进制、十进制、十六进制)
    在这里插入图片描述
  • 对于一个整数来说,在内存中的二级制表示形式有【原码】【反码】【补码】三种,它们写成二进制位的形式也就是32位二进制,例如整数4,它的原码即为0 0000000000000000000000000000100。可以看到我将最高位和后面的31位做了一个分隔,因为对于32个二进制位来说,最高位叫做符号位
  • [x] 符号位是0,表示正整数
  • [x] 符号位是1,表示负整数
  • 而对于一个正数来说,它的原码、反码、补码都是相同的;对于一个负数来说,它的反码等于原码除符号位外其余按位取反,补码等于反码 + 1
    在这里插入图片描述

看到这里的读者可能会很好奇,不是要讲移位操作符吗,为什么要先讲这些呢❓

  • 在这里你需要记住这一句话,它在后面的位操作符中同样适用

==在计算机中都是使用二进制数的补码进行运算的,但是在计算完之后输出的结果都要再转化为原码的形式==

2、左移操作符【扩大2倍】

有了上面的知识做铺垫,接下去就让我们真正地进入移位操作符的学习:book:

📚【移位规则】:左边抛弃、右边补0

  • 首先来看到的是要计算一个数字4左移1位后的,将其运算后的结果放到b里面去。那我们知道,在内存中进行计算都是使用补码的形式,因为依次写出4的原、反、补码,因为它是整数,所以均是相同的
int main(void)
{
	int a = 4;
	//0 0000000000000000000000000000100 - 4的原码
	//0 0000000000000000000000000000100 - 4的反码
	//0 0000000000000000000000000000100 - 4的补码

	int b = a << 1;		//把a向左移动一位
	printf("a = %d, b = %d\n", a, b);
	return 0;
}

在这里插入图片描述

  • 通过图示就可以看出,将一个二进制数进行左移运算,要执行的规则是左边丢弃,右边补0,其实这起到的效果就是将原来的数乘上一个2倍,因为对于每一个相邻的二进制数说,均是一个2倍的关系,因为其运行出来的结果即为4 * 2 = 8,也就是b = 8

在这里插入图片描述

  • 可以看到,这个原来的数字a是不会发生变化的,只是对其进行一个运算,将其运算之后的结果放到b中去而已

接着我们再来看看负数的情况

  • 对于正数来说,其实看不出其在内部中的数值变换,因为原、反、补码均是相同的;但是对于负数来说可不一样了,可以看到下面是对-4进行一个移位的操作,写出其补码之后就开始了移位操作
int main(void)
{
	int a = -4;
	//1 0000000000000000000000000000100 - -4的原码
	//1 1111111111111111111111111111011 - -4的反码
	//1 1111111111111111111111111111100 - -4的补码

	int b = a << 1;		//把a向左移动一位

	//1 1111111111111111111111111111000 - -4移位后的的补码
	//1 1111111111111111111111111110111 - -4移位后的的反码
	//1 0000000000000000000000000001000 - -4移位后的的原码
	printf("a = %d, b = %d\n", a, b);
	return 0;
}

在这里插入图片描述

  • 但是在移位之后我们还要将二进制数转换为原码的形式,上面说到过,虽然在计算机内部是采用补码的形式进行计算的,但是输出打印在屏幕是是采用原码的形式,所以还是要按照负数原、反、补码的规则进行一个转换,最后得出的结果再转换为十进制便是8

在这里插入图片描述

3、右移操作符【缩小2倍】

讲完左移操作符,接下去我们再来讲讲右移操作符>>

📚【移位规则】:
① 逻辑移位
👉左边用0填充,右边丢弃
② 算术移位
👉左边用原该值的符号位填充,右边丢弃

  • 可以看到,对于右移操作符,和左移不同的是它的运算规则比较复杂,因为在不同编译器下对于移位后的符号位填充是有所不同的,但是在VS下采用的是第二种算术移位,所以我以此作为讲解

在这里插入图片描述

  • 因为上面已经给出过代码了,因此这里不做展示,从运行结果可以看出对于右移运算来说就是一个缩小的情况,但具体是如何计算的呢,我们通过画图来看看

在这里插入图片描述


然后我们再来看看负数的情况

  • 对于负数来说还是一样,在计算机内部会将其转化为补码的形式进行运算,然后再内部计算完毕后还要将其转换为原码的形式

在这里插入图片描述

  • 下面是它在内存中转换的情况

在这里插入图片描述
【注意⚠】
👉对于移位运算符,不要移动负数位,这个是标准未定义的

int main(void)
{
	int a = 5;
	int b = a << -1;	//error
	return 0;
}

对于这个移位操作符来说,你可能会决定它不是很常用,所以也没必要很认真地学习,其实这就错了,那是因为你接触得还不够多,其实在某些特定的场合下,它可以起到非常之大的作用,后面有一道综合例题我会进行讲解✒

三、位操作符

好,接下去我们来讲讲位操作符,这也是很多同学长期以来没有搞懂的一块

1、按位与【&】

📚【规则】:全1为1,有0为0

  • 一样,对于位运算来说,在计算机中进行运算的是一个数的补码形式,然后打印在屏幕上是原码的形式
//按位与 - 全1为1,有0为0
int main(void)
{
	int a = 3;
	int b = -5;
	int c = a & b;
	printf("c = %d\n", c);
	//00000000000000000000000000000011 - 3的原码、反码、补码

	//10000000000000000000000000000101 - -5的原码
	//11111111111111111111111111111010 - -5的反码
	//11111111111111111111111111111011 - -5的补码

	//00000000000000000000000000000011
	//11111111111111111111111111111011
	//00000000000000000000000000000011 - 3【补码即为原码】
	return 0;
}
  • 根据按位与的运算规则,我们就可以得出最后的结果为3

在这里插入图片描述

2、按位或【|】

📚【规则】:有1为1,全0为0

//按位或 - 有1为1,全0为0
int main(void)
{
	int a = 3;
	int b = -5;
	int c = a | b;
	printf("c = %d\n", c);
	//00000000000000000000000000000011 - 3的原码、反码、补码

	//10000000000000000000000000000101 - -5的原码
	//11111111111111111111111111111010 - -5的反码
	//11111111111111111111111111111011 - -5的补码

	//00000000000000000000000000000011
	//11111111111111111111111111111011
// --------------------------------------
	//11111111111111111111111111111011 |
	//11111111111111111111111111111010 |
	//10000000000000000000000000000101 | - 5
	return 0;
}
  • 根据按位或的运算规则,我们就可以得出最后的结果为-5

在这里插入图片描述

3、按位异或【^】

📚【规则】:相同为0,相异为1

//按位异或 - 相同为0,相异为1
int main(void)
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
	printf("c = %d\n", c);
	//00000000000000000000000000000011 - 3的原码、反码、补码

	//10000000000000000000000000000101 - -5的原码
	//11111111111111111111111111111010 - -5的反码
	//11111111111111111111111111111011 - -5的补码

	//00000000000000000000000000000011
	//11111111111111111111111111111011
// --------------------------------------
	//11111111111111111111111111111000
	//11111111111111111111111111110111
	//10000000000000000000000000001000 -> -8
	return 0;
}
  • 根据按位异或的运算规则,我们就可以得出最后的结果为-8

在这里插入图片描述

  • 对于异或有两个很重要的结论要记,在使用异或操作符的时候基本都是用到它们

👉两个相同的数异或为0a ^ a = 0
👉任何数和0异或均为那个数本身a ^ 0 = a


4、按位取反【~】

📚【规则】:1变0, 0变1

  • 对于按位取反来说不考虑符号位,也就是将所有的1变成0,所有的0变成1即可。但是不要忘了那句口诀,对于负数的补码来说是需要再进行一个转换的
int main(void)
{
	int a = 0;
	int b = ~a;
	printf("b = %d\n", b);

	//00000000000000000000000000000000
	//11111111111111111111111111111111  按位取反【补码】	
	//11111111111111111111111111111110	【反码】
	//10000000000000000000000000000001 -> -1【原码】
	return 0;
}
  • 那么0按位取反之后就变成了-1

在这里插入图片描述

😈两道很变态的面试题😈

介绍完了所有的位操作符符,接下去我们马上来检验一下学习的成果, 下面是两道历年面试中的考题,比较复杂而且也很多的位运算在里面,因为拿出啦做讲解

① 两数交换

首先第一道比较容易一些,对于两个数的交换相信大家是非常熟悉的

  • 我们可以使用第三方临时变量做一个存放;若是放到个单独的函数中进行交换的操作,那么就要进行对应的传址操作,如果是在C++中的话还可以使用引用操作符&
  • 但是在本文中,我要使用位运算来进行实现,主要是使用到异或位运算^

在这之前,我们通过加减的方式来试着交换一下这两个数

int main(void)
{
	int a = 3;
	int b = 5;
	printf("a = %d, b = %d\n", a, b);

	a = a + b;
	b = a - b;		//a + b - b
	a = a - b;		//a + b - a

	printf("a = %d, b = %d\n", a, b);
	return 0;
}
  • 可以看到,变量中的两个数发生了交换,我们来分析一下它们是如何发生交换的
  • 首先执行的是a = a + b,也就是把a + b的值放到a里面去,接着第二句是b = a - b,因为经过上面一句的运算,a里面存放的已经是a + b的和了,那么再减去b的话也就是a,因为我们要交换两个值,所以就是要将a的值放到变量b里面去,所以拿b来接受
  • 那此时b里面存放的就是a的值了,但是a里面存放的还是a + b的值,所以第三句a = a - b计算出来的结果就是b的值,将其存入变量a中
  • 那么此时打印出来a与b值便是交换之后的值

在这里插入图片描述


  • 但其实对于上面这种方法是有缺陷的,若是a与b是两个很大的数时,就会出现数据溢出的情况,所以在下面我要使用异或^的方法来运算
  • 可以看到使用异或来进行运算的时候总体的思路还是不变的,将加减运算符改成异或运算符之后,就需要使用到我们上面讲到过的异或拓展规则👈
  • 首先第一句a = a ^ b将a和b异或后的结果暂存到a里面去,然后再去异或b的话就相当于是a ^ b ^ b,根据规则便可以得出结果为a,将其放入b中
  • 然后第三句a = a ^ b,就相当于是a ^ b ^ a,那么结果就是b,将其放入a中
int main(void)
{
	int a = 3;
	int b = 5;
	printf("a = %d, b = %d\n", a, b);

	a = a ^ b;
	b = a ^ b;		//a ^ b ^ b = a ^ 0 = a
	a = a ^ b;		//a ^ b ^ a = b ^ 0 = b

	printf("a = %d, b = %d\n", a, b);
	return 0;
}
  • 最后打印出来就是交换之后的结果

在这里插入图片描述

通过这道题,相信你对异或的运算一定可以掌握得很好,平常在进行OJ刷题的时候,其实也是可以使用到的,但这前提是你要会灵活使用

② 进制定位

接下去第二道是比较困难的,因为结合了我们上面所学习的所有位操作符

【需求1】:将变量a的第n位置为1

  • 看到这个需求你可能有点懵,我来解释一下,这个第n位值的不是十进制位,而是二进制位,也就是将一个变量在内存中的32位中的某一位置为1,但是又不能改变其他位置的数字,那有同学瞬间感觉有些烧脑了:ocean:

不过没关系,我们慢慢来分析一下:mag:

① 思路分析

  • 首先不去考虑第n位置,先去考虑某个特定的位置,假设我现在有个变量a为10,那么它的二进制位即为00000000000000000000000000001010,那么此时我们先将其第三位置为1,要怎么去实现呢❓
  • 首先就是要使用到我们上面学习过的按位或 |运算,将第三位按位或上一个1,那么这一位就变成了1,但是呢又不想让其他位置发生变化,那此时就让其他位按位或上一个0即可,若是那个位上为0,那么就是0,若是那个位上为1,那也为1,那也就是00000000000000000000000000000100,但是要如何去获取到这个二进制数呢,此时就又需要使用到我们上面讲到过的一个操作符叫做左移<<那也就是将一个数扩大两倍,这里我们对1进行操作,扩大2倍就是2,再扩大两倍就是我们想要的4,即1 << 2
  • 具体的表达式应该为:[a = a | 1 << 2]

在这里插入图片描述

  • 来看看运行结果

在这里插入图片描述

  • 此时我们已经完成了第一步,若是你想要置哪个位上的数为1的话,那就修改表达式的最后一个数字即可,但是呢这样的话并没有很大的通用性,需要每次运行前做一个修改,此时就来实现题目中的需求
  • 可以再来修改一个位上的数,若是我们要将第5位置为1的话,左移4位即可那也就是1 << 4,最后的结果就是26

在这里插入图片描述

  • 再来看看运行结果

在这里插入图片描述
② 规律总结

  • 从上述的两个例子就可以找出规律
  • [x] 若是要置【第3位】的数为1的话,使用数字1左移2位1 << 2
  • [x] 若是要置【第5位】的数为1的话,使用数字1左移4位1 << 4;
  • [x] 若是要置【第n位】的数为1的话,使用数字1左移(n - 1)位1 << (n - 1);
  • 那么此时的话就可以将这个n作为我们自己输入的一个变量,每次想要修改哪一个直接输入即可
int main(void)
{
	int a = 10;
	int n = 0;

	scanf("%d", &n);
	a = a | 1 << (n - 1);
	//把变量a的第n为置1
	//000000000000000000001010
	//000000000000000000010000
//--------------------------------
	//000000000000000000001110
	printf("a = %d\n", a);
	return 0;
}

在这里插入图片描述


【需求2】:将变量a的第n位置为0

实现了上面这个需求之后,便要去另一个需求,可以将一个二进制数的某一位置为1,那能不能置为0呢❓ 我们来研究研究:mag:

  • 要将一个二进制位置为0的话就又需要使用到我们上面所学习过的按位与&,也就是将需要置0的那一位按位与上一个0即可,因为任何数和0进行与都为0,但是呢又不能使得其他二进制位发生改变,那就要使其他二进制位按位与上一个1即可,若是那个位上为0,那么就是0,若是那个位上为1,那也为1,那此时我们再对刚才的第三位进行一个按位与即11111111111111111111111111111011
  • 可是要怎么产生这些个二进制位呢,还记得刚才使用的1 << 2吗,即00000000000000000000000000000100,那其实仔细观察就可以看出这两个二进制位其实每个位呈现的都是一个相反的趋势,那么我们在上面使用到的位操作符中哪个具有取反的功能呢❓其实说得很明显了,就是按位取反,那其实只需要将刚才求出的那个表达式外层再加上一个按位取反符就可以了
  • 具体的表达式应该为:[a = a & ~(1 << (n - 1))]

在这里插入图片描述

  • 再来看看运行结果

在这里插入图片描述
给出整体代码⌨

int main(void)
{
	int a = 10;
	int n = 0;

	scanf("%d", &n);
	a = a | 1 << (n - 1);
	//把变量a的第n为置1
	//00000000000000000000000000001010
	//00000000000000000000000000000100
//--------------------------------
	//00000000000000000000000000011010
	printf("置1:a = %d\n", a);

	a = a & ~(1 << (n - 1));
	//把变量a的第n为置0
	//00000000000000000000000000001110
	//11111111111111111111111111111011
//--------------------------------
	//00000000000000000000000000001010
	printf("置0:a = %d\n", a);
	return 0;
}

从上述这个案例来看,真的可以说是淋漓尽致地展现了位运算的奇妙之处💡

四、赋值操作符

  • 赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值
int weight = 120;	//体重
weight = 89;		//不满意就赋值
double salary = 10000.0;
salary = 20000.0;	//使用赋值操作符赋值
  • 不仅如此,赋值操作符还可以进行连续使用
int b = a += 10;

在这里插入图片描述

  • 但是对于上面这样的代码其实是不太好的,将所有的运算都堆积堆积在一起,就无法调试了,一个F10就进入了下一行代码
  • 但如果写成像下面这样的话就显得非常清爽而且易于调试
a += 10;
b = a;

复合赋值符

  • 对于赋值操作符来说,还可以和其他操作符进行一个复合的操作
  • 例如:【+=】、【-=】、【*=】、【/=】、【%=】、【>>=】、【<<=】、【^=】等等。下面举出两个例子👇

在这里插入图片描述
在这里插入图片描述

五、单目操作符

1、单目操作符介绍

首先来浏览一下所有的单目操作符👀

在这里插入图片描述

2、【!】逻辑反操作

  • 对于逻辑取反操作符来说,就是[真变假,假变真]
int main(void)
{
	int flag = 0;
	if (!flag)
	{
		printf("haha\n");
	}
	return 0;
}
  • 具体的用法就是像上面这样,若是某个变量为假的时候,就做一些特殊的操作,当然你也可以写成if(flag == 0),不过这样看起来就不像是一个真假的判断
  • 对于这个操作符其实我们在后面数据结构的二叉树中也蛮常用的,因为在二叉树进行向下递归的时候需要有递归出口,那就是当前传进来的根节点是否为NULL,就可以写成if(!root)或者是if(root == NULL),当然如果你听不懂的话可以看看我的二叉树文章,后续对应地去学习一下即可,这里想到里我就顺便说一下

在这里插入图片描述

3、【&】和【*】

  • 对于【&】来说叫做取地址操作符,可以获取一个变量在内存中的地址。就如下面这样去取到这两个整形变量和字符型变量的地址

在这里插入图片描述

  • 当然我现在觉得这样去取地址打印查看太费时间了,那我们就可以将这块地址存放到一个指针中去,在初始C语言的时候我有说到过一个对于地址而言其实就是一个指针,所以我们将其给到一个指针变量是完全没有问题的,只是我们广义上说的指针接收地址

在这里插入图片描述

  • 再带大家稍微回顾一下指针的有关知识,对于pa前面的[*]而言,指的就是此为一个指针变量,而[*]前面的[int]表示这个指针变量所指向的是一个整型的地址。那当前这个指针的名字是什么呢,就是pa,而不是*pa
  • 那这个时候我要获取到这个指针所指向的地址中的对象要怎么做呢,那就是要用到【*】这个解引用操作符

在这里插入图片描述

  • 若是可以获取到这个地址中的对象,那我们就可以对其进行一个修改了,即*pa = 20
  • 但若是你没有使用[*]解引用操作符获取到这个对象就为他赋值,那其实编译器会会报出一个Warning说是==等号两边的类型级别不同==,这其实就是讲一个整型的变量强制给到一个指针变量,才会出现的类型异常问题

在这里插入图片描述

  • 那其实这一步操作就是在使这个指针变量重新指向一块新的地址,即00000014,转换为十进制也就是【20】

在这里插入图片描述

  • 再来拓展一块有关野指针的内容,因为这一个只有相关很危险☠的东西,也是很多初学者容易犯的
*(int*)0x0012f40 = 100
  • 对于上面这个语句,首先我捏造了一个十六进制的整数,然后将其强制化为一个整型地址,在前面加上一个[*]解引用操作符,此时我就可以去修改这个地址中存放着的对象了,将其对象的值修改为100,然后可以看到去编译的时候是没有问题的

在这里插入图片描述

  • 但是一运行起来可以看到发生了写入异常,这其实就是野指针,因为这个十六进制的数值,这个地址是我随手捏造出来的,操作系统并没有为其分配内存,因此这是一块随机的地址值,是不确定的,若是随便去访问这块地址的话就会造成问题
  • 这其实和我们在释放掉一块申请的地址时然后没有将其置为NULL是一个道理,若是你有一个指针去访问这块随机的地址,那么你的这个指针就叫做[野指针]

在这里插入图片描述

听完这些,相信你一定回忆起了一些有关指针和地址的知识点

4、【-】和【+】

对于【-】和【+】这个两个操作符并不常用,其实它们不能完全说只是单目操作符,因为在特定的场景下它们也可以算是一个双目操作符,例如:-6的话就只一个单目操作符,8 - 3的话就是第一个双目操作符,【+】的话也是同理,不做赘述

5、sizeof [⭐]

然后我们再来说说一个操作符,叫做sizeof,你没听错,那就是个操作符👈

① 写在前面

  • 对于sizeof来说,是用来计算操作数的类型长度,它以字节为单位。在sizeof后跟一个小括号(),里面就是你要计算的数据类型,但是很多同学看到这个小括号()的时候很多同学就会认为这是一个函数,它确实和函数的样子很类似。
  • 对于函数,它后面的这个小括号叫做函数调用操作符,在后面我也会说到,可是对于sizeof后面的这个()来说却不一样,这只是一种语法规定罢了,你只需要记住这么去用,而且不要把它当成函数就行😀

  • 使用sizeof()可以去计算很多数据类型的长度,例如一个整型变量、一个指针、一个数组元素大小、一整个数组等等。。。
  • 对于sizeof()而言有其特定的返回值打印格式【%zu
int a = 10;
int* p;
int arr[10];

printf("%zu\n", sizeof(a));			//int 4
printf("%zu\n", sizeof(p));			//int 4
printf("%zu\n", sizeof(arr));		//特殊,计算的是整个数组的大小
printf("%zu\n", sizeof(arr[0]));	//int [10] 40
printf("%zu\n", sizeof(arr[10]));	//int 4

在这里插入图片描述

② sizeof后可省略()

  • 如何更加地去明确sizeof是一个操作符呢,那就是它后面的()其实是可以省略的。看到下面的代码没有语法方面的报错。因为我们平常所见的操作符和操作数结合的时候都不会看到(),==这其实就更有力地说明了它是一个操作符==

在这里插入图片描述

③ sizeof()内部表达式不参与计算

  • 接着来看到下面这段代码,可以看到我在sizeof()内部写了一个表达式,这其实也是合法的,大家来猜一猜输出的结果是多少
int main(void)
{
	short s = 10;
	int a = 2;
	
	printf("%d\n", sizeof(s = a + 2));
	printf("%d\n", s);

	return 0;
}

在这里插入图片描述

  • 看到上面的这个结果你可能会震惊(○´・д・)ノ让我猜猜看你算出来的答案是不是【4】和【7】呢,亦或是其他的答案,虽然sizeof()内部是可以放表达式的,但是呢这个表达式是不参与运算的
  • 我们在看这个sizeof()最后的结果时其实只需要看这个s即可,短整型为2个字节,那如果这个表达式不计算的话s的值也就不会发生变化了,所以最后打印出来是10
  • 那有同学可能会疑惑为什么这个表达式不参与运算呢?又是语法规定吗?
    • 答:一方面是这样,但是在内存中看来,在运行阶段的时候,这个表达式其实早就已经不在了,因为它在编译阶段的时候就会替换成了short

在这里插入图片描述


  • 不过这里还要给读者补充的一点是有关数据截断这个小知识,若是我们不将这个表达式放到sizeof()中去,而是拿到外面来计算,那么最后s的结果还是会发生改变的,不过可以看到对于a + 5的结果它是一个整型,占4个字节,但是变量s呢它是一个短整型,占2个字节,若是 强行将4个字节的数据放到2个字节中去,其实就会发生一个【数据截断】这个一个现象

在这里插入图片描述

  • 下面其实就是在内存中的一个截断操作,强行将一个整型变量给到一个短整型都会造成这样的情况,所以我们在进行赋值的时候要看清楚数据类型
    在这里插入图片描述

④ sizeof() 与数组

  • 最后再来讲讲sizeof()和数组之间的运算,首先看看下面这段代码
#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));
}

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

int main()
{
	int arr[10] = {0};
	char ch[10] = {0};
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(ch));
	test1(arr);
	test2(ch);
	return 0;
}
  • 答案是下面这4个,你猜对了吗,前面3个相信你一定没问题,但是对于最后一个我猜一猜是不是算成【1】了 (^ _^)

在这里插入图片描述

  • 正好通过这一块我们再来回顾一下有关数组名的知识
  • [x] 对于数组名,一般来说指的都是首元素地址
  • [x] sizeof(数组名) —— 计算的是整个数组的大小【特殊情况1】
  • [x] &数组名 —— 获取整个数组的地址【特殊情况2】
  • 有了这些知识,再来分析一下上面的这段代码,首先第一个【sizeof(arr)】,arr表示数组名,那么计算的就是整个数组的大小,这是一个整型数组,数组中有10个元素,每个元素4个字节,那么整个数组的大小就是40个字节
  • 对于【sizeof(ch)】而言也是同理,这是一个字符型数组,数组中有10个元素,每个元素1个字节,那么整个数组的大小就是10个字节
  • 接下去就是将数组名传入函数中,除了两种特殊的情况而言,数组名就相当于是首元素地址,然后函数形参中便是指针进行接受,那对于一个指针而言,无论它是【整型指针】、【字符型指针】【浮点型指针】均为4个字节,当然这是在32为系统下,若是在64位系统下就为8个字节,这其实取决于计算机内存中地址总线的长度,有兴趣可以去了解一下
  • 那既然上面说到一个指针均为4个字节,就可以得出为什么最后一个sizeof(ch)为4了,这也只是在求一个指针的大小,不要和字符串数据所占字节数混淆了

以上便是对于sizeof()这个操作符而言要介绍的所有内容,希望读者能够理解:kissing_closed_eyes:

6、【++】和【- -】

首先来看看前置++和后置++

int a = 10;
int b = ++a;
//a = a + 1; b = a;

printf("a = %d, b = %d\n", a, b);
int a = 10;
int b = a++;
//b = a; a = a + 1; 

printf("a = %d, b = %d\n", a, b);
  • 可以看到,对于这里的b = ++a相当于就是先让a++,然后再把a的值给到b
  • 可以看到,对于这里的b = a++相当于就是先把a的值给到b,然后再让a++

在这里插入图片描述
在这里插入图片描述


接着再来看看前置- -和后置- -

int a = 10;
int b = --a;
//a = a - 1; b = a;

printf("a = %d, b = %d\n", a, b);
int a = 10;
int b = a--;
//b = a; a = a - 1; 

printf("a = %d, b = %d\n", a, b);
  • 可以看到,对于这里的b = --a相当于就是先让a- -,然后再把a的值给到b
  • 可以看到,对于这里的b = a--相当于就是先把a的值给到b,然后再让a- -

在这里插入图片描述
在这里插入图片描述

7、强制类型转换

最后的话再来说一下强制类型转换

  • 比方说我这里将一个double类型的数据强制给到一个int类型的变量,就会出现一种叫做【精度丢失】的现象,若是想要强制给到一个整型的变量,那就要去做一个强制类型转换

在这里插入图片描述

  • 可以看到这样就不会出现问题了,所以你想要把一个不同数据类型的值给到一个另一个数据类型的变量,就可以使用强制类型转换
int a = (int)3.14;

在这里插入图片描述

其余内容见【下】

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。