C语言初阶——5操作符

一、算数操作符

在这里插入图片描述

  1. 除了% 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于/ 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除
    法。
  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。char类型也可以用%
    在这里插入图片描述

1.1 类型转换

C语言中整数除以整数还是整数,直接把小数部分给舍去了,如果两个不同类型的变量进行算数运算,就会触发隐式类型转换有一个内置的转换规则,短的往长的转(int 和 double 这两个,double长)

  • 例子1
#include<stdio.h>

int main()
{
	//5 -> int
	//5.0 -> double
	//5.0f ->float
	int a = 5;
	int b = 2;
	printf("%d\n",a / b);

	return 0;
}

在这里插入图片描述

  • 例子2:
#include<stdio.h>

int main()
{
	//5 -> int
	//5.0 -> double
	//5.0f ->float
	double a = 5.0;
	int b = 2.0;
	printf("%f\n", a / b);

	return 0;
}

在这里插入图片描述

  • 例子3
#include<stdio.h>

int main()
{
	//5 -> int
	//5.0 -> double
	//5.0f ->float
	int a = 5;
	int b = 2;
	printf("%f\n", (double)a / b);

	return 0;
}

在这里插入图片描述
这里涉及到了——强制类型转换(显示类型转换)

  • 例子4——交换函数如果调用的时候没有解引用会发生什么?
void swap(int* x, int* y)
{
	int tem = *x;
	*x = *y;
	*y = tem;
}
int main()
{
	int a = 5;
	int b = 2;
	swap(a, b);

在这里插入图片描述
这里将swap(a,b)传入的的int型a,b,会在swap函数中隐式自动转换为int*
函数中就是(int * 5, int *2)下面 *5 和 *2,表示将地址为5的内存进行解引用 和地址为2的内存解引用了——这两块内存我们是不能用的,只有自己申请的内存我们才能使用(自己创建变量才会申请空间~)
编译器没有报错,但是最后程序会崩溃,说明编译器会自动把int隐式转换为 *int,这是一件非常让人讨厌的事情 。代码出错了但是并没有直接给出错误提示。

做出了这样的分类:
如果这个语言越支持隐式类型转换,称为类型越弱
如果这个语言越不支持隐式类型转换,称为类型越强~
一般总结——强类型好于弱类型;静态类型好于动态类型

1.2 运算符优先级

	printf("%d\n",2+3*5);

结果是多少
在这里插入图片描述

二. 移位运算符

左移操作符:<<
右移操作符:>>
注:移位操作符的操作数只能是整数。
注:对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10;
num>>-1;//error

补充知识(这里说的都是整数):

  1. 数值在计算机中存储的时候是以二进制存存储的,有三种表示方式,原码、反码和补码。
  2. 正数的原码、反码和补码是相同的
  3. 负数的原码、反码和补码需要计算,是不同的
  4. 数值在计算机中的存储是以补码的形式存放的。
  5. 源码转换为反码的转换规则是:符号位不变,其他位按位取反。补码就是在反码的基础上+1。
    在这里插入图片描述

2.1 左移运算符 <<

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

#include <stdio.h>
int main()
{
	int num = 10;//正数原、反补都一样  //00000000 00000000 00000000 00001010 
	int num2 = num << 1;             //00000000 00000000 00000000 00010100

	printf("%d %d", num, num2);

	return 0;
}

在这里插入图片描述

2.2 右移运算符 >>

移位规则:
首先右移运算分两种:

  1. 逻辑移位
    左边用0填充,右边丢弃
  2. 算术移位(平常见到
    左边用原该值的符号位填充,右边丢弃
  • -1的补码是32个1
#include <stdio.h>
int main()
{
	int a = -1;
	//原码:10000000 00000000 00000000 00000001 
	//反码:11111111 11111111 11111111 11111110
	//补码:11111111 11111111 11111111 11111111
	//
	int b = a >> 1;
	printf("%d\n", b);
	printf("%d\n", a);

	return 0;
}

在这里插入图片描述

三、位操作符

  • & 对应的二进制位有0,则为0,两个同时为1,才为1
  • | 对应的二进制位有1则为1,两个同时为0则为0
  • ^ 对应的二进制位:相同为0,相异为1
    异或的重要特性:
    a^0 -> a
    a^a ->0

注:他们的操作数必须是整数

  • 练习
#include <stdio.h>

int main()
{
	int a = 3; //0011
	int b = 4;// 0100

	int c = a & b;//同1为1 -》 0000
	int d = a | b;//有1为1 -》 0111
	int e = a ^ b;//相同为0 ,相异为1 -》 0111

	printf("%d %d %d ",c , d, e);
}

在这里插入图片描述

  • 练习:
    编写代码实现:求一个整数存储在内存中的二进制中1的个数。
#include<stdio.h>


//如何计算一个数的二进制中有几个1
//转换为如何取出一个数字的每一位,判断是否为1
int bitoneCount(int num) {
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (num & (1 << i)) {
			count++;
		}
	}
	return count;
}

  • 练习:不能创建临时变量(第三个变量),实现两个整数的交换
int main()
{
	int a = 3;
	int b = 5;

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

	return 0;
}

四、 赋值操作符

注意区分一下赋值和初始化的区别~~
赋值:变量已经有了,进行修改
初始化:第一次创建变量,设置一个值

eg:
在这里插入图片描述

五、单目操作符

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

5.1 逻辑反

  1. C语言中0表示假,非0表示真。!:表示逻辑反,
#include<stdio.h>
int main()
{
	int flag = 0;
	if (!flag) //!flag = 1
	{
		printf("hello,2025!\n");
	}
	return 0;
}

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

  1. C语言中,C99之后引入了布尔类型。就是用来表示真假的。
    头文件是:#include<stdbool.h>
    在这里插入图片描述示示例:
#include<stdio.h>
#include<stdbool.h>
int main()
{
	bool flag = 0; //按着Ctrl然后单击这个bool就可以跳到头文件里面了
	if (!flag)
	{
		printf("hello,2025!\n");
	}
	return 0;
}

在这里插入图片描述

5.2 正值+ 和负值-

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

在这里插入图片描述

5.3 取地址:&

  1. 可以取出变量的地址
#include<stdio.h>
int main()
{
	int a = 10;
	printf("%d %p\n", a, &a);
	return 0;
}

在这里插入图片描述

  1. &主要和*配合使用

5.4 解引用*

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a; //把a的地址取出来给指针p
	*p = 20; //对指针p解引用就是找到a存放的值,对其进行操作会修改a的值 ,*p等价于 a 
	printf("%d \n", a);
	return 0;
}

在这里插入图片描述

5.5 按位取反 ~

#include<stdio.h>
int main()
{
	int a = 1; //00000000 00000000 00000000 00000001
	int b = ~a; //按位取反得到11111111 11111111 11111111 11111110  
	printf("%d\n", b); //得到的原码需要变成补码。计算机中存储的都是补码。
					// (1)符号位不变,其他位按位取反, 变成 10000000 00000000 00000000 00000001 
					// (2)再加1得到补码 10000000 00000000 00000000 000000010 最后得到 -2
	return 0;
}

在这里插入图片描述

  • 小练习
#include<stdio.h>

int main()
{
	int a = 9;
	//00000000 00000000 00000000 00001001
	//00000000 00000000 00000000 00010000
	//00000000 00000000 00000000 00011001
	//把a的二进制第五位改成1
	a |= (1 << 4);
	printf("%d\n", a);

	//把目前a的第5位改回去,由1变成0
	//00000000 00000000 00000000 00011001
	//00000000 00000000 00000000 00010000   1<<4
	//11111111 11111111 11111111 11101111   ~(1<<4) 
	a &= ~(1 << 4);
	printf("%d\n", a);
	return 0;
}

在这里插入图片描述

5.6 前置后置++ 和前置后置- -

  1. 前置++和后置++
    (1)前置++,先++再赋值:
#include<stdio.h>
int main()
{
	int a = 10;
	int b = ++a;
	printf("a = %d b = %d", a, b);
	return 0;
}

在这里插入图片描述
(2)后置++,先赋值再++

#include<stdio.h>
int main()
{
	int a = 10;
	int b = a++;
	printf("a = %d b = %d", a, b);
	return 0;
}

在这里插入图片描述
(3)++ – 带有副作用的 会影响其本身的值

#include<stdio.h>
//++ -- 带有副作用的 会影响其本身的值

int main()
{
	//1
	int a = 10;
	int b = ++a;//b=11 a=11

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

	return 0;
}
  1. 前置 - - 和后置 - -与前置++ 和后置++原理一致,这里就不赘述了

5.7 sizeof和数组

#include <stdio.h>
void test1(int arr[])//形参这里写的数组,也可以写成指针 int* 
{
	printf("%zd\n", sizeof(arr));//(2)这里是指针长度,在64位机子上长度是8 在32位机子上长度是4和指针类型无关
}
void test2(char ch[])
{
	printf("%zd\n", sizeof(ch));//(4) 这里是指针长度,在64位机子上长度是8 在32位机子上长度是4,和指针类型无关
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%zd\n", sizeof(arr));//(1) 求数组的长度是4*10 = 40
	printf("%zd\n", sizeof(ch));//(3)求数组长度是2*10 =20
	test1(arr);
	test2(ch);
	return 0;
}

在这里插入图片描述

5.8 强制类型转换

  1. 在C语言中的使用
	int a = 10;
	double b = (double)a;
	int a = 0x11223344;
	short b = (short)a;
	//int 4个字节(-21亿~21亿也是0~42亿9千万)   short 2个字节(0~65535 也是-32768-32768)
	printf("%x\n", b);

在这里插入图片描述
这里强制类型转换有一个从int强转为short ,会出现直接截断现象

反过来:

	short a = 0xF122;
	int b = (int)a;
	//int 4个字节(-21亿~21亿也是0~42亿9千万)   short 2个字节(0~65535 也是-32768-32768)
	printf("%x\n", b);

在这里插入图片描述
前面补的是符号位 short 0xF122-> 0000 0000 0000 0000 1111 0001 0010 0010
从short转到int 的时候 转换为 1111 1111 1111 1111 1111 0001 0010 0010

short a = 0x1122;
	int b = (int)a;
	//int 4个字节(-21亿~21亿也是0~42亿9千万)   short 2个字节(0~65535 也是-32768-32768)
	printf("%x\n", b);

在这里插入图片描述
前面补的是符号位 short 0x1122-> 0000 0000 0000 0000 0001 0001 0010 0010
从short转到int 的时候 转换为 0000 0000 0000 0000 0000 0001 0010 0010

小结:
从长的往短的强转的时候,直接截断高位,
从短的往长的强转的时候,填充的都是符号位
把short强转为int的时候,前面高位两个字节,填充的都是符号位;
把int强转为short的时候,前面高位的两个字节,就直接舍去不要了。
隐式类型转换也是类似的规则

六、关系操作符

=
< >
<=
!= 用于测试“不相等”
== 用于测试“相等”
这就是要在if条件判断的时候,注意不要少写等号。我作为初学者总是会犯这样的低级错误。要多练习!

七、 逻辑操作符

&& 逻辑与 :并且的意思
|| 逻辑或 :或者的意思

7.1 逻辑与:&&

&&操作符,左边有表达式为假,右边就不再计算,整个表达式为假

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;  //这里是&操作符,从左往右,一旦有一个表达式求出0,后面的都不会计算
	//这里a++先赋值再给a自增1,已经得到第一个表达式是0 ,后面直接不计算了 i = 0&&不计算&&不计算 
	// 所以这里a = 1,b c d 都是原来的值

	printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
	return 0;
}

在这里插入图片描述

7.2 逻辑或

||操作符,一旦左边有表达式为真,右边就不在计算,整个表达式为假

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	 i = a++ || ++b || d++;  
	 //这里a先赋值,再给a自增1 ,第二个表达式先给b自增,再给赋值,所以第二个表达式是3 为真,那么第四个表达式不再计算。
	 //a = 1 ,b = 3, c = 3  d = 4
	printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
	return 0;
}

在这里插入图片描述

八、条件操作符

exp1 ? exp2 : exp3

比较简单,举例求两个数较大值

#include <stdio.h>
int main()
{
	int a = 3, b = 4;
	printf("%d\n",a > b ? a : b);

	return 0;
}

在这里插入图片描述

九、逗号表达式

exp1, exp2, exp3, …expN

所有表达式都要运行,但是最后以最后一个表达式为准

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

在这里插入图片描述

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

10.1 [ ] 下标访问操作符

操作数:一个数组名 + 一个索引值
在我们C语言中数组和指针都可以进行[ ]操作。

  • 对数组来说[ ] 的有效下标范围 是【0,length-1】,如果下标越界,就会出现未定义行为~
  • 对于指针来说 【】范围就不好确定

10.2 ( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
在这里插入图片描述

  • 函数指针:是一个变量,里面存了个整数,是一个内存地址 ,那我们创建一个函数,是如何到内存中去的?
  • 分析一下这个过程:
  1. 编译:把.c文件变成二进制exe文件
  2. 运行:双击 exe 文件,让系统执行程序。(exe文件中包含了CPU运行时要执行的指令,以及依赖的数据,原来在.c文件中写的函数就被转换为二进制的机器指令存放到exe文件中)

10.3访问一个结构的成员

. 结构体.成员名
-> 结构体指针->成员名

#include<stdio.h>

struct Stu
{
	char name[10];
    int age;
    char sex[5];
    double score;
};

void set_age1(struct Stu stu)
{
	stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
	pStu->age = 18;//结构成员访问
}
int main()
{
	struct Stu stu;
	struct Stu* pStu = &stu;//结构成员访问

	stu.age = 20;//结构成员访问
	set_age1(stu);

	pStu->age = 20;//结构成员访问
	set_age2(pStu);
	return 0;
}

十一、 表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

11.1 隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。(缺省就是默认的意思C语言默认是int)
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

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

  1. 如何进行整形提升

整形提升是按照变量的数据类型的符号位来提升的

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111

//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

  1. 实例:
//实例1
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;
}

实例1中的a,b要进行整形提升,但是c不需要整形提升
a,b整形提升之后,变成了负数,所以表达式 a== 0xb6 , b = = 0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真
在这里插入图片描述

12.2 算术转换

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

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

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

12.3 操作符的属性

复杂表达式的求值有三个影响的因素。

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。
    两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

十二、其他知识:char类型的表达范围

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值