C语言操作符

操作符

运算操作符

+        -        *        /        %

        /:两边都是整数的时候,进行整数除法,不保留后面小数;一边有浮点数才进行浮点数除法

int a = 1 / 2;        //结果是0,两个整型相除,得到整型
double b = 1 / 2;     //结果是0,两个整型相除,得到整型
double c = 1.0 / 2;   //结果是0.5,有一个浮点型,进行浮点数除法,得到0.5

        %:求余规定两个操作数都要是整型。x%y:y不能是0,结果范围是0 ~ y-1。

移位操作符

>>        <<

        移位操作符是对补码二进制位移动的,并且操作数必须是整数,先讲一下整数的二进制的3种形式:原码、反码、补码。

原码 反码 补码

        计算机内存中存储整数是以整数的补码存储的,移位操作符实际上是对补码进行操作,打印整数是以原码的形式打印的。

正整数:

        原码:直接将整数转换成二进制,符号位(最左边)是0。

        正整数的原码、反码和补码是一样的。

负整数

        原码:直接将整数转换成二进制,符号位是1。

        反码:原码除了符号位全部二进制位取反(0变1,1变0)。

        补码:反码+1得到补码。

        补码变原码,先-1得到反码,然后除了符号位全部取反得到原码。

        如果是有符号的整数,并且只有符号位是1,会直接解析成负数。-(2 的(符号位的位数减1)次方)。

char ch = -128;
// 补码:100000000
// 符号位1是第八位,直接解析成 -(2的(8-1)次方) = -128

正整数

        11 = 2^0(右往左第一位) 2^1(右往左第二位)+ 2^3(右往左第四位)符号位是0。

        原码:00000000        00000000        00000000        00001011(隔开方便看)

        反码:00000000        00000000        00000000        00001011

        补码:00000000        00000000        00000000        00001011

负整数

        -11 = 2^0(右往左第一位) 2^1(右往左第二位)+ 2^3(右往左第四位)符号位是1。

        原码:10000000        00000000        00000000        00001011

        反码:111111111        111111111        111111111        11110100        符号位不变,原码取反

        补码:111111111        111111111        111111111        11110101        补码+1

左移操作符

        左边丢弃,右边补0。

右移操作符

        算术右移:右边丢弃,左边补符号位。右边丢弃,左边补0。

        逻辑右移:右边丢弃,左边补0。

        实现算术右移还是逻辑右移是取决于使用的编译器的。下面代码右移都是算术右移。

正数移位 

 

        移位操作符不会改变原本的值,对值移位。

        11 << 2:11的补码二进制位向左移动两位,右边补两个0。

        11 >> 1:11的补码二进制位向右移动一位,左边一个补符号位。

        11 << 1的补码:00000000        00000000        00000000        00101100

        11的补码:        00000000        00000000        00000000        00001011(方便比较)

        11 >> 1的补码:00000000        00000000        00000000        00000101

        因为符号位是0,所以是正数,正数原码反码补码都一样。

 负数移位

        -11 << 2:-11的补码二进制位向左移动两位,右边补两个0。

        -11 >> 1:-11的补码二进制位向右移动一位,左边一个补符号位。

         -11 << 2的补码:111111111        111111111        111111111        11010100

        -11的补码:         111111111        111111111        111111111        11110101

         -11 >> 1的补码:111111111        111111111        111111111        11111010

        (注:不要移动负数位,这是标准未定义的行为。) 

位操作符

&        |        ^        ~

        位操作符也是对补码的二进制位进行操作的,操作数必须是整数。

        &(按位与):两个相同位置的二进制位都为1的时候为1,只要有0就为0。

        |(按位或):两个相同位置的二进制位只要有1的时候为1,都为0才为0。

        ^(按位异或):两个相同位置的二进制位相同的时候为0,不同的时候为1。

        ~(按位取反):对二进制全部取反,包括符号位,1变0,0变1。

        10的补码:00000000        00000000        00000000        00001010

        6的补码:  00000000        00000000        00000000        00000110

        10&6:      00000000        00000000        00000000        00000010

        10|6:        00000000        00000000        00000000        00001110

        10^6:       00000000        00000000        00000000        00001100

        ~10:         11111111          11111111         11111111          11110101

赋值操作符

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

        +=:例如 a += 1; a先+1再将得到的值赋值给a。

a = a + 1;
a += 1;

        这两个效果是一样的。其它复合赋值操作符也是一样的。

单目操作符

!        -        +        sizeof        --        ++        &         *        (类型)

        !:条件取反。真变假,假变真。 C语言中0表示假,非0表示真。

int a = 1;
if (!a)
{
	printf("%d\n", a);
}

        1为真,!取反,条件不成立,不执行打印。

int a = 0;
if (!a)
{
	printf("%d\n", a);
}

        0为假,!取反,条件成立,执行打印,输出0。 

        布尔类型只有两个值:true和false,对应的是真和假,通常用来作为判断条件,需要包含头文件 stdbool.h 才能使用。 

bool flag1 = true;
bool flag2 = false;

        布尔类型的值判断条件更加直观,并且布尔类型可以作为函数的返回值。

        sizeof:计算数据大小,在

int a = 1;
// 可以通过变量名或者类型计算大小
sizeof(a);
sizeof(int);

// 对变量名计算大小,可以不用()
sizeof a;

        sizeof里面的表达式不会进行计算。

int a = 1;
sizeof(a++);
printf("%d\n", a); // 打印的结果是1

         sizeof计算数组大小,数组名本质上是指针,但是在sizeof中计算的是整个数组的大小。

char arr1[5];
int arr2[10];
sizeof(arr1); // 大小为5
sizeof(arr2); // 大小为40

         sizeof计算形参数组,本质上计算的是指针大小,数组名作为实参,并不会将整个数组传过去,传的是首元素的地址,形参得到的是地址。

        --:让变量减1,分为前置--和后置--。

int a = 5;
int b = a--;    // 先将a的值(5)值赋值给b,a再减1,a变为4
int c = --a;    // a先减1,a变为3,再将a的值(3)赋值给c

        ++:让变量加1,也分为前置++和后置++。

        &:取地址操作符。取变量的地址,需要存储该变量的地址需要对应的类型指针。

        *:解引用操作符。对指针解引用,找到指针指向的对象。

// 取字符变量地址
char ch = 'a'
char* pch = &ch; // *表示pch是指针,指向的对象是char类型的
*pch = 'b'; // *pch找到变量ch,再将b赋值给ch

// 取整型变量地址
int a = 1;
int* pa = &a; // *表示pa是指针,指向的对象是int类型的
*pa = 2;     // *pa找到变量a,再将2赋值给a

// 取常量字符串地址
const char* pch1 = "abc"; // const修饰的具有常属性,无法通过*解引用修改值

         (类型):强制类型转换,当类型不匹配的时候可以使用强制类型转换。(注:并不会改变原本变量的类型,生成一个临时变量赋值给另一变量)

double d = 1.1;
int a = (int)d; // d还是double类型的

关系操作符

>         >=        <         <=        !=        ==

        比较的是内置类型的数据,字符串不能使用这些关系操作符比较,字符串本质上比较的是指针,而不是指针指向的内容。

        不能像数学一样连续判断,这种是错误的

if (10 <= 20 <= 30)

        需要使用逻辑操作符

if (10 <= 20 && 20 <= 30)

逻辑操作符

&&       ||

        &&:多个条件判断,全部条件都为真,整个判断条件就为真,有一个条件为假就为假。(依次判断,遇到假,后面不再判断)

#include <stdio.h>

int main()
{
	int a = 0;
	int b = 3;
	if (a++ && b++)
	{
		;// 空语句
	}
	printf("a = %d\n", a); // a是1
	printf("b = %d\n", b); // b是3
	return 0;
}

        后置++,先使用,再++,作为判断条件0为假,后面不再判断,整个条件不成立,a再++变为1。

        ||:多个条件判断,只要有条件为真,整个判断条件就为真,全部条件为假就为假。(依次判断,遇到真,后面不再判断)

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 3;
	if (a-- || b++)
	{
		;// 空语句
	}
	printf("a = %d\n", a); // a是1
	printf("b = %d\n", b); // b是3
	return 0;
}

        后置--,先使用,再--,作为判断条件1为真,后面不再判断,整个条件成立,a再--变为0。

条件操作符

判断条件 ? 执行语句1 : 执行语句2

int a = 5 > 3 ? 5+3 : 5-3;

        条件成立执行前面的,条件不成立执行后面的。

逗号表达式

表达式1, 表达式2, ..., 表达式N

        从左往右依次执行,有()的情况下,()里面的所有表达式是一个逗号表达式,整个表达式的结果为()内最后的一个表达式的值。

int a = 3;
int b = 0;
b = (a + 2, a--, a + 5);

         a+2,不改变a的值;a--,a变为2;最后把a+5的值(7)赋值给a。

        从左往右依次执行,没有()的情况,所有表达式是一个逗号表达式。

int a = 3;
int b = 0;
b = a + 2, a--, a + 5;

        从左往右依次执行,b=a+2是第一个表达式,将a+2的值(5)赋值给b;a--,a变为2,a+5,不改变a的值。

数组下标、函数调用、结构体成员访问操作符

[]        ()        .        ->

        []:访问数组元素。 

int arr[5] = {0};
arr[2] = 2; // 将下标为2的元素(第三个元素)赋值为2

        ():函数调用,函数名和()要连在一起,中间不能有空格。

int add(int a, int b)
{
    return a + b;
}

int main()
{
    int sum = add(3, 5);
    
    return 0;
}

        .:结构体变量访问成员操作符

        ->:结构体指针变量访问成员操作符。

隐式类型转换

整型提升

        C的整型算数运算总是至少以缺省整型类型的精度来进行,为了获得这个精度,表达式中的字符和短整形操作数在使用之前都被转换成普通整型,这种转换为整型提升。char和short在使用的时候,都会先提升为int类型的数据,将数据补充够32位。

        有符号的char和short,发生整型提升的时候,会按照符号位提升,如果符号位是0,全部补0,如果符号位是1,全部补1。无符号的直接补0。

        直接写的short和int默认是有符号的,而char是标准未定义的,大部分编译器都是有符号的char。

正数

char ch = 1;
// 1的补码: 00000000  00000000  00000000  00000001
// ch是char类型是的,一个字节,八个比特位,将1的补码后面八个存储在ch
// ch的补码:00000001

printf("%d\n", ch);
// ch隐式类型转换:整形提升,补符号位0,在打印数据。
// 00000000  00000000  00000000  00000001

负数

char ch = -1;
// 1的补码: 11111111  11111111  11111111  11111111
// ch是char类型是的,一个字节,八个比特位,将1的补码后面八个存储在ch
// ch的补码:11111111

printf("%d\n", ch);
// ch隐式类型转换:整形提升,补符号位0,在打印数据。
// 11111111  11111111  11111111  11111111

无符号

unsigned char ch = -1;
// 1的补码: 11111111  11111111  11111111  11111111
// ch是char类型是的,一个字节,八个比特位,将1的补码后面八个存储在ch
// ch的补码:11111111

printf("%d\n", ch);
// ch隐式类型转换:整形提升,直接补0,在打印数据。
// 00000000  00000000  00000000  11111111

验证

        a整型提升为0xffffffff。b整型提升为0xffffff00。c是整型不用整型提升。

算术转换

        如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作无法进行。

        自下而上的类型转换:int->unsigned int->long->unsigned long->float->double

操作符的属性

优先级

        相邻操作符才比较优先级。

int a = 1 + 2 * 3;
// 先算*,再算+

结合性

        相邻操作符的一样。

int a = 1 + 2 + 3;
// 从右往左+

求值顺序

        操作符控制求值顺序,例如:||(逻辑与),多个条件的情况下,第一个条件符合不会进行第二个条件判断。

        有优先级、结合性和求值顺序,有时候并不能有效的求值,如果没有唯一路径计算,该表达式的求值错误。

int a = 1;
int b = a + a++;

        先算++,再算+。a++先使用后++,但是第一个a不知道是什么时候准备好的,可能会受到a++的影响,所以不清楚是1还是2,该表达式的情况可能有两种1 + 1 或 2 + 1。 不同环境下的求值顺序可能并不一样。

练习

1.一组数据中,有一个数只出现一次,另外的数都是出现两次,请找出这个出现一次的数。 

        提示:0^a = a。        a^a = 0。

2.求一个整数补码的二进制位中1的个数

练习答案

1.

#include <stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,4,3,2,1 };
    // 计算数组大小
	int size = sizeof(arr) / sizeof(arr[0]);

	int n = 0;
    // 遍历数组进行异或
	for (int i = 0; i < size; ++i)
	{
		n = n ^ arr[i];
	}

	printf("只出现一次的数是:%d\n", n);
	return 0;
}

2. 

#include <stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	for (int i = 0; i < 32; ++i)
	{
		if ((n >> i) & 1 == 1)
			++count;
	}

	printf("%d的二进制位中1的个数是:%d\n", n, count);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值