C语言初级---操作符详解

        

目录

1. 操作符分类:

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

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


2. 算术操作符
+     -   *   /   %
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数
int main()
{
	double m = 3.0 / 2;//如果/两边全为整型,则进行的是整型的计算,只保留整数部分
	printf("%lf\n", m);
	int n = 3 % 2;//%的两边必须为整型,范围0~1
	printf("%d\n", n);
	return 0;
}
3. 移位操作符(操作的对象是二进制)
<< 左移操作符
>> 右移操作符
   
注:移位操作符的操作数只能是整数.
这里先得了解一下二进制:整数在计算机中二进制有原码,反码,补码三种,其中整数是以补码储存在计算机中,在计算机中操作的也是补码,打印则是以原码的形式来打印。原码:就是该整数对应的二进制数。反码:32位二进制中最高位作为符号位不变,其他位取反。补码:反码+1。
其中, 正数的原码=反码=补码 ,而 负数的反码与补码是要计算的
C语言初级---操作符详解_第1张图片

3.1 左移操作符
移位规则:
左边抛弃、右边补 0
正数<<
int main()
{
	int a = 5;
	a <<= 1;
	printf("%d", a);//a=10 左移产生*2的效果
	return 0;
}

C语言初级---操作符详解_第2张图片

 负数<<

int main()
{
	int a = -5;
	a <<= 1;
	printf("%d", a);//a=-10 左移产生*2的效果
	return 0;
}

C语言初级---操作符详解_第3张图片

3.2 右移操作符
>> 右移操作符
移位规则:
首先右移运算分两种:
1. 逻辑移位
左边用 0 填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃
C语言初级---操作符详解_第4张图片

正数右移:
int main()
{
	int a = 5;
	int b = a >> 1;
	printf("%d", b);//2
	return 0;
}

C语言初级---操作符详解_第5张图片

 负数右移:

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

C语言初级---操作符详解_第6张图片

警告
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10 ;
num >>- 1 ; //error

4. 位操作符
位操作符有:
& // 按位与
| // 按位或
^ // 按位异或
注:他们的操作数必须是整数。
C语言初级---操作符详解_第7张图片
a&b:
int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	printf("%d", c);//3
	return 0;
}

 C语言初级---操作符详解_第8张图片

 a|b:

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

C语言初级---操作符详解_第9张图片

 a^b:

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

C语言初级---操作符详解_第10张图片

 异或带来的性质:

a^a=0,每个二进制位都相同,所有二进制位异或的结果都为0。

a^0=a,因为二进制位相异为1,所以二进制位异或的结果还是为a。

例题:有一数组,假设不知道该数组的大小,但是除了只有1个数字只出现一次外,其他的数字都出现了两次,求这个只出现一次的数字,假设该数组为{1 2 3 4 5 1 2 3 4 }。

方法:将相同的两个数异或,得到结果0,再将所有的0与这个单独出现的数字异或就可以求出这个单独出现的数字。

练习

两个变量的交换

1.引入中间变量tmp:

int main()
{
	int a = 10;
	int b = 20;
	int tmp = a;//引入第三个参数,来实现两个变量的交换
	a = b;
	b = tmp;
	printf("%d %d", a, b);
	return 0;
}

2.利用两个变量的和:

int main()
{
	int a = 10;
	int b = 20;
	a = a + b;
	b = a - b;//b=a
	a = a - b;//b=a,a=a+b-a => a=b
	printf("%d %d", a, b);
	return 0;
}

3.异或:

int main()
{
    int a = 10;
    int b = 20;
    a = a ^ b;
    b = a ^ b;//(a^b)^b=a^0=a
    a = a ^ b;//a^b^a=b^0=b
    printf("%d %d", a, b);
    return 0;
}

第二种与第三种都没有在创建新的变量。但是第一种是可读性最高的。

练习:
编写代码实现:求一个整数存储在内存中的二进制中 1 的个数。
C语言初级---操作符详解_第11张图片

 代码:

int main()
{
	int a = 5;
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		int b = a >> i;//依次右移i位
		if (b & 1 == 1)
		{
			count++;//统计1的个数
		}
	}
	printf("%d", count);
	return 0;
}
5. 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋 值。
int weight = 120 ; // 体重
weight = 89 ; // 不满意就赋值
double salary = 10000.0 ;
salary = 20000.0 ; // 使用赋值操作符赋值。
赋值操作符可以连续使用,比如:
int a = 10 ;
int x = 0 ;
int y = 20 ;
a = x = y + 1 ; // 连续赋值
这样的代码感觉怎么样?
C语言初级---操作符详解_第12张图片
那同样的语义,你看看:
x = y + 1 ;
a = x ;
这样的写法是不是更加清晰爽朗而且易于调试。

复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。
比如:
int x = 10 ;
x = x + 10 ;
x += 10 ; // 复合赋值
// 其他运算符一样的道理。这样写更加简洁。
6. 单目操作符
6.1 单目操作符介绍
!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof       操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--           前置、后置 --
++           前置、后置 ++
*           间接访问操作符 ( 解引用操作符 )
( 类型 )       强制类型转换
!           逻辑反操作
C语言中0为假,非0为真。
int main()
{
	int flag = 0;
	if(!flag)//当判断是假的时候执行
	return 0;
}
int main()
{
	int flag = 1;
	if(flag)//当判断是真的时候执行
	return 0;
}

C语言中c99之前没有表示真假的类型,在c99中引用了布尔类型。

#include
int main()
{
	_Bool flag1 = false;
	bool flag2 = true;
	if (flag1)
	{
		printf("hehe");
	}
	return 0;
}

C语言初级---操作符详解_第13张图片

-           负值
+           正值
-会影响变量的值,但是+不会。
&           取地址 :取出内存中的地址
*           间接访问操作符 ( 解引用操作符 )
struct s
{
	char name[20];
	int age;

};
int main()
{
	int a = 10;
	int* pa=&a;
         *pa;//解引用操作符/间接访问操作符 *&a==a
	int arr[10] = { 0 };
	&arr;//取出数组的地址,数组的地址应该放到【数组指针】当中
	struct s s1 = { 0 };//初始化一个结构体
	struct s* ps=&s1;//结构体指针
           *struct s;
	return 0;
}

sizeof       操作数的类型长度(以字节为单位)
int main()
{
	int c = 10;
	printf("%d\n", sizeof(c));//4
	printf("%d\n", sizeof c);//4 sizeof 不是函数而是一个单目操作符
	printf("%d\n", sizeof(int));//4

	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));//40 这里函数名表示整个数组,计算类型创建的变量所占内存的大小,单位是字节

	int  a = 10;
	short b = 0;
	printf("%d\n", sizeof(b = a + 2));//2
	printf("%d\n", b);//0 sizeof中的表达式不参与计算
	return 0;
}

C语言初级---操作符详解_第14张图片

 sizeof 和 数组

#include 
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)  4/8
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)  4/8  指针的大小是4/8
}
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 )、( 2 )两个地方分别输出多少?
3 )、( 4 )两个地方分别输出多少?
~           对一个数的二进制按位取反后置
int main()
{
	int a = 0;
	printf("%d", ~a);//-1
	return 0;
}

C语言初级---操作符详解_第15张图片

int main()
{
	//int a = 0;
	//printf("%d", ~a);//-1
	int a = 11;
	//000000000000000000001011 a的补码 a =11
	//000000000000000000000100 按位或上1<<2,将a的第三个二进制位变为1
	a |= (1 << 2);
	printf("%d\n", a);//15
	//如何将第三位二进制位又改为1
	//000000000000000000001111 a的补码 a=15
	//111111111111111111111011 按位&上整个二进制的补码,则第三位二进制位可变为0
	//000000000000000000000100 这个二进制补码取反就可以得到我们要按位&上的二进制补码
	a &= (~(1 << 2));
	printf("%d\n", a);//11
	return 0;
}

C语言初级---操作符详解_第16张图片

--           前置、后置 --
++           前置、后置 ++
  前置++:
int main()
{
	int a = 3;
	int b = ++a;//++表示自增,前置表示先自增,后使用
	printf("%d %d\n", b,a);//4 4
	printf("%d\n", ++a);//5 前置++ 先自增在打印4+1=5
	return 0;
}

后置++:

int main()
{
	int a = 3;
	int b = a++;//++表示自增,后置表示先使用,后自增
	printf("%d %d\n", b,a);//3 4
	printf("%d\n", a++);//4 后置++ 先打印4 再自增4+1=5
	return 0;
}

 --与++一样。

(类型)       强制类型转换

int main()
{
	int a = (int)3.14;//强制类型转换,将double类型转换为int类型,只保留整数部分3
	printf("%d\n", a);//3
	return 0;
}

7. 关系操作符
关系操作符
>
>=
<
<=
!=   用于测试 不相等
==       用于测试 相等
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
警告:
在编程的过程中 == = 不小心写错,导致的错误。
字符串比较大小
C语言初级---操作符详解_第17张图片

8. 逻辑操作符
逻辑操作符有哪些:
&&     逻辑与
||           逻辑或
区分 逻辑与 按位与
区分 逻辑或 按位或
1 & 2 -----> 0 按位与
1 && 2 ----> 1 逻辑与 判断真假
1 | 2 -----> 3 按位或
1 || 2 ----> 1 逻辑或 判断真假、
360面试题:
#include 
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;//a=0,逻辑与1假全假,后面的全部不会计算量了,++后置,先使用后自增
	//i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);// a=1,b=2,c=3,d=4
	return 0;
}
//程序输出的结果是什么?
#include 
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;//没有=0的变量,所以会计算问完++后置,先使用后自增
	/*i = a++||++b||d++;*/
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);// a=2,b=3,c=3,d=5
	return 0;
}
//程序输出的结果是什么?
#include 
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
    //i = a++ && ++b && d++;//没有=0的变量,所以会计算问完++后置,先使用后自增
	i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);// a=2,b=2,c=3,d=4
	return 0;
}
//程序输出的结果是什么?
#include 
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
    //i = a++ && ++b && d++;//没有=0的变量,所以会计算问完++后置,先使用后自增
	i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);// a=1,b=3,c=3,d=4
	return 0;
}
//程序输出的结果是什么?
9. 条件操作符
exp1 ? exp2 : exp3 、
1.
if ( a > 5 )
        b = 3 ;
else
        b = - 3 ;
转换成条件表达式,是什么样?
2. 使用条件表达式实现找两个数中较大值
int main()
{
	int a = 3;
	printf("%d\n", a > 0 ? 3 : -3);//3
	int b = 0;
	int m = a > b ? a : b;
	printf("%d\n", m);//3
	return 0;
}
10. 逗号表达式
exp1 , exp2 , exp3 , …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的 结果是最后一个表达式的结果。
int main()
{
	//代码1
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	//         0     a=2+10=12  a=12 b=12+1=13
	printf("%d", c);//13
	//c是多少?
}
//代码2
if (a =b + 1, c=a / 2, d > 0)

if判断的是最后一个语句d>0。

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


如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0) 
{
         //业务处理
}

11. 下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
int arr [ 10 ]; // 创建数组
arr [ 9 ] = 10 ; // 实用下标引用操作符。
[ ] 的两个操作数是 arr 9
int main()
{
	int arr[10] = { 0 };
	arr[4] = 5;//[] 就是下标引用操作符 arr 4是它的两个操作数
	return 0;
}

C语言初级---操作符详解_第18张图片

 []只是一个操作符而已。

2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
add(int x, int y)
{
	return x + y;
}
int main()
{
	int x = 10;
	int y = 20;
	int ret = add(x, y);//add x,y 是函数调用符的两个操作数
	return 0;
}

函数操作符至少有一个操作数:函数名。

3. 访问一个结构的成员
. 结构体 . 成员名
-> 结构体指针 -> 成员名
struct Stu
{
	char name[20];
	int age;
	float score;
};

void print(struct Stu s)
{
	printf("%s %d %f\n", s.name,s.age,s.score );//结构体.成员名
}
int main()
{
	struct Stu s1 = { "张三",18,100.5f };
	print(s1);
	return 0;
}
struct Stu
{
	char name[20];
	int age;
	float score;
};

void print(struct Stu *ps)
{
	printf("%s %d %f\n", (*ps).name,(*ps).age,(*ps).score );//结构体.成员名
}
int main()
{
	struct Stu s1 = { "张三",18,100.5f };
	print(&s1);
	
	return 0;
}
struct Stu
{
	char name[20];
	int age;
	float score;
};

void print(struct Stu* ps)
{
	printf("%s %d %f\n", ps->name, ps->age, ps->score);//-> 结构体指针->成员名
}
int main()
{
	struct Stu s1 = { "张三",18,100.5f };
	print(&s1);
	
	return 0;
}
#include
struct Stu
{
	char name[20];
	int age;
	float score;
};

void print(struct Stu* ps)
{
	printf("%s %d %f\n", ps->name, ps->age, ps->score);//-> 结构体指针->成员名
}
int main()
{
	struct Stu s1 = { "张三",18,100.5f };
	strcpy(s1.name, "张三丰");//改变结构体里的字符串数组,用strcpy
	//scanf("%s", s1.name);//s1.name是数组的首地址,将字符串放入这个地址所在的内存中
	print(&s1);
	
	return 0;
}
12. 表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
12.1 隐式类型转换
C 的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为 整型 提升
整型提升的意义
表达式的整型运算要在 CPU 的相应运算器件内执行, CPU 内整型运算器 (ALU) 的操作数的字节长度
一般就是 int 的字节长度,同时也是 CPU 的通用寄存器的长度。
因此,即使两个 char 类型的相加,在 CPU 执行时实际上也要先转换为 CPU 内整型操作数的标准长
度。
通用 CPU general-purpose CPU )是难以直接实现两个 8 比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转
换为 int unsigned int ,然后才能送入 CPU 去执行运算。

你可能感兴趣的:(C语言初级,c语言,开发语言,后端)