【零基础C语言】基础操作符详解

操作符的分类:


算术操作符: +    -    *    /    %
移位操作符 :  <<  >> 
位操作符 :  &  |  ^ 
赋值操作符 :  =   +=   -=   *=   /=    %=    <<=    >>=    &=    |=    ^= 
单⽬操作符: !  ++  --   &    *    +     -    ~  sizeof  (类型)
关系操作符 :  >    >=    <    <=     ==     != 
逻辑操作符: &&    ||
条件操作符: ?  :
逗号表达式:,
下标引⽤:[]
函数调⽤:()
结构成员访问:.  ->

二进制和进制转换

int main()
{
    十进制 0~9
    二进制 0~1
    八进制 0~7
    十六进制 0~15

    /*十进制 ——> 二进制*/ //不停地 %2 取余,余数的颠倒顺序即是二进制
    十进制:15
    二进制:1111
    printf("%d\n", 1111);
    /*二进制转换为十进制*/  //如1111  从右边开始权重为2^0 2^1 2^2 2^3... 
    以此类推,1111 就是(2^0) * 1 + (2^1) * 1 + (2^2) * 1 + (2^3) * 1 


    /*二进制转换为八进制*/ 
    我们知道  八进制 0~7 
    7的二进制位数为 111 所以二进制位转换为八进制时最高只需3位
    如一个二进制数为1111 -->  001 111         001二进制为1 111二进制为7
    所以该二进制转换为八进制是 017 ---在17前加0表示为八进制,这是编译器规定的,不然编译器无法分辨17为
    十进制还是八进制
    printf("%d\n", 17); //17
    printf("%d\n", 017);//15

    /*二进制转换为十六进制*/
    同样的 十六进制0~15
    15的二进制数为 1111 所以最高只需4位
    但是我们知道十六进制有十六个数字,为了防止误解
    0~9   a  b  c  d  e  f
         10 11 12 13 14 15
    转换方式与八进制相同,表示十六进制前需要加0x;
    printf("%d\n", 16); //16
    printf("%d\n", 0x16);//22

    八进制和十六进制转换为二进制反推即可。

    return 0;
}

原码 反码 补码 - 进行整数运算时才使用 

原码 反码 补码 - 进行整数运算时才使用
int main()
{
	整数的二进制表示方法有三种即 -> 原码 反码 补码
	符号则由最高位表示符号位,其他表示数值位。
	符号位0表示正数,1表示负数

	整数类型是4字节->32个bit位的
	00000000 00000000 00000000 00001010 -原码
	int a = 10;
	10000000 00000000 00000000 00001010 -原码
	int b = -10;

	正整数的原码 反码 补码相同

	int a = 10;
	00000000 00000000 00000000 00001010 -原码
	00000000 00000000 00000000 00001010 -反码
	00000000 00000000 00000000 00001010 -补码

	负整数
	/*原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
	反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
	补码:反码 + 1就得到补码。
	反码得到原码也是可以使⽤:取反, + 1的操作。*/

	int b = -10;
	10000000 00000000 00000000 00001010 -原码
	11111111 11111111 11111111 11110101 -反码
	11111111 11111111 11111111 11110110 -补码
	
	了解以上后,为什么要使用原码,反码,补码呢,原因是计算机系统中数据存放在内存中是
	采用补码的形式,gpu中只有加法器,不可以算减法

	如  1-1 这个是不能计算的       1+(-1)这个可以计算
	printf("%d", 1 + (- 1)); //答案毋庸置疑是0
	但是需要使用补码来计算
	 1的原码:00000000 00000000 00000000 00000001 -原码
	-1的原码:10000000 00000000 00000000 00000001 -原码
	 相加则为 10000000 00000000 00000000 00000010 - 这个答案是-2 则计算错误


	但是使用补码计算就是对的
	 1的补码:00000000 00000000 00000000 00000001 -补码
	-1的补码:11111111 11111111 11111111 11111111 -补码
	相加则为:10000000 00000000 00000000 000000000 前面多的一位是存不下的必然丢失
	         00000000 00000000 00000000 00000000  - 0
	return 0;
}

移位操作符


<<左移位操作符
 >>右移位操作符
只能在整数上操作,因为挪位置是操作补码

 <<左移位操作符

int main()
{
	//正数
	int a = 10;

	//10二进制数字 00000000 00000000 00000000 00001010 -补码
	//左边丢弃,右边补上
	//            00000000 00000000 00000000 00010100 -相当与 *2  就等于20

	int b = a << 1;//20
	int c = a << 2;//40
	int d = a << 3;//80
	int e = a << 4;//160
	printf("a == %d\n", a);
	printf("b == %d\n", b);
	printf("c == %d\n", c);
	printf("d == %d\n", d);
	printf("e == %d\n", e);
	

	//负数
	int f = -10;

	//10二进制数字 10000000 00000000 00000000 00001010 -原码
	//            11111111 11111111 11111111 11110101 -反码
	//            11111111 11111111 11111111 11110110 -补码

	int g = f << 1;

	//左边丢弃,右边补上
	//            11111111 11111111 11111111 11101100 -补码 - g
	//            10000000 00000000 00000000 00010100 -原码 - g  等于-20

	printf("f == %d\n", f); // -10
	printf("g == %d\n", g); // -20

	return 0;
}

>>右移位操作符

 右移有两种——逻辑和算术,这取决于编译器,vs编译器是采用算术右移。

逻辑右移:

算术右移:

注意:左移和右移的都是补码 

正数:

逻辑右移 
 右移前:00000000 00000000 00000000 00001010  10
 右移后:00000000 00000000 00000000 00000101   他总是在前面补0,这显然会带来一定的错误

算术右移
右移前: 00000000 00000000 00000000 00001010
右移后: 000000000 00000000 00000000 0000101 左边⽤原该值的符号位填充,右边丢弃

int main()
{
	int a = 10;
	int b = a >> 1;
	printf("a == %d\n", a);//10
	printf("b == %d\n", b);//5
	//算术右移和左移差不多但是是反过来 /2 
	return 0;
}



负数:

逻辑右移 
 右移前:11111111 11111111 11111111 11110110  -10
 右移后:011111111 11111111 11111111 1111011   他总是在前面补0,这显然会带来一定的错误


011111111 11111111 11111111 1111011 // 2147483643 是一个很大的正数



算术右移
右移前: 11111111 11111111 11111111 11110110
右移后: 111111111 11111111 11111111 1111011 左边⽤原该值的符号位填充,右边丢弃

取反+1
111111111 11111111 11111111 1111011
100000000 00000000 00000000 0000101 // -5


int main()
{
	int a = -10;
	int b = a >> 1;
	printf("a == %d\n", a);//-10
	printf("b == %d\n", b);//-5
	//算术右移和左移差不多但是是反过来 /2 
	return 0;
}



注意:不要写出 a<<-1 这种逻辑代码 //error

位操作符

 & - 按位与     && - 逻辑与(并且)
 | - 按位或     || - 逻辑或(或者)
 ^ - 按位异或   
 ~ - 按位取反

& - 按位与 

int main()
{
	int a = 4;
	int b = -7;
	int c = a & b;

	//使用补码进行按位与
	//对应二进制位有0就是0,两个同时为1才是1。
	//00000000 00000000 00000000 00000100 -    4的补码
	//11111111 11111111 11111111 11111001 -  (-7)的补码
	//00000000 00000000 00000000 00000000 -   0

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

| - 按位或

int main()
{
	int a = 4;
	int b = -7;
	int c = a | b;

	//使用补码进行按位或
	//对应二进制位有1就是1,两个同时为0才是0。
	//00000000 00000000 00000000 00000100 -    4的补码
	//11111111 11111111 11111111 11111001 -  (-7)的补码
	//11111111 11111111 11111111 11111101 -   补码
	//取反 +1
	//10000000 00000000 00000000 00000011 -   原码 - (-3)

	printf("c == %d\n", c);// -3
	return 0;
}

^ - 按位异或

int main()
{
	int a = 4;
	int b = -7;
	int c = a ^ b;

	//使用补码进行按位异或
	//规则:相同为0,相异为1
	//00000000 00000000 00000000 00000100 -    4的补码
	//11111111 11111111 11111111 11111001 -  (-7)的补码
	//11111111 11111111 11111111 11111101 -   补码
	//10000000 00000000 00000000 00000011 -   原码

	printf("c == %d\n", c);// -3
	return 0;
}

 ~ - 按位取反

int main()
{
	int a = 0;
	int b = ~a;

	//00000000 00000000 00000000 00000000 -补码
	//11111111 11111111 11111111 11111111 -b中的补码
	//取反 +1 
	//10000000 00000000 00000000 00000001  -- (-1)

	printf("b == %d\n", b);// -1
	return 0;
}

 异或运算的特殊场景

 相同为0,相异为1
 a^a = 0
 a^0 = a
假设a为1
二进制 001
a ^ a 000 - 0
a ^ 0 001 - 1
支持交换律 即 a ^ b ^ a == a ^ a ^ b;
int main()
{
	int a = 3;
	int b = 5;
	printf("交换前:a = %d b = %d\n", a, b);
	a = a ^ b;
	b = a ^ b;//等价于 b = a ^ b ^ b; a ^ 0; a
	a = a ^ b;//等价于 a = (a ^ b) ^ a; 0 ^ b; b
	printf("交换后:a = %d b = %d\n", a, b);
	return 0;
}

 实际题目应用

统计二进制数中1的个数
我们知道十进制数统计每个各位数时是 %10 /10 
那么二进制数可以通过 %2 /2 

方法一:
int count_of_bit(unsigned int m)
{
	int count = 0;
	while (m)
	{
		if (m % 2 == 1)
			count++;
		m /= 2;
	}
	return count;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = count_of_bit(n);
	printf("%d\n", ret);
	return 0;
}


方法二:
int count_of_bit(unsigned int m)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((m >> i) & 1) == 1)
			count++;
	}
	return count;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = count_of_bit(n);
	printf("%d\n", ret);
	return 0;
}


方法三:
int count_of_bit(unsigned int m)
{
	int count = 0;
	while (m)
	{
		count++;
		m = m & (m - 1);
	}
	return count;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = count_of_bit(n);
	printf("%d\n", ret);
	return 0;
}


改动单独一个位置
int main()
{
	int n = 13;

	//00000000 00000000 00000000 00001101 改动前
	//00000000 00000000 00000000 00010000 改动或
	//00000000 00000000 00000000 00011101 改动后
	//将n的第五位改成1
	//1<<(5-1)

	n = n | (1 << 4);
	printf("%d\n", n);

	//00000000 00000000 00000000 00011101 改动前
	//11111111 11111111 11111111 11101111 改动与 

	//{这个数字就是
    //00000000 00000000 00000000 00010000 按位取反}

	//00000000 00000000 00000000 00001101 改动后
	//改回来

	n = n & (~(1 << 4));
	printf("%d\n", n);
	return 0;
}

单目操作符和双目操作符

故名思意:只有一个操作数的就叫做单目操作符,存在两个操作数的称作双目操作符。

如:

!、++、--、&、*、+、-、~ 、sizeof、(类型)
int main()
{
	int a = 1;
	int b = 10;

    像以下的加减乘除等等,是对两个操作数进行操作称作双目操作符

	printf("%d\n", a + b); // 11
	printf("%d\n", a - b); // -9 
	printf("%d\n", a * b); // 10
	printf("%d\n", a / b); // 0
	printf("%d\n", a % b); // 1
	printf("%d\n", a & b);
	// & 有0就是0,两个为1才是1
	// 00001010 - 10
	// 00000001 - 1
	// 00000000 - 0

	printf("%d\n", a | b);
	// | 有1就是1,两个为0才是0
	// 00001010 - 10
	// 00000001 - 1
	// 00001011 - 11

	printf("%d\n", a ^ b);
	// ^ 相同为0,相异为1
	// 00001010 - 10
	// 00000001 - 1
	// 00001011 - 11

	printf("%d\n", ~a);
	// 00000000 00000000 00000000 00000001 - 1
	// 11111111 11111111 11111111 11111110 
	// 取反+1
	// 10000000 00000000 00000000 00000010 - -2

	printf("%d\n", ~b);
	// 00000000 00000000 00000000 00001010 - 10
	// 11111111 11111111 11111111 11110101
	// 取反+1
	// 10000000 00000000 00000000 00001011 - -11

	return 0;
}

像自增,自减这种是单目操作符

int main()
{
	int a = 10;
	int b = ++a;
	int c = --a;

	printf("%d\n", a); // 10
	printf("%d\n", b); // 11
	printf("%d\n", c); // 10

	return 0;
}

三目操作符 

列如:

表达式1 ? 表达式2 :表达式3;

 如果表达式1的判断为真,执行表达式2,否则执行表达式3,我们可以利用这个特性来判断两个整数的大小。

int main()
{
	int a = 1;
	int b = 2;
	int max = a > b ? a : b;
	printf("max = %d\n", max); // max = 2
	return 0;
}

下标访问[],函数调用()

下标访问 [ ] 

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

int arr[10];
arr[10] = 1;

 函数调用()

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

void Text01()
{
	printf("hehe\n");
}

int main()
{
	Text01();
	return 0;
}

 结构体成员访问操作符

结构体 

用于存放一类型的元素集合,如学生这一对象,我们可以存放学生的名字,学生的性别,学生的学号等等。

声明格式如下:

struct Student
{
	char name[20];	// 学生的名字
	char sex[5];	// 学生的性别
	int age;		// 学生的年龄
	char id[20];	// 学生的学号
};

 初始化:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值