C-操作符

目录

操作符

 算术操作符

移位操作符

右移位操作符

算术右移

逻辑右移

 左移位操作符 

 PS:对于移位操作符,不要移动负数位,这个标准是未定义的

PS:移位及位操作符只能作用于整数,不能作用于浮点数 

位操作符

&——按位与

|——按位或

 ^——按位异或

应用:

一 

 二

赋值操作符

 复合赋值符

 单目操作符

!         逻辑反操作

-                负值

+               正值

&               取地址

sizeof        操作数的类型长度

~               对一个数的二进制按位取反

--               前置、后置--

++             前置、后置++

*                间接访问操作符(解引用操作符

(类型)        强制类型转换

逻辑操作符

&&        逻辑与

||           逻辑或

条件操作符(三目操作符

逗号表达式 

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

下标引用操作符

函数调用操作符

结构成员

表达式求值

隐式类型转换

整型提升

算术转换

操作符的属性


操作符

分类 

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

下标引用

函数调用

结构成员

 算术操作符

+        -        *        /        %

加      减     乘      除     取模

A%B  A除以B取余数,取模时A与B都要为整数

A/B  A除以B,当两边全为整数时,结果也为整数,当A与B任意一方为浮点数时(2.0或2.1···),结果可算出小数

移位操作符

<<左移位操作符

>>右移位操作符

移动的是二进制位

右移位操作符

算术右移

        需要知道符号位(正0负1)

        右边丢弃,左边补原符号位

#include <stdio.h>
int main()
{
	int a = -1;
	int b = a >> 1;
	//整数二进制表示有:原码、反码、补码
	//存储到内存中的是补码
	//正数:原码=反码=补码
	//10000000000000000000000000000001——-1原码
	//11111111111111111111111111111110——-1反码(符号位不变,其他位按位取反
	//11111111111111111111111111111111——-1补码(反码+1
	//11111111111111111111111111111111——右移位后
	printf("%d\n", b);
	return 0;
}
#include <stdio.h>
int main()
{
	int a = 16;
	int b = a >> 1;
    //    16:00000000000000000000000000010000
    //右移后:00000000000000000000000000001000
	printf("%d\n", b);
	return 0;
}

逻辑右移

        右边丢弃,左边补0

 左移位操作符 

左边舍弃,右边补0

#include <stdio.h>
int main()
{
	int a = 5;
	//00000000000000000000000000000101——5
	int b = a << 1;
	//00000000000000000000000000001010——移位后
	printf("%d\n", b);
	return 0;
}

 PS:对于移位操作符,不要移动负数位,这个标准是未定义的

int a = 1;
int b = a >> -1;

PS:移位及位操作符只能作用于整数,不能作用于浮点数 

位操作符

&        按位与

|         按位或

^        按位异或

&——按位与

是按二进制位与

相同位只要有一个为0则为0,两个为1才为1(补码进行运算

#include <stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = 5;
	//00000000000000000000000000000101
	int c = a & b;
	//00000000000000000000000000000001
	printf("%d\n", c);
	return 0;
}

|——按位或

相同位只要有一个为1则为1,两个为0才为0(补码进行运算

#include <stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = 5;
	//00000000000000000000000000000101
	int c = a | b;
	//00000000000000000000000000000111
	printf("%d\n", c);
	return 0;
}

 ^——按位异或

 相同位相同为0,相异为1(补码进行运算

#include <stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = 5;
	//00000000000000000000000000000101
	int c = a ^ b;
	//00000000000000000000000000000110
	printf("%d\n", c);
	return 0;
}

应用:

一 

在不创建第三个变量的情况下交换两个值,a=3,b=5,交换后:a=5,b=3 

        加减法:

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

                缺陷:数据可能会溢出

        异或:

#include <stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = 5;
	//00000000000000000000000000000101
	a = a ^ b;
	//00000000000000000000000000000110
	b = a ^ b;
	//00000000000000000000000000000011
	a = a ^ b;
	//00000000000000000000000000000101
	printf("a=%d,b=%d\n", a, b);
	return 0;
}

                缺陷:执行效率不高,可读性差

 二

编写一个代码:求一个整数存储在内存中的二进制中1的个数

#include <stdio.h>
int main()
{
	int num = 0;
	scanf_s("%d", &num);
	//此处用scanf亦可
	int count = 0;
	while (num)
	{
		if (num % 2 == 1)
			count++;
		num = num / 2;
	}
	printf("%d\n", count);
	return 0;
}

        缺陷:无法计算负数,负数存储时是补码,而代码计算的是原码

#include <stdio.h>
int main()
{
	int num = 0;
	scanf_s("%d", &num);
	//此处用scanf亦可
	int count = 0;
	int i = 0;
	for ( i = 0; i < 32; i++)
	{
		if (((num >> i) & 1) == 1)
			count++;
	}
	printf("%d\n", count);
	return 0;
}
#include <stdio.h>
int main()
{
	int num = 0;
	scanf_s("%d", &num);
	//此处用scanf亦可
	int count = 0;
	int i = 0;
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("%d\n", count);
	return 0;
}

赋值操作符

可将自己重新赋值

int a = 10; //不满意
a = 20;     //重新赋值

int a = 10; //不满意
int b = 20;
a = a + b;  //重新赋值

可连续赋值

int x = 1;
int y = 2;
int a = 3;
a = x = y + 1; //不易理解

x = y + 1;
a = x;    //更易理解

 复合赋值符

+=

-=

*=

/=

%=

>>=

<<=

&=

|=

^= 

a = a + 2;
a += 2;  //等价

其余复合赋值符用法类似

 单目操作符

 只有一个操作数

a+b有两个操作数,则此时+为双目操作符

!         逻辑反操作

        真变假,假变真(假变为1

#include <stdio.h>
int main()
{
	int a = 10;
    if (a)
	{
		printf("hello,world\n"); //a为真打印
	}
	if (!a)
	{
		printf("hello,world\n"); //a为假打印
	}
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

-                负值

-------------------------------------------------------------------------------------------------------------------------

+               正值

-------------------------------------------------------------------------------------------------------------------------

&               取地址

        一般与指针一起用

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	*p = 20;//* 解引用操作符,此时a变为20
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

sizeof        操作数的类型长度

        打印 sizeof 时最好用 %zd (%d也可以

#include <stdio.h>
int main()
{
	int a = 10;
	char c = 'r';
	char* p = &c;
	int arr[10] = { 0 };
	//sizeof 计算变量所占内存空间的大小,单位是字节
	printf("%zd\n", sizeof(a)); //a 两边()可省略
	printf("%zd\n", sizeof(int)); //int 两边()不可省略

    printf("%zd\n", sizeof(c));
    printf("%zd\n", sizeof(char));

	printf("%zd\n", sizeof(p));
    printf("%zd\n", sizeof(char*));

	printf("%zd\n", sizeof(arr));
    printf("%zd\n", sizeof(int [10]));
    //用 %d 也可以
	return 0;
}
#include <stdio.h>
int main()
{
	short s = 0;
	int a = 10;
	printf("%d\n", sizeof(s = a + 5)); //打印2 0
	printf("%d\n", s);    //不管a是什么类型,只管s的类型,s只有两个字节,即为2
	return 0;             //sizeof里的表达式不会真正去计算,s的值不会发生变化
}
#include <stdio.h>
void test1(int arr[])
{
	printf("%zd\n", sizeof(arr));
}
void test2(char ch[])
{
	printf("%zd\n", sizeof(ch));
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%zd\n", sizeof(arr));
	printf("%zd\n", sizeof(ch));
	test1(arr); //传过去的是地址,是指针,打印4(32位)或者8(64位)
	test2(ch);
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

~               对一个数的二进制按位取反

#include <stdio.h>
int main()
{
	int a = 0;
	//00000000000000000000000000000000
	printf("%d\n", ~a);
	//11111111111111111111111111111111——补码(-1
	//11111111111111111111111111111110——反码
	//10000000000000000000000000000001——原码
	return 0;
}
#include <stdio.h>
int main()
{
	int a = 11;
	//00000000000000000000000000001011 只将1011中的0变为1
	a = a | (1 << 2);
	//00000000000000000000000000000100——1 << 2
	//00000000000000000000000000001111——a | (1 << 2)
	printf("%d\n", a);
	//再将1111还原回去
	int b = a;
	b = b & (~(1 << 2));
	//11111111111111111111111111111011——~(1 << 2)
	//00000000000000000000000000001011——b & (~(1 << 2))
	printf("%d\n", b);
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

--               前置、后置--

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 10;
	printf("%d\n", --a); //前置--,先--,再使用
	printf("%d\n", b--); //后置--,先使用,再--
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

++             前置、后置++

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 10;
	printf("%d\n", ++a); //前置++,先++,再使用
	printf("%d\n", b++); //后置++,先使用,再++
	return 0;
}

-------------------------------------------------------------------------------------------------------------------------

*                间接访问操作符(解引用操作符

-------------------------------------------------------------------------------------------------------------------------

(类型)        强制类型转换

#include <stdio.h>
int main()
{
	int a = (int)3.14; //将浮点数强制转换为int类型
	return 0;
}

逻辑操作符

&&        逻辑与

 两个数全为真时才为真,一个为假即为假

#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	int c = a && b;
	printf("%d\n", c); //均为真,则为真,c为1
	return 0;
}
#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++; //结果是1 2 3 4,a++,先使用后++,使用时仍未0,则后面的++b和d++不运算
	printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
	return 0;
}

||           逻辑或

两个数全为假才为假,一个为真即为真

#include <stdio.h>
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	i = a++ || ++b || d++; //结果是1 3 3 4,a++,使用时仍为0,++b使用是为3(真),则d++不计算
	printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
	return 0;
}

条件操作符(三目操作符

exp1 ? exp2 : exp3

表达式1的结果为真,表达式2计算,表达式2的结果是整个表达式的结果

表达式1的结果为假,表达式3计算,表达式3的结果是整个表达式的结果

#include <stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	b = (a > 5 ? 3 : -3);
	printf("%d\n", b);
	return 0;
}
#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int max = 0;
	max = (a > b ? a : b); //a与b比大小
	printf("%d\n", max);
	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 + b);
	printf("%d\n", c); //逗号表达式中前面式子成立与否不影响后续表达式的进行
	return 0;
}

 逗号表达式不易理解,可读性不高

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

下标引用操作符

[ ]  操作数为数列名及下标

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	arr[9] = 10; //arr和9为[ ]的操作数
	return 0;
}

函数调用操作符

( )  操作数可有多个,必有函数名

#include <stdio.h>
int get_max(int x, int y)
{
	return x > y ? x : y;
}
int main()
{
	int a = 10;
	int b = 20;
	int max = get_max(a, b); //调用get_max函数
	return 0;  //函数名(get_max)和a、b都是函数调用操作符的操作数
}

结构成员

.    结构体            .成员名

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

#include <stdio.h>
struct Movie //创建一个结构体类型-struct Movie
{
	//成员变量
	char name[50];
	int time;
	char id[20];
};
int main()
{
	//使用struct Movie这个类型创建了一个电影对象s1
	struct Movie s1 = { "Josee, the Tiger and the Fish",98,"20210820" };
	printf("%s\n", s1.name);
	printf("%d\n", s1.time);
	printf("%s\n", s1.id);
	//结构体变量 .成员名
	return 0;
}
#include <stdio.h>
struct Movie //创建一个结构体类型-struct Movie
{
	//成员变量
	char name[50];
	int time;
	char id[20];
};
int main()
{
	//使用struct Movie这个类型创建了一个电影对象s1
	struct Movie s1 = { "Josee, the Tiger and the Fish",98,"20210820" };
	struct Movie* ps = &s1;
	printf("%s\n", (*ps).name);
	printf("%d\n", (*ps).time);
	printf("%s\n", (*ps).id);
	return 0;
}
#include <stdio.h>
struct Movie //创建一个结构体类型-struct Movie
{
	//成员变量
	char name[50];
	int time;
	char id[20];
};
int main()
{
	//使用struct Movie这个类型创建了一个电影对象s1
	struct Movie s1 = { "Josee, the Tiger and the Fish",98,"20210820" };
	struct Movie* ps = &s1;
	printf("%s\n", ps->name);
	printf("%d\n", ps->time);
	printf("%s\n", ps->id);
	//结构体指针->结构名
	return 0;
}

表达式求值

表达式求值顺序是由操作符的优先级和结合性决定的

有些操作符在求值过程中可能会进行类型转换

隐式类型转换

整型提升

C的整形算术运算总是至少以缺省整型类型的精度来进行的

为获得这个精度,表达式中的字符和短整型操作数在使用前被转换为普通整型

char a,b,c;
……
a= b + c;
//b与c的值被提升为普通整型,然后才会执行加法运算
//加法运算完成后,结果将被截断,然后再存储于a中

整型提升方式:

        整型提升按照变量的数据类型的符号为来提升的

        无符号数直接补0

#include <stdio.h>
int main()
{
	char a = 3;
	//00000000000000000000000000000011——3的二进制
	//00000011——3,发生截断(挑最低字节的内容)
	char b = 127;
	//00000000000000000000000001111111——127的二进制
	//01111111——127
	//符号位为什么,整型提升时补的就是什么
	//a提升:00000000000000000000000000000011
	//b提升:00000000000000000000000001111111
	//a+b后:00000000000000000000000010000010
	char c = a + b;
	//c是char类型,只存8个bit位,结果发生截断
	//100000010——c
	//打印发生整型提升并取原码:
	//111111111111111111111111100000010——补码
	//111111111111111111111111100000001——反码
	//100000000000000000000000011111110——原码
	printf("%d\n", c); //结果是-126
	return 0;
}

例1:

#include <stdio.h>
int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000;
	if (a == 0xb6)
	//a是char类型,发生整型提升
		printf("a");
	//不打印
	if (b == 0xb600)
	//b是short类型,也发生整型提升
		printf("b");
	//不打印
	if (c = 0xb6000)
	//c是int类型,不发生整型提升
		printf("c");
	//打印
	return 0;
}

例2:

#include <stdio.h>
int main()
{
	char a = 1;
	printf("%u\n", sizeof(a));  //1
	printf("%u\n", sizeof(+a)); //4 +a发生整型提升
	printf("%u\n", sizeof(!a)); //1
	//%u是打印十进制无符号整数
	return 0;
}

算术转换

如果某个操作符的各个操作数属于不同类型,就会进行算术转换,转为一个类型

算术转换优先度:

long double

double

float

unsigned long int

long int

unsigned int

int

不同类型进行计算时会将下面的类型转换为上面的类型

操作符的属性

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

1.操作符的优先级

2.操作符的结合性(优先级相同时在考虑结合性

3.是否控制求值的顺序

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = b + a * 3;
	//*的优先级比+更高,先计算*再计算+
	return 0;
}
#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = b + a + 3;
	//当优先级相同时,考虑结合性
	//+的结合性为从左到右
	//即先算左再算右
	return 0;
}

能控制求值顺序的操作符如:&&(左边为假就不算右)  ||(左边为真就不算右)  ,(逗号,决定结果的是最后一个表达式)……

A * B + C * D + E * F;
//*优先级比+高,只能保证相邻时*先计算,但第三个*不一定比第一个+先计算
int C = 1;
int A = C + --C;
//--比+的优先级高,但有歧义
//先准备第一个C,结果为1
//先计算--再准备第一个C,结果为0
//无法得知+操作符的左操作数的获取在右操作数之前还是之后求值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值