详解 c语言中的操作符

文章目录

  • 一、算术操作符号
  • 二、移位操作符
    • 1. << 左移操作符
    • 2. >> 右移操作符
  • 三、位操作符号
    • 补充 原码 补码 反码 的转换
    • 1. 按位与 ( & )
    • 2.按位或 ( | )
    • 3.按位异或 ( ^ )
    • 4. 小练习
      • 1.不能创造 临时变量,实现两数交换
      • 2.编写代码实现:求一个整数存储在内存中的二进制中1的个数
  • 四、赋值操作符
    • 1. =
    • 2.复合赋值符
  • 五、单目操作符
    • 1. 逻辑反操作 ( ! )
    • 2. 正值 ( + ) 与 负值 ( - )
    • 3. 取地址操作符 ( & )
    • 4. sizeof
    • 5. ( ~ ) 按位取反操作符
    • 6. ++ 与 --
    • 7. ( * ) 间接访问操作符(解引用操作符)
    • 8.() 强制类型转换
  • 六、关系运算符
  • 七、逻辑操作符 (&&) ( || )
    • 1. 逻辑与 && 左边为假,右边就不计算
    • 2.逻辑或 || 左边为真,右边就不计算
    • 3. 有关逻辑与逻辑或的练习
  • 八、三目操作符 与 逗号表达式
    • 1.三目操作符
    • 2.逗号表达式
  • 九、下标引用、函数调用和结构成员
    • 1.下标引用操作符 [ ]
    • 2.函数调用操作符
    • 3. 结构体成员访问操作符 ( . ) ( ->)

一、算术操作符号

  • ( + )
  • ( - )
  • ( * )
  • ( / )
  • ( % )
  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
/
int a = 1;
int b = 2;
printf("%d",a/b);
结果 为 0 ,因为 a 和 b都 为int 型相处 结果保留整数部分
如果想要 得到 0.5
float = 1.0f;
float = 2.0f;
printf("%d",a/b);
结果就是0.5%
1. 使用 % 两边必须是整数
int a = 10;
int b = 3;
printf("%d",a%b);
结果就为 1

二、移位操作符

1. << 左移操作符

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

<<
int a = 10;

00000000 00000000 00000000 00001010  ----  a 的二进制 位
a << 1 
(0)0000000 00000000 00000000 00001010
括号里的 0被丢弃,
00000000 00000000 00000000 0001010(0) 
右边补上 0
最终为
00000000 00000000 00000000 00010100 ---- 20
 
 左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8

2. >> 右移操作符

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

  1. 逻辑移位 左边用0填充,右边丢弃
  2. 算术移位 左边用原该值的符号位填充,右边丢弃。
 	注 绝大部分编译器 使用的 都是 算术移位
 	1. 算术移位
 	int a = 10;
 	00000000 00000000 00000000 00001010 ---- a 的二进制位
 	
 	00000000 00000000 00000000 0000101(0) --- 括号里是将要丢弃的
 	
	(0)0000000 00000000 00000000 00000101 --- 括号里补符号位,0  结果 5
	
	int a = -10;
	
	10000000 00000000 00000000 00001010 ---- a 的二进制位  原码
	
	11111111 11111111 11111111 11110101 ---- a 的反码
	
	11111111 11111111 11111111 11110110 ---- a 的补码
	
	11111111 11111111 11111111 1111011(0) --- 右移丢弃

	(1)1111111 11111111 11111111 11111011  --- 补符号位 1  补码,打印需要原码
	
	11111111 11111111 11111111 11111010  --- 反码
	
	10000000 00000000 00000000 00000101 --- 原码  结果 -5
	可以见 右移 是 除以 2 ,
	与 左移 相反 1位 除 224 38 ......
	
	2. 逻辑右移
	
	int a = -10;
	10000000 00000000 00000000 00001010 ---- a 的二进制位  原码
	
	11111111 11111111 11111111 11110101 ---- a 的反码
	
	11111111 11111111 11111111 11110110 ---- a 的补码
	
	11111111 11111111 11111111 1111011(0)
	  
	(0)1111111 11111111 11111111 11111011  
	
	01111111 11111111 11111111 11111011  ----符号位为 0 正数 原返补相同
	所以逻辑右移 一个负数 会变成一个挺大的正数
	

三、位操作符号

& - 按 (二进制) 位与
| - 按 (二进制) 位或
^ - 按 (二进制) 位异或

补充 原码 补码 反码 的转换

计算机中的整数有三种2进制表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位
正数的原、反、补码都相同。
负整数的三种表示方法各不相同。

原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码
反码+1就得到补码
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统
一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程
是相同的,不需要额外的硬件电路。

如 1-1
1的原码反码补码相同
0000000000000000000000000000000000000001

-1
原码
1000000000000000000000000000000000000001
反码
1111111111111111111111111111111111111110
补码
1111111111111111111111111111111111111111

由补码到反码
反码减一
由反码到原码
符号位不变 补码取反


1. 按位与 ( & )

按位与规则为
对应的 二级制位 只要 有 0 就为 0
2个都为 1 结果 为1

int main()
{
	int a = 3;
	int b = -5;
	 正数 的原码补码反码相同
	00000000 00000000 00000000 00000011 - 3的补码 

	10000000 00000000 00000000 00000101 - -5的原码
	11111111 11111111 11111111 11111010 - -5的反码
	11111111 11111111 11111111 11111011 - -5的补码
	00000000 00000000 00000000 00000011 - 3的补码 
	 对应的 按位与只要 有 0 就为 0 2个都为 1 结果 为1
	  3 & -5
	00000000 00000000 00000000 00000011  --- 3
	 %d  打印 一个 有符号整数
	printf("%d", a & b);
}

2.按位或 ( | )

int main()
{
	int a = 3;
	int b = -5;
	 正数 的原码补码反码相同
	00000000 00000000 00000000 00000011 - 3的补码 

	10000000 00000000 00000000 00000101 - -5的原码
	11111111 11111111 11111111 11111010 - -5的反码
	11111111 11111111 11111111 11111011 - -5的补码
	00000000 00000000 00000000 00000011 -  3的补码 
	 对应的二进制位只要有 一 才为一 2个 同时为 0 才为 0
	  3 & -5
	11111111 11111111 11111111 11111011 --- 补码
	11111111 11111111 11111111 11111010 --- 反码
 	10000000 00000000 00000000 00000101 --- 原码 -5  
	 %d  打印 一个 有符号整数
	printf("%d", a | b);
}

3.按位异或 ( ^ )

int main()
{
	int a = 3;
	int b = -5;
	 正数 的原码补码反码相同
	00000000 00000000 00000000 00000011 - 3的补码 

	10000000 00000000 00000000 00000101 - -5的原码
	11111111 11111111 11111111 11111010 - -5的反码
	11111111 11111111 11111111 11111011 - -5的补码
	00000000 00000000 00000000 00000011 -  3的补码 
	 对应的二进制位只要不同就 为1 相同为0
	  3 ^ -5
	11111111 11111111 11111111 11111000 --- 补码
	11111111 11111111 11111111 11110111 --- 反码
	10000000 00000000 00000000 00001000 --- 原码 -8  
	 %d  打印 一个 有符号整数 
	printf("%d", a ^ b);
}

4. 小练习

1.不能创造 临时变量,实现两数交换

第一种方法
#include
// 这种方法会有溢出的问题
int main()
{
	int a = 10;
	int b = 20;
	printf("a = %d b = %d\n", a, b);
	a = a + b;
	b = a - b;
	a = a - b;
	printf("a = %d b = %d\n", a, b);
}
第二种方法
结论 异或 支持 交换律 
int main()
{
	int a = 10;
	int b = 20;
	printf("a = %d b = %d\n", a, b);
	a = a ^ b;
	相同的两个数字异或还是原来的值
	例 3 ^ 3 
	 00000000 00000000 00000000 00000011
	 00000000 00000000 00000000 00000011
	 异或 规则 相异为 1 相同 为 0 
	 所有 3 ^ 30 
	 00000000 00000000 00000000 00000101  ---5 的补码
	 00000000 00000000 00000000 00000000  ---0 的补码
	 异或一下结果 为 5 
	 得出结论 0 不管与谁异或都得本身 0 ^ 0 除外 
	 所以 a ^ b ^ b == a ^ 0 == a
	 
	 0 ^  5 == 5
	b = a ^ b; // a ^ b ^ b
	a = a ^ b;// a ^ b ^ a  == a ^ b ^ b ^b ^ a  
	printf("a = %d b = %d\n", a, b);
	return 0;
}

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

翻译 题目 求整数二进制补码在内存1得个数

方法一
a & 1 
int a = 3
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000001
得到最后一位 有一位 1:
然后 然  a  像 右移动一位
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000001
说明 第二位 也是 1 
继续右移
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000000
说明得 三位 不为 0
依次类推 就能得到 1 得个数

代码如下
int main()
{
	int num = 648;
	int count = 0;// 计数器
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (num & 1 == 1)
		{
			count++;
			num = num >> 1;
		}
		else
		{
			num = num >> 1;
		}
	}
	printf("%d", count);
	return 0;
}

方法 二 : 我们可以了解 一下奇数的 二进制 最后一位必定为一,我们让 a % 2 == 1 就说明a 为奇数 ,a/2 得到一个新的数 在继续
代码如下

int main()
{
	int a = 10;
	int count = 0;// 计数器
	while (a)
	{
		if (a % 2 == 1)
		{
			count++;
		}
		a /= 2;//得到一个新的数,直到为0
	}
	printf("%d", count);
	return 0}
但这种方法只能计算正数 有缺陷

方法三 (最优解)
思路
假设num的二进制序列为1111
num-----------1111
num-1-------- 1110
num=num&(num-1)
num-----------1110
num-1 --------1101
num=num&(num-1)
num-----------1100
根据以上例子,我们不难看出每次num=num&(num-1)之后,num的二进制序列都会少一个1,直至循环到num为0时,count就是二进制中1的个数
代码如下
int main()
{
	int a = 10;
	int count = 0;
	while (a)
	{
		count++;
		a = a & (a - 1);
	}
	printf("%d", count);
	return 0;
}

 加深印象题
第一题 输入两个整数,求两个整数二进制格式有多少个位不同

1.最优解  方法一
#include

int main()
{
    int a = 0;
    int b = 0;
    scanf("%d%d",&a,&b);
    int c = a^b;
    int count = 0;
    while(c)
    {
        count++;
        c = c&(c-1);
    }
    printf("%d",count);
    return 0;
}

2. 方法二
 #include

int main()
{
    int a = 0;
    int b = 0;
    scanf("%d%d", &a, &b);
    int c = a ^ b;
    int count = 0;
    while (c)
    {
        if (c % 2 == 1)
        {
            count++;
        }
        c /= 2;
   }
    printf("%d", count);
    return 0;
}

3. 方法三
 #include

int main()
{
    int a = 0;
    int b = 0;
    scanf("%d%d", &a, &b);
    int c = a ^ b;
    int count = 0;
    int i = 0;
    for (i = 0; i < 32; i++)
    {
        if (c & 1 == 1)
        {
            count++;
            c = c >> 1;
        }
        else
        {
            c = c >> 1;
        }
   }
    printf("%d", count);
    return 0;
}

第二题 获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
#include

int main()
{
	int n = 0;
	int i = 0;
	scanf("%d",&n);

	for (i = 0; i < 32; i+=2)
	{
		printf("%d ", (n >> i) & 1);
	}
	printf("\n");
	for (i = 1; i < 32; i += 2)
	{
		printf("%d ", (n >> i) & 1);
	}
	return 0;
}

四、赋值操作符

1. =

赋值操作符 其实就 是为变量赋值的 
如
int a = 10;
如果 你觉得 10 不行 可以改
a = 20;
int b = 0;
int c = 0;
连续赋值 
a = b = c = 10;
这样也是可以得,赋值操作符没有太多东西 了解这些就差不多了

2.复合赋值符

+=   如 a += 10 与 a = a + 10 等价
-= 		a -= 10    a = a - 10
*=		a *=10 	   a = a * 10
/= 	    a /= 10    a = a / 10
%=	    a &= 10    a = a & 10
>>=     a >>= 10   a = a >> 10
<<=     a <<= 10   a = a<< 10 
&=      a &= 10    a = a & 10
|=      a |= 10    a = a| 10
^=      a ^=10     a = a^10

五、单目操作符

1. 逻辑反操作 ( ! )

int main()
{
	int a = 10;
	if(!1) 此时 不会打印 a if 条件 为真 但 ! 把 1 转化为 0 条件为假就不会进入 if语句中
	{
		printf("%d",a);
	}
	return 0;
}

2. 正值 ( + ) 与 负值 ( - )

正值 与 负值 其实就与数学中得 + - 相同 在这就不讲解 只是让大家知道 他们 为单目操作符

3. 取地址操作符 ( & )

取地址操作符 一般与指针 那块息息相关
我们 来了解 一下 & 得作用 
int a = 10;
&a 就是拿到变量在内存中得起始地址
int* p = &a; p 就为一个指针变量
printf("%p",&a); 打印 a 得地址
int arr[10] = {0};
&arr 此时 拿出得数组得地址 指向第一个元素
int(*p)[10] = &arr;

4. sizeof

sizeof 也是一个 单目操作符
int n =10;
int sz = sizeof(n); sizeof 计算得是 n 所占内存得大小,单位是字节
计算变量所占内存空间得大小
运用sizeof 计算数组大小
int arr[10] = { 0 };
int sz = sizeof(arr)/sizeof(arr[0]);
注意 sizeof(arr) 此时计算整个数组所占内存空间大小

5. ( ~ ) 按位取反操作符

int a = 0;
 ~ 是按二进制位取反
00000000 00000000 00000000 00000000 --- 0 原反补相同
~ 按位取反后
11111111 11111111 11111111 11111111 --- 补码
11111111 11111111 11111111 11111110 --- 反码
10000000 00000000 00000000 00000001 --- 原码
printf("%d",~a); ---- -1

6. ++ 与 –

 前置++--
 后置++--
int main()
{
	int a = 10;
	int b = ++a;
	int c = --a;
	int d = a++;
	int f = a--;
	printf("%d %d %d %d",b,c,d,f);
	结果为 11 9 10 10
	为啥呢  前置++-- 就相当于 b = a + 1; c = a - 1
	后置 ++--  相当于  d = a; a = a + 1; f = a; a = a-1
	可以看出 前置 是 (++--) 使用 在赋值
	而 后置 是 先赋值 在使用   
	
}

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

( * )功能 :取该指针指向的存储单元的值

int main()
{
 int a = 10;
 int* p = NULL;
 p = &a;
 //*p;
 // p 存放的地址 ,* 就是 找到他所指向的对象的值
 printf(" %d ",*p);
}

8.() 强制类型转换

int a = 10;
printf("%f",(float)a);
我们知道%f 是打印 浮点型数据的 这里我们想要打印
a 可以 强制类型转换 (float) a 
强制类型转换 其实就是在要转换的变量前 加上一个() 括号里输入要转换的类型

六、关系运算符

>
>=
<
<=
!=   用于测试“不相等”
==      用于测试“相等”

关系运算符 就是一些比大小 等的操作符 ,但要注意
不能 把 == 写 做 = 要不 会出现 错误
如循环 把 == 写成 = 那么循环 大概率会死循环
还有 if 语句 如果 写成 = 那么 不管 条件成不成立 都会进入语句。

七、逻辑操作符 (&&) ( || )

1. 逻辑与 && 左边为假,右边就不计算

&& 相当于 并且 ,两边都为真 才能为真 ,一个为假 都为假

int main()
{
	int a = 3;
	int b = 5;
	if(a && b)
	{
	 printf("%d",a+b);
	}
	else
	{
		printf("%d",b-a);
	}
	return 0;
}

运行后 我们发现结果 为 8 为什么呢
&& 只关注真 与 假
如果 两边都为真 则返回 1
反之 返回 03 && 5 结果 就为 1
   3 && 0 结果 就为 0

2.逻辑或 || 左边为真,右边就不计算

|| 相当于 或者 只要有一个为真就为真,


int main()
{
	int a = 3;
	int b = 0;
	if(a || b)
	{
	 printf("%d",a+b);
	}
	else
	{
		printf("%d",b-a);
	}
	return 0;
}
结果 为 3 
b 虽然 为 0|| 了 也可以进入 if 语句

注
a = 3, b = 0
	if(a || ++b)
请问 此时 b 的值为多少
这里有一个大坑 || 会先判断 a 是否为真 如果为真 
就会直接 进入 语句 ,++b 就不会执行 ,
所以b 还是 0.	

3. 有关逻辑与逻辑或的练习

#include 
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;
    i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0; }
逻辑与 时 a == 1,b == 2 c == 3, d == 4
	a++ 后置 ++ 会先 使用 a == 0,后 ++  然后 &&  第一个为假 后面就不会继续计算
逻辑或    a == 1,b == 3,c == 3, d == 4
	a ++ 后置 ++ 会先 使用 a == 0++ 然后 || 第一个 为假, 判断第二个表达式 ++b  == 3为真  后面就不用判断了
	 

八、三目操作符 与 逗号表达式

1.三目操作符

exp1 ? exp2 : exp3

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

int main()
{
	int a = 3;
	int b = 0;
	if(a > 5)
	{
		b = 3;
	}
	else
	{
		b = -3;
	}
}
运用三目操作符 简化以上代码

b = ( a > 5 ) ? ( b = 3 ): ( b = -3 );
a > 5 吗 不 大于 那将 b = -3 赋予 b

2.逗号表达式

逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
c 为 13 从 左边 算到 右边,结果 取最右边的

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

1.下标引用操作符 [ ]

操作数:一个数组名 + 一个索引值`
int arr[10] = { 0 };
arr[7] = 6; 就是通过下标引用操作符 为第8个元素赋值

 4 + 5 = 5 + 6 
 这个为 加法的交换律那么 下标引用操作符遵守这个吗
 arr[7] = 4;
 7[arr] = 4;
 大家可以尝试一下
 结果 发现这样 也没有 错误,更能体现 [] 是一个操作符
 但 不能 这样 定义数组
补 arr[7] == *( arr + 7 ) == *7 + arr )== ( 7[arr] )
arr 指向数组首元素的地址 ,+7 向右移动 7位 就为第 8个元素的地址 * 找到 这个地址所代表的值

2.函数调用操作符

int Add(int x,int y)
{
	return x+y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a,b); 这里的 () 就是函数调用操作符 ,操作数 Add , a , b
	return 0;

3. 结构体成员访问操作符 ( . ) ( ->)

定义一个结构体
struct Stu
{
	char name[20];
	int age;
	double score;
}
void set_stu(struct Stu s)
{
	s.name = "zhangsan";
	name 是一个数组 ,数组是一个地址,不能将一个字符串赋给一个地址
	随意要用 strcpy () 库函数
	strcpy(s.name,"zhangsan");
	s.age = 20;
	s.score = 100.0;
}
void print_stu(struct Stu s)
{
	printf("%s %d %f",s.name,s.age,s.score);
}

int main()
{
	struct Stu s = { 0 };
	set_stu ( s );
}

上面 会打印不出来 因为 是值传递 ,形参的改变 并不会影响实参,我们要使用 址传参

struct Stu
{
	char name[20];
	int age;
	double score;
}
void set_stu(struct Stu *ps)
{
	strcpy((*ps).name,"zhangsan");
	(*ps).age = 20;
	(*ps).score = 100.0;

	strcpy(ps->name,"zhangsan");
	ps->age = 20;
	ps->score = 100.0;
	这里使用 ->  简洁一点
	(*ps).age 等价于 ps->age
	
}
void print_stu(struct Stu s)
{
	printf("%s %d %f",s.name,s.age,s.score);
}

int main()
{
	struct Stu s = { 0 };
	set_stu ( &s );
	print_stu (s);
}

详解 c语言中的操作符_第1张图片

你可能感兴趣的:(c语言,c语言,开发语言)