几个知识点:1.如何用printf打印百分号% 2.一道很坑的递归题 3.关于 非(感叹号) | ^ ~ & || &&,即位操作和逻辑与、逻辑或 4.左移<<和>>右移这两个移位操作符

愚人节快乐!

好久不更新博客了(因为懒得更),今天凌晨刚刚完结C语言的学习(指入门),晚上躺床上刷qq的时候在群里看到了几个很有意思的题,发出来给大家看看:

1.如何用printf打印百分号%?

直接上代码:

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

运行结果:

正确方法就是打俩 %

2.一道很坑的递归题:

void a(int w)
{
	if (w == 0)
		return;
	else
		printf("%d", w);
	a(w--);
}
int main()
{
	a(3);
}

运行结果是什么?请给出你的答案(答案会在文章末尾公布)

3.关于! | ^ ~ & || &&:

// & | ^ ~是位操作符
// & - 按二进制位(补码)与 --- 两个同时为1才为1,有一个为0则为0
// | - 按二进制位(补码)或 --- 两个有一个为1则为1,同时为0才为0
// ^ - 按二进制位(补码)异或 --- 相同为0,相异为1
// ~ - 按二进制位(补码)取反 --- 0变1,1变0


//逻辑与 && 逻辑或 ||        
// && 相当于 并且    
// || 相当于 或者
int main()
{ 
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++;		//只要&&之前判断为0,那么就不再进行后面的任何操作
	//i = a++ || ++b || d++;		//只要||之前判断为非0,那么就不再进行后面的操作
	printf("%d %d %d %d", a, b, c, d);
}


//!((a != 0) && (b != 0) && (c != 0))  等价于  a == 0 || b == 0 || c == 0
//类似于高中数学里的逆否命题
//做题时遇到括号外有 ! ,开括号的时候就把括号里的 && 变成 || ,同理 ||变成 &&,并且==变成!=,!=变成==

//对任一数值 x 进行按位非操作(~)的结果为 - (x + 1)

想记笔记的朋友可以直接把上面这段代码复制过去,省的写了

(毕竟这些本来就是我从笔记里复制过来的)

4.左移<<和>>右移这两个移位操作符:

这两个讲起来相对比较复杂

首先移位分为两种方式

一种是逻辑移位:用0补齐

一种是算术移位:左移时用0补齐,右移时整数用0补齐,负数用1补齐

这么讲可能比较抽象,下面分别讲左移右移:

  左移:

              高位舍弃,低位补0(符号位不变)

    5 << 2

    00000101   --->   00010100    由5变为20

    左移k位相当于扩大2^k倍

  右移:

    无符号数是逻辑移位,有符号数采用逻辑移位还是算术移位由编译器决定

    右移相当于缩小2^k倍

对于左移,无论使用逻辑移位还是算术移位都是高位舍弃,低位补0,没有区别。

而对于右移,当对象是无符号数的时候,没有符号位,则遵循逻辑移位:低位舍弃,高位补0即可

但当对象是有符号数的时候,有符号位,这时可能是逻辑移位,也有可能是算术移位(由编译器决定)

如果是逻辑移位,则低位舍弃,高位补0(这时如果对象是负数,会发生符号的变化)

如果是算术移位,则低位舍弃,对象为正数时高位补0,为负数时高位补1(这时对符号没有影响)。

注:通常见到的右移都是算术右移

注:1.采用有符号数右移的程序不可移植(因为不同编译器结果可能不同)

  2.移位负数位属于未定义行为,由编译器决定(如a<<-2)

       3.移位操作符只能用于整数

 很晚了,我困了,懒得打字了,直接从笔记里复制一段过来,大家自己悟吧,加油!

//int main()
//{
//	int a = 16;
//	//	>>	右移操作符
//	//移动的是二进制位
//	//00000000000000000000000000100000
//	//00000000000000000000000000010000
//	int b = a >> 1;
//	printf("%d\n", b);
//
//  //负数情况
//	a = -1;
//	b = a >> 1;
//	printf("%d\n", b);
//	//整数的二进制表示有:原码,反码,补码
//	//存储到内存的是补码
//  //-1
//	//10000000000000000000000000000001 - 原码
//	//11111111111111111111111111111110 - 反码:符号位不变,其他位按位取反
//	//11111111111111111111111111111111 - 补码:反码加1
//	//11111111111111111111111111111111 - 右移之后,左边补1,仍然不变
//	//算术右移:右边丢弃,左边补原符号位	//通常见到的右移都是算术右移
//	//逻辑右移:右边丢弃,左边补0
//}

//1
//00000000000000000000000000000001
//01111111111111111111111111111110
//01111111111111111111111111111111
//错!如果一个数是正数,那么它的原码,反码,补码相同

//0
//00000000000000000000000000000000
//01111111111111111111111111111111
//00000000000000000000000000000000
//0也应该算正数吧..

//-0
//10000000000000000000000000000000
//11111111111111111111111111111111
//00000000000000000000000000000000//进位1被丢弃

//在计算机系统中,数值一律用补码来表示(存储) 
//主要原因:
//使用补码,可以将符号位和其它位统一处理
//减法也可按加法来处理
//两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃

//int main()
//{
//	//	<<	左移操作符
//	//左边丢弃,右边补0(符号位不变)
//	int a = 5;
//	int b = a << 1;
//	printf("%d\n", b);
//	//负数情况
//	a = -1;
//	b = a << 1;
//	printf("%d\n", b);
//	
//}

建议复制到编译器上自己跑一跑,更易于理解

还有一个点:

//左移里一个比较特殊的情况是当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位,如:
//
//int i = 1, j = 0x80000000; //设int为32位
//i = i << 33;   // 33 % 32 = 1 左移1位,i变成2
//j = j << 33;   // 33 % 32 = 1 左移1位,j变成0,最高位被丢弃

//需要注意的一个问题是int类型最左端的符号位和移动出去的情况,我们知道,int是有符号的整型数,左端的1位是符号为,即0为正1为负,那么用移位的时候就会出现溢出,如:
//int i = 0x40000000;//16进制的40000000,为2进制的01000000...0000
//i = i << 1;
//那么,i在移动1位之后就会变成0x80000000,也就是2进制的100000...0000,符号位被置1,起位全是0,变成了int类型所能表达的最小值,32为的int这个值是-2147483648,溢出,如果在接着把i左移1位会出现什么情况呢?
//在c语言都采用了丢弃最高位的处理方法,丢弃了1之后,i的值变成了0

对了,差点忘了,那道递归题的答案:

//会死循环                                                                                  因为后置--传参相当于没减

可恶啊,标题里不让打 ! ,搞得我得用 非(感叹号) 来代替 !

哦,刚刚点进了CSDN创作规范,发现大家都在骂标题规范写作助手。

那没事了。

感谢阅读,如有学术问题敬请纠正。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值