【ONE·C || 操作符详解】

news2024/9/20 16:44:20

总言

  C语言:各种操作符的使用介绍。

文章目录

  • 总言
  • 1、算术操作符
  • 2、移位操作符
    • 2.1、整体介绍
    • 2.2、左移操作符
    • 2.3、右移操作符(逻辑右移、算术右移)
  • 3、位操作符
    • 3.1、整体介绍
    • 3.2、演示实例
      • 3.2.1、按位与
      • 3.2.2、按位或
      • 3.2.3、按位异或
      • 3.2.4、按位异或的基本性质
    • 3.3、相关练习
      • 3.3.1、不能创建临时变量(第三个变量),实现两个数的交换。
      • 3.3.2、求一个整数存储在内存中的二进制中1的个数。
  • 4、赋值操作符
  • 5、单目操作符
    • 5.1、逻辑真假
    • 5.2、sizeof运算符
    • 5.3、按位取反
    • 5.4、自增自减运算符
  • 6、关系操作符、逻辑操作符
    • 6.1、关系操作符
    • 6.2、逻辑操作符
  • 7、条件操作符、逗号表达式
    • 7.1、条件操作符(三目操作符)
    • 7.2、逗号表达式
  • 8、下标引用、函数调用和结构成员
    • 8.1、下标引用操作符
    • 8.2、函数调用操作符
    • 8.3、结构体成员访问
  • 9、表达式求值
    • 9.1、隐式类型转换
      • 9.1.1、整型提升
        • 9.1.1.1、实例演示一
        • 9.1.1.2、实例演示二
        • 9.1.1.3、演示实例三
      • 9.1.2、算术转换
    • 9.2、操作符的优先级
      • 9.2.1、基础说明
      • 9.2.2、一些问题:子表达式求值

  

1、算术操作符

  1)、整体介绍

+    -   *   /   %

  1、 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2、对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
  3、 % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
  

  2)、小数除法和整数除法

int main()
{
	//整数除法:除法两端操作数皆为整型
	int a = 3 / 2;
	float b = 3 / 2;
	printf("a=%d\nb=%f\n\n", a, b);

	//小数除法:需要操作符有一端操作符为小数
	float c = 3.0 / 2;
	float d = 3 / 2.0;
	printf("c=%f\nd=%f\n\n", c, d);

	//强制类型转换
	float e = (float)3 / 2;
	printf("e=%f\n",e);

	return 0;
}

在这里插入图片描述
  
  
  3)、取模运算符两端操作数和范围说明

int a = 9 % 2 :

  1、两端操作数需要为整型;
  2、%n,其得到的取值范围为0~n-1;
  
  
  

2、移位操作符

2.1、整体介绍

  1)、基本介绍

<< 左移操作符 
>> 右移操作符

  需要注意:
  1、移位操作符的操作数只能是整数
  2、移位操作符作用在二进制位上。
  
  
  2)、整数二进制
  整数的二进制有三种表述形式:原码、反码、补码。
  对正整数:原码、反码、补码相同;
  对负整数:原码、反码、补码需要计算。
  
  

在这里插入图片描述正整数二进制举例:

  正整数,原反补相同。
  对有符号位整型,其最高位是符号位,0表示整数、1表示负数。
  int类型,在32位下为4个byte,即32个bite位。

int a = 5;
原码:00000000 00000000 00000000 00000101
反码:00000000 00000000 00000000 00000101
补码:00000000 00000000 00000000 00000101

  
  

在这里插入图片描述负整数二进制举例:

  对于负整数
  反码:原码的符号位不变,其它位按位取反。
  补码:反码+1即为补码。

int a = -5;
原码:10000000 00000000 00000000 00000101
反码:11111111 11111111 11111111 11111010
补码:11111111 11111111 11111111 11111011

  
  

在这里插入图片描述其它一些问题说明

  1、问题: 一个整型数据存入内存中,存放的是原码、反码、还是补码?
  回答:整数在内存中存储的是补码(VS编译器在内存窗口中使用十六进制展示,实则依旧是二进制),运算也为补码打印或使用时显示原码值
  

  2、说明: 移位操作符在不对自身赋值的情况下,不改变原来的变量。

int a = 5;
int b = a << 5;

  在不自身赋值的情况下,a的值并没有因为使用移位操作符而改变。
  
  3、注意: 对于移位运算符,不要移动负数位,这个是标准未定义的。

int num = 10; 
num>>-1;//error

  
  
  

2.2、左移操作符

  1)、左移操作符演示

  规则说明:
  左移操作符,左边抛弃,右边补0。
  

在这里插入图片描述实例演示一:正数

  相关代码如下:

int main()
{
	int a = 5;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:
在这里插入图片描述

  左移操作符丢弃最高位,在有符号整型中,其丢弃的是最高位的符号位。
  输出结果如下:
在这里插入图片描述

  
  

在这里插入图片描述实例演示二:负数

  相关代码如下:

int main()
{
	int a = -5;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:

在这里插入图片描述

  需要注意:从补码回到原码,一种方法是,对补码-1,对反码非符号位按位取反。
  另外一种方法是,对补码按位取反,对得到的结果再加1。

11111111 11111111 11111111 11110110//补码
10000000 00000000 00000000 00001001//补码非符号位按位取反
10000000 00000000 00000000 00001010//得到结果再+1

  输出结果如下:
在这里插入图片描述

  
  
  

2.3、右移操作符(逻辑右移、算术右移)

  1)、右移操作符演示

  右移运算分两种,逻辑右移和算术右移。

  对逻辑右移:左边用0填充,右边丢弃。
  对算术右移:左边用原该值的符号位填充,右边丢弃。
  使用右移操作符默认为哪种模式,取决于编译器,常见编译器一般为算术右移。(VS2019下也是默认算术右移)
在这里插入图片描述

  
  
  2)、逻辑右移演示

在这里插入图片描述实例演示一:正数

  相关代码如下:

int main()
{
	int a = 5;
	int b = a >> 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:

在这里插入图片描述

  
  
  

在这里插入图片描述实例演示二:负数

  相关代码如下:

int main()
{
	int a = -5;
	int b = a >> 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:
在这里插入图片描述

  
  
  3)、算术右移演示

在这里插入图片描述实例演示一:正数

  相关代码如下:

int main()
{
	int a = 5;
	int b = a >> 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:对于正数,算术右移和逻辑右移结果一致。
在这里插入图片描述
  
  
  

在这里插入图片描述实例演示二:负数

  相关代码如下:

int main()
{
	int a = -5;
	int b = a >> 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

  分析:
在这里插入图片描述

  
  
  
  

3、位操作符

3.1、整体介绍

  1)、概述

& //按位与
| //按位或
^ //按位异或

  需要注意:
  1、位操作符的操作数只能是整数
  2、位操作符作用在二进制位上。即,按二进制位与&,按二进制位或|,按二进制位异或^
  
  3、注意区分取地址操作符和按位与操作符。虽然符号一致,但含义不同。

&a;//取地址操作符,是单目操作符
a&b;//按位与操作符,双目操作符
a&&b;//逻辑与操作符

  
  整体规则:

按位与:有00,全11
按位或:有1皆为1
按位异或:相同为0,相异为1

  
  

3.2、演示实例

3.2.1、按位与

  相关规则:

按位与:对应的二进制位,有0为0,全1为1

  相关代码如下:

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	printf("%d\n", c);
	return 0;
}

  分析:

  int a = 3;

a为整型,其补码如下:
00000000 00000000 00000000 00000011

  int b = -5;

b为整型,其补码如下:
10000000 00000000 00000000 00000101  原码
11111111 11111111 11111111 11111010  反码
11111111 11111111 11111111 11111011  补码

  int c = a & b;

00000000 00000000 00000000 00000011 //3的补码
11111111 11111111 11111111 11111011 //-5的补码
------------------------------------
00000000 00000000 00000000 00000011  //得补码,高位为0是正数,原反补相同,打印结果为3

  
  输出结果:
在这里插入图片描述

  
  
  
  

3.2.2、按位或

  相关规则:

按位或:对应的二进制位,有1皆为1

  相关代码如下:

int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;
	printf("%d\n", c);
	return 0;
}

  分析:

  int a = 3;

a为整型,其补码如下:
00000000 00000000 00000000 00000011

  int b = -5;

b为整型,其补码如下:
10000000 00000000 00000000 00000101  原码
11111111 11111111 11111111 11111010  反码
11111111 11111111 11111111 11111011  补码

  int c = a | b;

00000000 00000000 00000000 00000011 //3的补码
11111111 11111111 11111111 11111011 //-5的补码
------------------------------------
11111111 11111111 11111111 11111011  //得补码,高位为1是负数,打印要转换为原码
11111111 11111111 11111111 11111011 //补码
11111111 11111111 11111111 11111010 //反码
10000000 00000000 00000000 00000101 //原码,输出结果为-5

  
  输出结果:
在这里插入图片描述

  
  
  

3.2.3、按位异或

  相关规则:

按位异或:对应的二进制位,相同为0,相异为1

  相关代码如下:

int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
	printf("%d\n", c);
	return 0;
}

  分析:

  int a = 3;

a为整型,其补码如下:
00000000 00000000 00000000 00000011

  int b = -5;

b为整型,其补码如下:
10000000 00000000 00000000 00000101  原码
11111111 11111111 11111111 11111010  反码
11111111 11111111 11111111 11111011  补码

  int c = a ^ b;

00000000 00000000 00000000 00000011 //3的补码
11111111 11111111 11111111 11111011 //-5的补码
------------------------------------
11111111 11111111 11111111 11111000 //得补码,高位为1是负数,打印要转换为原码
11111111 11111111 11111111 11111000 //补码
11111111 11111111 11111111 11110111 //反码
10000000 00000000 00000000 00001000 //原码,输出结果为-8

  
  输出结果:
在这里插入图片描述

  
  
  

3.2.4、按位异或的基本性质

  1)、按位异或的基本性质
  1、交换律: A ^ B = B ^ A
  2、结合律: A ^ (B ^ C) = (A ^ B) ^ C
  3、恒等律: X ^ 0 = X
  4、归零律: X ^ X = 0
  5、自反: A ^ B ^ B = A ^ 0 = A
  6、对于任意的X:X ^ (-1) = ~ X
  7、如果 A ^ B = C 成立,那么 A ^ B = C ,B ^ C = A
  
  
  2)、衍生小运用

  一、
  问题:用异或运算找出一串已知数字中没有重复的数。
  例如:1、2、3、4、5、1、2、3、4
  
  
  二、
  问题:一个数组存放若干整数,一个数出现奇数次,其余数均出现偶数次,找出这个出现奇数次的数。
  解法:将所有的数全部异或,得到的结果就是那个数。
  
  
  三、
  问题:1-1000放在含有1001个元素的数组中,只有唯一的一个元素重复,找出这个重复的数字。要求不能使用辅助存储空间并且数组的每个元素只能访问一次。
  解法一:将这1001个元素加起来的和减去1+2+……+1000,所得的值就是重复的数字(数据过大容易溢出)
  解法二:将所有的数全部异或,得到的结果与1 ^2 ^3 ^ …^1000的结果进行异或,得到的结果就是重复数。
  
  
  四、
  一个整形数组里除了两个数字之外,其它的数字都出现了两次。找到这两个只出现一次的数字。
  解析:剑指offer,40
  
  
  

3.3、相关练习

3.3.1、不能创建临时变量(第三个变量),实现两个数的交换。

  1)、通常情况下的两数交换
  创建临时变量的方法,实例开发中,多用此方法。

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

  
  2)、一种相对投机的写法
  此方法只是单纯打印a、b,而非使得a、b内存储的数据发生交换

	int a = 3, b = 5;
	printf("a=%d,b=%d\n", a + b - a, a + b - b);

  
  3)、常规方法一
  此法面临的问题是,当a、b两数都很大时,相加得到的和可能会超出整型所表示的范围,即溢出。

	int a = 3, b = 5;
	a = a + b;
	b = a - b;//(a+b)-b = a
	a = a - b;//(a+b)-a = b
	printf("a=%d,b=%d\n", a, b);

  
  4)、改进方法二
  此方法虽解决了上述问题,但也有其局限性,因为位操作符只能作用于整型变量。

	int a = 3, b = 5;
	a = a ^ b;
	b = a ^ b;//(a^b) ^ b = a
	a = a ^ b;//(a^b) ^ a = b
	printf("a=%d,b=%d\n", a, b);

  
  
  

3.3.2、求一个整数存储在内存中的二进制中1的个数。

  1)、方法一:位操作符+移位操作符结合

int main()
{
	int num = 0;
	scanf("%d", &num);

	int count = 0;
	for (int i = 0; i < 32; ++i)
	{
		if (1 == ((num >> i) & 1))
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

  细节说明:
  1、对于右移运算符有逻辑右移、算术右移,若是高位补1,会导致数据多出1,上述方法还奏效吗?
  回答:可行。只要我们确定了机型,对应整型变量位数,那么for循环移位时并不会统计到因位运算生成的1。
  
  
  
  
  

4、赋值操作符

  分为简单赋值操作符和复合赋值操作符,可连续赋值。

=
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

  
  
  

5、单目操作符

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转换

  
  

5.1、逻辑真假

  1)、概述
  1、C语言中,0为假,非零为真。
  2、C语言中,C99前没有表示真假的类型,C99中引入了布尔类型。布尔类型中,true为真,false为假。

#include<stdbool.h>
int main()
{
	//写为_Bool和bool是一样的
	_Bool flage1 = true;
	bool flage2 = false;
	return 0;
}

在这里插入图片描述

  
  
  

5.2、sizeof运算符

  1)、sizeof运算符的使用
  ①sizeof可与普通变量、变量类型结合,计算变量类型的长度,以字节为单位,注意:sizeof不是函数!函数后的形式参数必须有括号,而sizeof后不一定需要有括号。

int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));//可以填变量
	printf("%d\n", sizeof(int));//也可以填变量的类型

	printf("%d\n", sizeof a);//类型不能省略括号,变量可以
	//printf("%d\n", sizeof int);//error

	return 0;
}

  ②sizeof与数组名结合,数组名非首元素地址的特殊形式之一,表示整个数组元素总长度,单位为字节。(前提条件为数组没有传参)

	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));

  问题:数组作为参数时,使用sizeof运算后的结果是什么?(见后续)
在这里插入图片描述

  
  
  2)、sizeof运算符中表达式
  如下:下述代码结果问什么?

int main()
{
	int b = 10;
	short c = 5;
	printf("%d\n", sizeof(c = b + 2));
	printf("%d\n", c);

	return 0;
}

  输出结果如下:
在这里插入图片描述
  
  相关解释:
  ③sizeof运算符中的表达式不参与计算。c = b + 2,我们无需知晓c的具体计算结果,直接就能知道其为短整型,即大小可明确。
  原因:从.c文件到.exe可执行程序,要经过编译、链接两步骤。sizeof(表达式)在编译期间已经被处理了,即将sizeof(c = b + 2)直接替换为2(这是明确跑不掉的),故后期.exe运行时,printf直接输出替换后的值。
  
  
  3)、sizeof和数组

  如下述代码,请问1、2、3、4其输出结果为?

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)
}

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

int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr);
	test2(ch);
	return 0;
}

  输出结果如下:
在这里插入图片描述
  相关解释:
  1、sizeof内部单独放数组名,表示整个数组
  2、数组传参时,传递的是首元素地址,形式参数接收到的实际上是指针,指针在不同机位上有固定的大小。32位机器上,有32bit位,而8个bit位1byte,则使用sizeof运算符求取数组首元素地址时,固定为4字节。
  
  
  

5.3、按位取反

  1)、基本介绍
  同位操作符一样,其作用于二进制位上,对每一个二进制位进行按位取反,0变1,1变0,包括符号位。

int main()
{
	int a = 0;
	printf("%d\n", ~a);
	return 0;
}
00000000 00000000 00000000 00000000 //a
11111111 11111111 11111111 11111111 //~a(补码)
11111111 11111111 11111111 11111110 //~a反码
10000000 00000000 00000000 00000001 //~a原码:-1

在这里插入图片描述

  
  2)、作用演示
m
  问题引入1:如何将整数11的二进制低位第三个0变为1?

1100000000 00000000 00000000 00001011
要求:将其变为如下
00000000 00000000 00000000 00001111

  解决方案如下:

00000000 00000000 00000000 00001011 //11
00000000 00000000 00000000 00000100 //进行按位或
00000000 00000000 00000000 00001111 //就可以得到结果
	int a = 11;
	int b = a | (1 << 2);

  问题引入2:那么,如何再将得到的值返回为原先的11呢?
  解决方案如下:

00000000 00000000 00000000 00001111 //15
11111111 11111111 11111111 11111011 //得到这串数值,与15进行按位与
00000000 00000000 00000000 00001011 //就能返回到11

  问题,11111111 11111111 11111111 11111011如何得到?

11111111 11111111 11111111 11111011 
00000000 00000000 00000000 00000100 //按位取反后
	int a = 11;
	int b = a | (1 << 2);
	int c = b & (~(1 << 2));

  演示结果如下:
在这里插入图片描述

  
  

  3)、解释多组输入时的while(~scanf(“%d”,&n))

while(~scanf("%d",&n))
while(scanf("%d",&n) != EOF)

  scanf读取失败时,会返回EOFEOF被定义为-1~(-1)0,则while循环读取结束。
  
  
  

5.4、自增自减运算符

#include <stdio.h>
int main()
{
    int a = 10;
    int x = ++a;
    //先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。
    int y = --a;
    //先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
    return 0;
}
#include <stdio.h>
int main()
{
    int a = 10;
    int x = a++;
    //先对a先使用,再增加,这样x的值是10;之后a变成11;
    int y = a--;
    //先对a先使用,再自减,这样y的值是11;之后a变成10;
    return 0;
}

在这里插入图片描述
  
  
  
  

6、关系操作符、逻辑操作符

6.1、关系操作符

  1)、基本介绍

>
>=
<
<=
!=   用于测试“不相等”
==   用于测试“相等”

在这里插入图片描述

  注意事项:
  1、注意不要将===混淆写错,导致不必要的错误。
  2、不能用关系运算符来判断字符串是否相等,关于字符串是否相等有专门的函数,即strcmp,但其规律也不是简单的比较字符串长度。

	if ("abcdef" == "abcd")//error
	{	
		//这种写法比较的是这两个字符串首字符的地址
	}

  
  
  
  

6.2、逻辑操作符

  1)、基本介绍

&&     逻辑与
||     逻辑或

在这里插入图片描述
  
  2)、逻辑操作符的短路现象演示例题

  程序输出的结果是什么?

#include <stdio.h>
int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;//(一)
    //i = a++||++b||d++;//(二)
    printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
    printf(" i = %d\n", i);
    return 0;
}

  对于&&:左边为假,则得假,故右边不必执行
  对于||:左边为正,则得真,故右边不必执行
在这里插入图片描述
  
  
  
  

7、条件操作符、逗号表达式

7.1、条件操作符(三目操作符)

  1)、基本说明

exp1 ? exp2 : exp3

  2)、演示实例

  演示一:下述代码如何转换成条件表达式?

if (a > 5)
    b = 3;
else
    b = -3;
 b = (a > 5 ? 3 : -3);   

  
  
  演示二:使用条件表达式实现找两个数中较大值

    int a, b;
    scanf("%d %d", &a, &b);
    printf("%d\n", (a > b ? a : b));

  
  
  

7.2、逗号表达式

  1)、基本说明

exp1, exp2, exp3, …expN

  逗号表达式,就是用逗号隔开的多个表达式。其从左向右依次执行。整个表达式的结果最后一个表达式的结果
  
  
  2)、演示实例

  演示一:以下结果输出为?

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf(" a=%d\n b=%d\n c=%d\n", a, b, c);
	return 0;
}

在这里插入图片描述

  
  演示二:

	if (a = b + 1, c = a / 2, d > 0)

  上述这个if语句,其起到判断作用的是最后一句d > 0,但前面的a = b + 1, c = a / 2,也会依次计算执行,若其中d参与运算,那么会影响后续d值。
  
  
  演示三:
  如下,有一段代码,其处理逻辑是①a = get_val();count_val(a);a > 0。重复上述操作,直到满足循环结束条件。

	a = get_val();
	count_val(a);
	while (a > 0)
	{         //业务处理
		a = get_val();
		count_val(a);
	}

  如果使用逗号表达式,可将其修改为:

	while (a = get_val(), count_val(a), a > 0)
	{
		//业务处理
	}

  
  
  

8、下标引用、函数调用和结构成员

8.1、下标引用操作符

  1)、概述

  基础说明:

[ ] 
操作数:一个数组名 + 一个索引值

  例子如下:

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

  
  2)、下标引用操作符:关于数组的一些小扩展

  arr[i]:对数组使用下标引用操作符,操作数为数组名arr、索引值i
  
  arr[i] 等价于*(arr+i):以i=7为例,arr为首元素地址,对编译器而言,其首先把arr[7]处理成*(arr+7),以这种指针的形式再进行运算。由于加法支持交换律,则上述表达式也可写成*(7+arr)7[arr]的形式,其代表的含义和arr[7]一致。
  即printf("%d %d",arr[7],7[arr]);得到效果一致。(打印地址也一样)

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%d\n", arr[7]);
	printf("%d\n", *(arr + 7));
	printf("%d\n", 7[arr]);
	return 0;
}

在这里插入图片描述

  
  
  

8.2、函数调用操作符

  1)、概述

()
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

  思考问题:函数调用操作符的操作数至少有几个?
  回答:一个,即函数名。

  例子:

#include <stdio.h>
void test1()
{
	printf("hehe\n");
}

void test2(const char* str)
{
	printf("%s\n", str);
}

int main()
{
	test1(); //实用()作为函数调用操作符。
	test2("hello world.");//实用()作为函数调用操作符。
	return 0;
}

  
  

8.3、结构体成员访问

  1)、概述

. 结构体变量.结构体成员
-> 结构体指针->结构体成员

  例子演示:

#include <stdio.h>
struct Stu
{
	char name[10];
	int age;
	double score;
};


void print1(struct Stu ss)//如果是结构体变量,就可以使用.操作符访问
{
	printf("name:%s age:%d score:%.2lf\n", ss.name, ss.age, ss.score);
}

void print2(struct Stu* ps)//如果是结构体指针,可以使用->操作符访问
{
	printf("name:%s age:%d score:%.2lf\n", ps->name, ps->age, ps->score);
}


int main()
{
	struct Stu s = { "王朝",20,93.5f };
	print1(s);
	print2(&s);
	return 0;
}

在这里插入图片描述
  注意事项:
  1、上述结构体中,不能直接修改结构体成员的名字s.name = "马汉";,因为name是数组名,非特例下,数组名是数组首元素地址,不能任意修改。若要修改结构体成员名称,需要使用字符串复制的函数strcpy

	struct Stu s = { "王朝",20,93.5f };
	print1(s);

	//s.name = "马汉";//error
	strcpy(s.name, "马汉");
	print1(s);
	scanf("%s", s.name);//使用scanf也行,s.name得到的是数组名
	print1(s);

  2、->*的关系:ps->name,也可以写成(*ps).name。但在上述举例的结构体中,仍然不能用(*ps).name=“马汉”该种方法修改数组名,因为对name解引用后只访问了一个字节,而此处人物名称不只是一个字节。
在这里插入图片描述

  
  
  
  

9、表达式求值

9.1、隐式类型转换

9.1.1、整型提升

  1)、是什么和为什么

  是什么: 表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
  为什么:
在这里插入图片描述
  

9.1.1.1、实例演示一

  相关代码如下:

int main()
{
	char c1 = 3;
	char c2 = 127;
	char c3 = c1 + c2;
	printf("%d\n", c3);
	return 0;
}

  说明:如上述代码,c1、c2的值被提升为普通整型,然后再执行加法运算。加法运算完成之后,结果将被截断,然后再存储于c3中。
  
  
  如何进行整型提升?
  1、整型提升是按照变量的数据类型符号位来提升的。
  2、有符号整型:对负数整型提升,高位补充符号位,即1,对整数整型提升,高位补充符号位,即0。
  3、无符号整型:整型提升,高位补0。
  
  上述代码分析如下:
  char c1 = 3;

Ⅰ、3是整型,32位下如下:
00000000 00000000 00000000 00000011 (原反补相同)
Ⅱ、存放入char类型的变量中,发生截断:
c1 = 00000011

  char c2 = 127;

Ⅰ、同理,127是整型,32位下如下:
00000000 00000000 00000000 01111111 (原反补相同)
Ⅱ、存放入char类型的变量中,发生截断:
c2 = 01111111

  char c3 = c1 + c2;

Ⅰ、c1 + c2要进行算术运算,需要提升为普通整型。按规则,二者结果如下(有符号char,正数,高位补000000000 00000000 00000000 00000011 (c1)
00000000 00000000 00000000 01111111 (c2)
00000000 00000000 00000000 10000010 (c1+c2)
Ⅱ、c1+c2计算完的结果要存储到char类型的C3中,发生截断:
c3 = 10000010 

  printf("%d\n", c3);

Ⅰ、printf要打印c3,同理需要提升为普通整型,且打印的是原码(有符号char,高位负数补1)
11111111 11111111 1111111 10000010 (补码)
11111111 11111111 1111111 10000001 (反码)
10000000 00000000 0000000 01111110 (原码)
最终结果为-126

  输出结果如下:
在这里插入图片描述

  
  

9.1.1.2、实例演示二

  相关代码如下:

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}

  此题快速判断的方法:b6b转换为二进制是1011char、short:a、b需要整型提升,有符号整型,高位补符号位1。补码到原码符号位不变,仍旧是1,故参与比较时,a、b为负数,而判等运算符右侧的数值为正数,故if语句对a、b不执行。
在这里插入图片描述

  
  

9.1.1.3、演示实例三

  相关代码如下:

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	return 0;
}

  分析:c只要参与表达式运算就会发生整形提升,表达式 +c 发生提升,所以sizeof(+c) 是4个字节。表达式 -c也会发生整形提升,所以sizeof(-c) 是4个字节,但是 sizeof(c) 是1个字节。
在这里插入图片描述

  
  
  
  

9.1.2、算术转换

  1)、是什么
  概念说明:如果某个操作符各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
  以下为寻常算术转换:

long double
double
float
unsigned long int
long int
unsigned int
int

  
  注意事项:
  1、算术转换要具有合理性,否则会有一些潜在的问题。

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

  2、算术转换也是隐式类型转换中的一种。
在这里插入图片描述

  
  
  

9.2、操作符的优先级

9.2.1、基础说明

  1)、基础说明

  复杂表达式的求值有三个影响的因素。
    1、操作符的优先级
    2、操作符的结合性
    3、是否控制求值顺序
  
  注意:
  1、相邻的操作符才讨论优先级。
  2、两个相邻的操作符谁先执行?①取决于他们的优先级。②如果两者的优先级相同,取决于他们的结合性。
  3、关于操作符优先级此处不做详细说明,这里附上一个查看链接:运算符的优先级
  
  
  

9.2.2、一些问题:子表达式求值

  
  1)、实例演示一

//表达式的求值部分由操作符的优先级决定。
a*b + c*d + e*f

  问题分析:上述代码在计算的时候,由于*+的优先级高,只能保证*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行。
  所以表达式的计算机顺序就可能是:

1、a*b
2、c*d
3、a*b + c*d
4、e*f
5、a*b + c*d + e*f
1、a*b
2、c*d
3、e*f
4、a*b + c*d
5、a*b + c*d + e*f

  将其延伸,假如此处的abcdef不是单纯的变量,而是许多表达式呢?那么其执行顺序会导致结果未定义。
  
  
  
  2)、实例演示二

c + --c;

  问题分析:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得知+操作符的左操作数的获取,在右操作数之前还是求值之后,所以结果是不可预测的。
  
  
  
  3)、实例演示三

//非法表达式《C和指针》
int main()
{
	int i = 10;
	i = i-- - --i * (i = -3) * i++ + ++i;
	printf("i = %d\n", i);
	return 0;
}

  不同编译器下的结果:

在这里插入图片描述

  
  
  
  4)、实例演示四

int fun()
{
    static int count = 1;//静态变量
    return ++count;
}

int main()
{
    int answer;
    answer = fun() - fun() * fun();
    printf("%d\n", answer);//输出多少?
    return 0;
}

  问题分析:上述代码 answer = fun() - fun() * fun(); ,我们只能通过操作符的优先级得知:先算乘法,再算减法。函数的调用先后顺序无法通过操作符的优先级确定。
在这里插入图片描述

  
  
  
  5)、实例演示五

#include <stdio.h>
int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret);
	printf("%d\n", i);
	return 0;
}

  问题分析:这段代码中的第一个 + 在执行的时候,第三个++是否执行是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序。
  
  
  
  
  
  
  
  
  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/172525.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

离线文章画像计算--Tfidf计算

2.4.2 Tfidf计算 2.4.2.1 目的 计算出每篇文章的词语的TFIDF结果用于抽取画像 2.4.2.2TFIDF模型的训练步骤 读取N篇文章数据文章数据进行分词处理TFIDF模型训练保存&#xff0c;spark使用count与idf进行计算利用模型计算N篇文章数据的TFIDF值 2.4.2.3 实现 想要用TFIDF进行…

【数据结构初阶(Java)】认识时间复杂度和空间复杂度

目录 前言&#xff1a; 1、算法效率 2、时间复杂度 1、大O的渐近表示法&#xff08;不是一个准确的&#xff09; 2、时间复杂度练习题&#xff08;没有明确要求&#xff0c;计算的时间复杂度就是最坏情况下&#xff09; 3、空间复杂度 前言&#xff1a; 如何衡量一个算法的…

Java中多线程wait和notify的用法

目录 一、wait和notify/notifyAll的由来 二、wait()方法 三、notify方法 3.1 notify的作用 3.2 wait和notify的 相互转换代码图 3.3 notifyAll 四、为什么需要notify和wait都需要上锁&#xff1f; 五、wait和sleep的对比 前言&#xff1a;由于线程之间是抢占式执行的&a…

Linux常用命令——tftp命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) tftp 在本机和tftp服务器之间使用TFTP协议传输文件 补充说明 tftp命令用在本机和tftp服务器之间使用TFTP协议传输文件。 TFTP是用来下载远程文件的最简单网络协议&#xff0c;它其于UDP协议而实现。嵌入式linu…

RTMP协议封装H264和H265协议详解

RTMP协议封装H264和H265协议详解 文章目录RTMP协议封装H264和H265协议详解1 RTMP和FLV2 RTMP协议封装H264视频流2.1 RTMP发送AVC sequence header2.2 RTMP发送AVCC视频帧数据‘3 RTMP协议封装H265视频流1 RTMP和FLV 有关RTMP和FLV格式详细介绍可查看如下文章&#xff1a; http…

2022 Moonbeam的点点滴滴离不开社区支持

Moonbeam成为首个上线波卡的平行链已经有一周年&#x1f382;啦&#xff0c;这是一段疯狂的旅程&#x1f3cd;。 为了纪念这一时刻&#xff0c;我们通过公开数据来回顾这一年的众多里程碑、更新和整体发生的一切。 让我们来回顾一下Moonbeam在2022年取得了哪些成就吧。 &…

GIS二维电子地图开发总结

二维平面地图&#xff0c;目前支撑设备渲染&#xff0c;真实场景&#xff0c;后期电子围栏&#xff0c;运动轨迹等业务需求做铺垫 一、所涉及的技术栈&#xff1a; 1.Openlayers,加载渲染地图 2.Geoserver 发布wms和wfs&#xff08;&#xff09;服务 3.Arcgis,Arcmap,进行源文件…

3.1、Ubuntu20桌面版远程连接SSHMobaXterm远程连接编辑器

连接SSH 安装系统完成并登陆后&#xff0c;输入 修改源码地址 进入apt文件夹 cd /etc/apt 备份文件 cp sources.list sources.list.bak 修改源码地址 vi sources.list # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to # newer versions of…

数据结构初级<排序>

本文已收录至《数据结构(C/C语言)》专栏&#xff01; 作者&#xff1a;ARMCSKGT 你的阅读和理解将是我极大的动力&#xff01; 目录 前言 排序的概念 常见排序简述 正文 直接插入排序 原理 代码实现 分析 希尔排序 原理 代码实现 分析 直接选择排序 原理 代码…

类加载的时机与过程

------ 摘自 周志明 《深入理解Java虚拟机》类加载的时机一个类型从被加载到虚拟机内存中开始&#xff0c;到卸载出内存为止&#xff0c;它的整个生命周期将会经历加载&#xff08;Loading&#xff09;、验证&#xff08;Verification&#xff09;、准备&#xff08;Preparati…

6、数组的常见运算

目录 一、数组的算术运算 二、数组的关系运算 三、数组的逻辑运算 一、数组的算术运算 &#xff08;1&#xff09;数组的加减运算&#xff1a;通过格式AB或A-B可实现数组的加减运算。但是运算规则要求数组A和B的维数相同。 示例1&#xff1a; A[1 2 3 4]B[2 4 6 8]C[1 1 …

三种简洁易行的方法解决基于Vue.js的组件通信

在总结Vue组件化编程的数据通信方面&#xff0c;看了网上的很多资料&#xff0c;都是讲父子组件的数据交互也就是参数传递&#xff0c;在组件的通信方面分几种情况&#xff0c;比如父子组件、非父子的兄弟组件、非父子的其他组件等等&#xff0c;这样看来&#xff0c;基于Vue.j…

STC15系列单片机EEPROM读写示例

STC15系列单片机EEPROM读写示例&#x1f33c;STC15手册有关EEPROM描述 &#x1f33e;STC15系列单片机内部集成了大容量的EEPROM&#xff0c;其与程序空间是分开的。利用ISP/IAP技术可将内部DataFlash当EEPROM&#xff0c;擦写次数在10万次以上。EEPROM可分为若干个扇区&#xf…

Android 蓝牙开发——蓝牙协议配置(七)

蓝牙主要分为两种模式&#xff0c;一种是媒体输出&#xff08;Source&#xff09;端&#xff0c;一种是媒体输入&#xff08;Sink&#xff09;端。也可以理解为服务端&#xff08;Server&#xff09;与客户端&#xff08;Client&#xff09;的关系。 蓝牙配置文件&#xff08;B…

4-1指令系统-指令格式

文章目录一.指令的基本格式1.结构2.长度3.根据操作数地址码数目分类&#xff08;1&#xff09;零地址指令&#xff08;2&#xff09;一地址指令&#xff08;3&#xff09;二地址指令&#xff08;4&#xff09;三地址指令&#xff08;5&#xff09;四地址指令二.扩展操作码指令格…

Maven学习(二):Maven基础概念

Maven基础概念一、仓库二、坐标三、全局setting与用户setting区别一、仓库 仓库&#xff1a;用于存储资源&#xff0c;包含各种jar包&#xff1b;仓库分类&#xff1a; 本地仓库&#xff1a;自己电脑上的存储仓库&#xff0c;连接远程仓库获取资源&#xff1b;远程仓库&#x…

信息论复习—离散信道及其容量

目录 信道的简介&#xff1a; 信道的分类&#xff1a; 离散无记忆信道&#xff08;DMC&#xff09;模型&#xff1a; 转移概率&#xff1a; 离散无记忆信道的转移矩阵 输出仅与当前的输入有关&#xff1a; 后验概率&#xff1a; 离散无记忆信道的后验概率矩阵 &#xf…

spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法

spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法 目录spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法出现问题之前出现的问题&#xff1a;解决办法&#xff1a;方案一&#xff1a;第一种是继承 spring-boot-starter-parent 然后 依赖覆盖方案…

怎么用Python测网速?

“speedtest-cli” 是一个 Python 的第三方库&#xff0c;它可以用来在命令行中测试网络速度。它使用了 Speedtest.net 的服务器来进行测速&#xff0c;并可以提供下载和上传速度、延迟、丢包率等信息。使用这个库可以很方便地在终端中测试网络速度&#xff0c;而无需在浏览器中…

轻量级代码生成器加测试数据生成器

轻量级代码生成器加测试数据生成器介绍代码生成常用注解基本使用全局控制属性模板文件相关属性模板文件配置模拟数据生成自定义词库索引注意事项从已经存在的表完成映射,生成模板代码使用步骤Gitee项目链接 介绍 本项目是一个轻量级代码生成器,并提供多种方式来完成模拟数据的…