5.操作符

1.算术操作符

+ - * / %

%

%得到的是余数
取模(余)操作符的两个操作数必须为整数
返回的是整除之后的余数
模0或模负数无意义
10.0 % 3 非法

/

10 / 3 => 3
除法得到的是商
对于 / 操作符如果两个操作数都为整数则执行整数除法
而只要有一个浮点数那么执行的就是浮点数除法
10 / 3.0 => 3.33333
注意double类型要以%lf格式打印

2.移位操作符

移位操作符操作数只能是整数

<<左移操作符

<< 左边抛弃,右边补0

#include <stdio.h>
int main()
{
	int a = 5;//0101
	int b = a << 2;
    //把a在内存中存储的二进制向左移动2位
	printf("%d\n", b);//20 = 5 * 2 * 2
	printf("%d\n", a);//5
}

<<只是一种运算,没有改变a的值
<<有*2的效果

打印/使用时 用的是原码的值

整数的3种二进制

int a = 5;
5 -十进制
00000000000000000000000000000101 -二进制(32位)
进制只是数值的一种表现形式

原码
反码
补码
正整数->三码相同

//5:
00000000000000000000000000000101 -原码
00000000000000000000000000000101 -反码
00000000000000000000000000000101 -补码
//负整数->
int a = -5;
1000000000000000000000000101 //-原码
1111111111111111111111111010 //-反码 -> 原码符号位不变,其他位按位取反得到反码
1111111111111111111111111011 //-补码 ->反码二进制+1得到补码

整数在内存种存储的是补码!!!

用16进制展示 但内存中是以2进制存储
-1在内存中的存储

image-20220111212844356

>>右移操作符

到底右移是算术右移还是逻辑右移取决于编译器
常见的编译器下都是算术右移

1.算术右移

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

#include <stdio.h>
int main()
{
	int a = 5;
	int b = a >> 1;
	printf("%d\n", b);//2  0010
	printf("%d\n", a);//5  0101
}
#include <stdio.h>
int main()
{
	int a = -5;
	int b = a >> 1;
	printf("%d\n", b);//  -3
	printf("%d\n", a);//  -5
}

VS编译器输出结果为-3 证明采用的是算术右移
image-20220111213553515

2.逻辑右移

左边补0,右边丢弃

注意:
不要写出移动负数位

int b = a >> -2;//标准未定义行为

3.位操作符

操作数必须为整数

&

//2个都是1才是1,只要有0就是0
00000000000000000000000000000011
00000000000000000000000000000101
//结果:
00000000000000000000000000000001
int main()
{
	int a = 3;
	int b = -5;
	int c = a & b; //补码相与
	printf("%d\n", c); //3
}
//a b计算时用的都是存在内存中的补码计算
00000000000000000000000000000011  3的补码
10000000000000000000000000000101  
11111111111111111111111111111010
11111111111111111111111111111011  -5的补码
//a&b
00000000000000000000000000000011  补码
//正数三码相同,打印的是原码的值

|

只要有1就是1,同时为0才是0

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

a b计算时用的都是存在内存中的补码计算

00000000000000000000000000000011  3的补码
10000000000000000000000000000101  
11111111111111111111111111111010
11111111111111111111111111111011  -5的补码
a | b
11111111111111111111111111111011  补码
打印的是原码
11111111111111111111111111111010 反码
10000000000000000000000000000101  -5
11111111111111111111111111111011  补码
10000000000000000000000000000100
10000000000000000000000000000101  原码
    //负数的补码符号位不变,其他位取反,然后再+1就能得到原码

^

相同为0,不同为1

int main()
{
	int a = 3;
	int b = -5;
	int c = a | b; 
	printf("%d\n", c); //-8
}
a b计算时用的都是存在内存中的补码计算
00000000000000000000000000000011  3的补码
10000000000000000000000000000101  
11111111111111111111111111111010
11111111111111111111111111111011  -5的补码
a^b
11111111111111111111111111111000  补码
11111111111111111111111111110111  反码
10000000000000000000000000001000  原码  -8

练习

不创建临时变量交换2变量的值

int main()
{
	int a = 3;
	int b = 5;
	printf("a=%d b=%d\n", a, b);
	a = a + b;
	b = a - b;
	a = a - b;  //此时a里面是a+b b里面是a
	printf("a=%d b=%d\n", a, b);

	return 0;
}

缺点:数值太大可能会溢出 毕竟只有32位

3^3 = 0
0^5 = 5

#include <stdio.h>
int main()
{
 int a = 3;
 int b = 5;
 printf("a = %d b = %d\n", a, b);
 a = a^b;
 b = a^b; //此时a^b^b 结果为a
 a = a^b;// b中存着的是a a^b^a 结果为b
 printf("a = %d b = %d\n", a, b);
 return 0; 
}

异或性质

1交换律 A ^ B = B ^ A
2结合律( A ^ B ) ^ C = A ^ ( B ^ C )
3自反性: A ^ B ^ B = A

如果交换2个浮点数呢?
位操作符只能操作整数
交换浮点数只能借助第三个变量了

4.赋值操作符

=

a = x = y+1; //连续赋值
//不推荐这么写,调试起来不方便

左值 是可以放在等号左边的,一般是一块空间
右值 是可以放在等号右边的,一般是一个值,或者一块空间内容

复合赋值

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

5.单目操作符

3+5

+是双目操作符
3是左操作数
5是右操作数

单目操作符 只有一个操作数

? : 三目操作符

!逻辑反操作

#include<stdio.h>
int main()
{
	int flag = 0;
	if (!flag)
	{
		printf("hehe\n");
	}
}
return 0;

-负号

#include<stdio.h>
int main()
{
	int flag = 1;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i * flag);//0 -1 2 -3 4 -5 6 -7 8 -9
		flag = -flag;
	}
	return 0;
}

绝对值函数:
abs -对整数求绝对值
fabs -对小数求绝对值

&取地址操作符

sizeof操作符

求变量(类型)所占空间的大小,单位是字节

printf("%d\n",sizeof a);

这样写也对,证明了sizeof不是函数
sizeof int 这样写不对
sizeof(int)才行
尽量加上(),写代码要让别人看得懂

~按位取反

对一个数二进制位按位取反,是对内存中补码进行操作
但打印时按原码进行打印

#include<stdio.h>
int main()
{
	int a = -1;
	int b = ~a;
	//10000000000000000000000000000001	原码
	//11111111111111111111111111111110	反码
	//11111111111111111111111111111111	补码
	printf("%d\n", a);//-1
	printf("%d\n", b);//0
	return 0;
}
00000000000000000000000000000000 补码
三码相同
原码也是
00000000000000000000000000000000

int a = 10;
00000000000000000000000000001010   10
00000000000000000000000000000100   按位或 1<<2
00000000000000000000000000001110
a |= (1<<2)
就能把指定位变为1
变回来?
00000000000000000000000000000100  

00000000000000000000000000001110
按位与
11111111111111111111111111111011  ~(1 << 2)
就能得到原来的
00000000000000000000000000001010
a &= ~(1 << 2);

++ / –

i++ 先用了再加
++i 先加了再用

#include<stdio.h>
int main()
{
	int a = 10;
	//int b = ++a;
	//printf("a = %d b = %d\n", a, b);//11 11


	//int b = a++; //先使用,先把a的值赋给b,a再自增
	//printf("a = %d b = %d\n", a, b);//11 10

	printf("%d\n", a++);//10 先使用 a再加一 此时a是11了
	printf("%d\n", ++a);//12   a=11 先+1 再使用
	printf("%d\n", a--);//12  然后a-1变成11
	printf("%d\n", a--);//11   先使用a 然后再-1

	return 0;
}
#include<stdio.h>
int main()
{
	int a = 1;
	int b = (++a) + (++a) + (++a);
	printf("%d\n", b);//12
	printf("%d\n", a); //4
	return 0;
}

VS2019的环境下结果是12 4
gcc环境下结果是10 4
这是错误的代码
3个4相加

* 解引用

解引用操作符/间接访问操作符

#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	*pa = 20;
	printf("%d\n", a);//20
	return 0;
}
//*pa = 20;通过a的地址找到a,并改变其值

(类型)

强制类型转换

#include<stdio.h>
int main()
{
       int a = 3.14;
       //int a = (int)3.14;
       printf("%d\n", a);
       return 0;
}

会报警告:
C4244“初始化”: 从“double”转换到“int”,可能丢失数据

(int*) p = &a;//err
不要写成这样,编译器会认为是强制类型转换

练习

#include <stdio.h>
int main()
{
int a = -10;
int *p = NULL;
printf("%d\n", !2); //0
printf("%d\n", !0); //1
a = -a; //10
p = &a;
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(int)); //4
printf("%d\n", sizeof a);//这样写行不行? 行
printf("%d\n", sizeof int);//这样写行不行? 不行
return 0;
}

计算变量大小,括号可以省略
但计算类型大小,括号不能省略

#include<stdio.h>
void test1(int arr[])	//此处int arr[]本质是 int* arr
{
	printf("%d\n", sizeof(arr));//4
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//4 指针大小
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));     //40
	printf("%d\n", sizeof(ch));     //10
	printf("%d\n", sizeof(int[10]));    //40
	test1(arr);     //4
	test2(ch);     //4
	return 0;
}

sizeof(arr) 计算的是整个数组大小
sizeof内部单独放数组名,此时数组名表示整个数组
test1(arr) 数组传参,本质传的是指针,传过去的是首元素地址
指针大小是4/8个字节,与平台有关

6.关系操作符

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

注意不要把判断相等写成 =
if(a = 5)…

最好写成if(5 == a)…
老司机写法

7.逻辑操作符

逻辑操作符只关注变量的真假
位操作符是针对二进制位的

&&

#include<stdio.h>
int main()
{
       int age = 0;
       scanf("%d", &age);
       if (age > 0 && age < 18)
       {
               printf("未成年\n");
       }
       //不要写成if(0<age<18) 编译能通过,但是有逻辑错误
       return 0;
}

||

#include<stdio.h>
int main()
{
       int month = 0;
       scanf("%d", &month);
       if (month < 1 || month > 12)
       {
               printf("输入错误\n");
       }
       else
       {
       }
       return 0;
}

练习

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; }
//程序输出的结果是什么?

1 2 3 4
张三&&李四下课到办公室
张三李四都要来
左边为假时,后面就不执行了
后置++先使用a = 0再++
0 && …
有一个为假,后面没必要再算了
整体都为假了

int main()
{
    int i = 0,a = 1,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; }
//程序输出的结果是什么?

2 3 3 5
a = 1时 都为真了,后面需要计算
&&操作符只关注真假

int main()
{
    int i = 0,a = 1,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; }
//程序输出的结果是什么?

2 2 3 4
a++ 表达式值为1 a变为2
a++ 表达式为真,后面就不用算了

总结

|| 左操作数为真,后面不用算
&& 左操作数为假,后面不用算

8.条件操作符

exp1 ? exp2 : exp3
也叫三目操作符
if (a > 5)
b = 3;
else
b = -3;
转换成条件表达式,是什么样?
b = (a > 5 ? 3 : -3);
最好加个()便于理解,不加也是对的

#include<stdio.h>
int main()
{
       int a = 10;
       int b = 20;
       int max = a > b ? a : b;
       int min = a > b ? b : a;
       return 0;
}

9.逗号表达式

逗号表达式,从左向右依次执行
整个表达式的结果是最后一个表达式的结果

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

必须从左向右依次计算,不能只算最后一个表达式

int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?  13
a = get_val();
count_val(a);
while (a > 0)
{
        //业务处理
       a = get_val();
       count_val(a);
}
如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
        //业务处理
}

10.下标引用,函数调用,结构成员

下标引用操作符[]

操作数:一个数组名 + 一个索引值
arr[7]
[]是下标引用操作符
[]的2个操作数为arr和7
编译器是把arr[7] --> * (arr+7) 处理成这样才进行计算,找到第8个元素,编译器依旧以指针的形式进行相应的计算

arr[7] --> * (arr+7) --> * (7+arr) --> 7[arr]
#include<stdio.h>
int main()
{
       int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
       printf("%d\n", 7[arr]);//8
       printf("%d\n", arr[7]);//8
       return 0;
    //说明[]只是个操作符而已
}

函数调用操作符()

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

#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1();            //()作为函数调用操作符。
test2("hello bit.");//()作为函数调用操作符。
return 0;
}

结构成员访问操作数

.
->

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	double score;
};

int main()
{
	struct Stu s = {"zhangsan", 20, 85.5};
	//.
	printf("%s %d %.1lf\n", s.name, s.age, s.score);//结构体变量.结构体成员
	//->
	struct Stu *ps = &s;//结构体指针
	//printf("%s %d %.1lf\n", (*ps).name, (*ps).age, (*ps).score);
	printf("%s %d %.1lf\n", ps->name, ps->age, ps->score);//结构体指针->结构体成员
	return 0;
}

11.表达式求值

操作符优先级,结合性+类型转换

隐式类型转换

整型提升

C的整型算术运算总是至少以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

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

#include <stdio.h>
int main()
{
	char a = 5;
	char b = 126;
	char c = a + b;//均为char类型
	printf("%d\n", c);//结果为什么是-125?
	return 0;
}

sizeof(char),sizeof(short)均小于sizeof(int)

char a = 5;
//截断
00000000000000000000000000000101
00000101  //a里面只能存8位  低八位

char b = 126;
00000000000000000000000001111110
01111110

00000000000000000000000000000101 - a整型提升完的结果
00000000000000000000000001111110 - b整型提升完的结果
00000000000000000000000010000011
10000011 - c
当a和b相加的时候,a和b都是char类型
表达式计算时就要发生整形提升

char c = a + b;
10000011 - c
11111111111111111111111110000011 - 补码
10000000000000000000000001111100 - 反码
取反再+1就行
10000000000000000000000001111101 -> -125 原码

以%d格式打印的时候是打印原码的值,char型还需要整型提升

整型提升意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。
所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

CPU计算的是整型,char,short型计算前要转化为整型

如何提升

整形提升是按照变量的数据类型的符号位来提升的

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
    
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
return 0;
}

最终只打印c
a,b会发生整型提升,值发生了变化

int main()
{
char c = 1;
printf("%u\n", sizeof(c)); //1
printf("%u\n", sizeof(+c));//4
printf("%u\n", sizeof(-c));//4
return 0;
}

c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升
sizeof()内部表达式没有真正进行运算,即不知道表达式结果,但是我们知道的是表达式结果的类型
知道类型属性

int main()
{
    int a = 10;
 	int b = 20;
	a + b;//表达式有2个属性:值属性,类型属性
	30 就是值属性
	int 类型属性
 	return 0;
}

类型属性不需要计算也知道

int main()
{
    short s = 20;
 	int a = 5;
 	printf("%d\n", sizeof(s = a + 4));//2
    //计算的是sizeof(short)
 	printf("%d\n", s);//20
 	return 0;
}

a+4是int类型
a+4的结果放到short里面,是short说了算
sizeof()内部表达式没有真正进行运算

sizeof在编译期间就进行了计算,也就是说
在编译期间,sizeof(s = a + 4) 已经变成了2了
运行期间自然没有什么s = a + 4了
image-20220111230018984

算术转换

image-20220111225321295

long double
double
float
unsigned long int
long int
unsigned int
int

从下往上进行转换

int main()
{
    int a = 3;
	float f = 5.5;
	float r = a + f;//算术转换
	return 0;
}

操作符属性

表达式求值三个影响因素
1.操作符优先级
2.操作符结合性
3.是否控制求值顺序

两个相邻的操作符先执行哪个?
取决于他们的优先级。
如果两者的优先级相同,取决于他们的结合性。

优先级

image-20220111225611720

image-20220111225650584

是否控制求值顺序:

&&
左边为假右边就不用算了
||
左边为真右边也不用算了
,逗号表达式
?:条件操作符

问题表达式

有了这些,也没办法确定某些表达式的唯一解
image-20220111230647157

只能保证的*计算比+,
但是优先级并不能决定第三个+比第一个+早执行
把abcdef看成互相影响的表达式,那么代码的逻辑是会出错的
除非明确加上()
//表达式2
int c = 5;
5 + 4 = 9
4 + 4 = 8
c + --c;//问题代码

左右操作数有关联的,不能确定谁先准备好了
这也是有问题的

int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}

非法表达式,在不同的编译器结果不一样,编译器也已经凌乱了

#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}

image-20220111231424095

image-20220111231501396

练习

1.优先级

#include <stdio.h>int main()
{
        int a, b, c;
        a = 5;
        c = ++a;//c=6 a=6
        b = ++c, c++, ++a, a++;
//b=7 c=8 a=8
        b += a++ + c;
    //+的优先级比+=高
    //b=23 a=9 c=8
        printf("a = %d b = %d c = %d\n:", a, b, c);
        return 0;
}

b = ++c, c++, ++a, a++;
b=++c 优先级比逗号要高,先算
b=++c 和后边的构成逗号表达式,依次从左向右计算的

#include <stdio.h>
int main()
{
int a, b, c;
a = 5;
c = ++a;// ++a:加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6  c = 6
b = ++c, c++, ++a, a++;
// 逗号表达式的优先级,最低,这里先算b=++c, b得到的是++c后的结果,b是7
// b=++c 和后边的构成逗号表达式,依次从左向右计算的。
// 表达式结束时,c++和,++a,a++会给a+2,给c加1,此时c:8,a:8,b:7
b += a++ + c; // a先和c加,结果为16,在加上b的值7,比的结果为23,最后给a加1,a的值为9
printf("a = %d b = %d c = %d\n:", a, b, c); // a:9, b:23, c:8
return 0;
}

+= 的优先级 比+优先级低

2.求二进制中1个个数

1.法一

n=15
00000000000000000000000000001111
15 % 2 = 1
15 / 2 = 7
00000000000000000000000000000111

类似是获取1234每一位的值
%10/10得到十进制每一位
%2/2得到二进制每一位

#include <stdio.h>
int count_number_of_1(int n)
{
	int count = 0;
	//只要2进制里有1就不可能是0
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}
	return count;
}
int main()
{
	int n = 15;//n放在内存中补码的2进制中1的个数
	int ret = count_number_of_1(n);
	printf("%d\n", ret);//4
	return 0;
}

缺点:只适用正数

//如果测试-1呢?
10000000000000000000000000000001  原码
11111111111111111111111111111110  反码
11111111111111111111111111111111  补码

-1 % 2 = -1 不满足n%2 == 1的条件
商0余-1

改进:
形参写成unsigned int 就行了
无符号整型所有的位都是有效位
image-20220112143348511

2.法二

#include <stdio.h>
int count_number_of_1(int n)
{
	int count = 0;
	//只要2进制里有1就不可能是0
	for (int i = 0; i < 32; i++)
	{
		if ((n & 1) == 1)
		{
			count++;
		}
		n >>= 1;
	}
	return count;
}
int main()
{
	int n = -1;//n放在内存中补码的2进制中1的个数
	int ret = count_number_of_1(n);
	printf("%d\n", ret);//32
	return 0;
}

优化:

int NumberOf1(int n ) {
    // write code here
    int count = 0;
    for(int i = 0; i < 32; i++)
    {
        if(n & (1<<i))
        {
            count++;
        }
    }
    return count;
}

都得循环32次

3.法三

n = 15
n = n&(n-1)
00001111
&
00001110
00001110

00001110
&
00001101
00001100
...
每一次&都少了个1
n = n&(n-1)这个表达式会把n的二进制序列中最右边的1去掉
#include <stdio.h>
int count_number_of_1(int n)
{
	int count = 0;
	while (n)
	{
		n &= n - 1;
		count++;
	}
	return count;
}
int main()
{
	int n = -1;//n放在内存中补码的2进制中1的个数
	int ret = count_number_of_1(n);
	printf("%d\n", ret);//32
	return 0;
}

优点:有几个1就循环几次,不会浪费

2的n次方判断

一个数二进制只有1个1代表它是2的k次方

n = 800001000
&
00000111
00000000
可以借此判断一个数是否是2^k次方
if((n & n-1) == 0) ....则n就是2的k次方

3.求2个数二进制中不同的个数

1.法一

循环32次,把每一位都拿下来比较

#include <stdio.h>
int count_diff_bit(int m, int n)
{
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((m & 1) != (n & 1))
		{
			count++;
		}
		m >>= 1;
		n >>= 1;
	}
	return count;
}
int main()
{
	int n = 1999;
	int m = 2299;
	int ret = count_diff_bit(m, n);
	printf("%d\n", ret);//7
	return 0;
}

2.法二

  1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1
  2. 统计异或完成后结果的二进制比特位中有多少个1即可
#include <stdio.h>
int count_diff_bit(int m, int n)
{
	int count = 0;
	int tmp = m ^ n;
	while (tmp)
	{
		tmp &= tmp - 1;
		count++;
	}
	return count;
}
int main()
{
	int n = 1999;
	int m = 2299;
	int ret = count_diff_bit(m, n);
	printf("%d\n", ret);//7
	return 0;
}

4.打印整数二进制的奇数位和偶数位

不写函数返回类型,默认返回int
没有返回值要写void
如果不写return,会把最后一条指令执行结果返回过去

image-20220112145055551
image-20220112145319244

#include <stdio.h>
void print(int m)
{
	//00000000000000000000000000000101
	//打印奇数位
	int i = 0;
	for (i = 30; i >= 0; i -= 2)//第1位右移0位,第31位右移30位
	{
		printf("%d ", (m >> i) & 1);
	}
	printf("\n");
    
	//打印偶数位
	for (i = 31; i >= 1; i -= 2)//第2位右移1位,第32位右移31位
	{
		printf("%d ", (m >> i) & 1);
	}
}
int main()
{
	int m = 0;
	scanf("%d", &m);
	print(m);
	return 0;
}

5.算术转换

下面代码结果?

#include <stdio.h>
int i;//全局变量,默认为0
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0;
}

结果为>

sizeof操作符计算的结果的类型是size_t 无符号整型
i > sizeof(i)
int  > unsigned int
int会进行算术转换,提升为unsigned
10000000000000000000000000000001
11111111111111111111111111111110
11111111111111111111111111111111  补码
//4个字节放的是有符号数,打印的是原码的值  -1
如果认为内存中放的是无符号数,三码相同
所有的2进制位都是有效位
//32个1表示4294967295

image-20220112145854083

6.判断整数奇偶数(多组输入)

scanf在读取失败时会返回EOF
end of file文件结束标志
EOF本质是-1
-1补码32个全1
11111111111111111111111111111111
~(-1)
00000000000000000000000000000000

while (~scanf("%d", &n))也是对的
#include<stdio.h>
int main()
{
    int n = 0;
    while (scanf("%d", &n) != EOF)
        //scanf读取失败返回EOF
    {
        if (n % 2 == 0)
        {
            printf("Even\n");
        }
        else
        {
            printf("Odd\n");
        }
    }
    return 0;
}

7.缓冲区问题

法一:

用数组把要判断的字母存起来
scanf读取字符,从缓冲区中获取
键盘敲下a时还附带了一个\n
scanf获取了a,缓冲区还剩下\n
\n干扰了判断,得清理缓冲区

#include<stdio.h>
int main()
{
    char v[] = "AaEeIiOoUu";
    char ch = 0;
    int i = 0;
    while(scanf("%c",&ch) != EOF)
    {
        for(i = 0; i < 10; i++)
        {
            if(ch == v[i])
            {
                printf("Vowel\n");
                break;
            }
        }
        if(i==10)
        {
             printf("Consonant\n");//辅音
        }
        //清理缓冲区
        getchar();//清理\n
    }
    return 0;
}

法二:

while(scanf("%c\n",&ch) != EOF)

%c拿走字符时会把缓冲区中的\n也顺便拿走

法三:

%c前面加上空格,会跳过空白字符
\n属于空白字符,会被跳过

while(scanf(" %c",&ch) != EOF)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值