文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/66b0acac6d8e4e24bacf55ebda2a6d60.png)
🌈反斜杠 \
🌳续行符
- 代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
if (1 == a && 2 == b && 3 == c)
{
printf("you can see me\n");
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
if (1 == a && \
2 == b && \
3 == c)
{
printf("you can see me\n");
}
return 0;
}
- 作用
👉续行符就是为了续行。如果一行代码太长,我就可以换到下一行去写。
👉 在用户看来:新起了几行。
在编译器看来:仍然是一行。
- 注意
👻在换行符之后不要出现任何符号,包括空格在内。也就是\之后直接回车
- 疑问
Q:有人说不用 \ 直接换行也行?就像这样
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
if (1 == a &&
2 == b &&
3 == c)
{
printf("you can see me\n");
}
return 0;
}
答:这个问题就好像你不加返回值(编译器就会默认返回值为int)代码照样能够运行一样。你直接换行的话,当别人看你代码时,他会产生疑惑:你是写错了,还是刻意这样。所以你加上\,就是告诉他:我这里就是嫌代码长,需要换行方便阅读。
🌳转义字符
🍁字面转特殊
- 代码
int main()
{
printf("hello n world");
return 0;
}
int main()
{
printf("hello \n world");
return 0;
}
👉 对比两者,可以知道 字面值 n --> 转义字符 \n.这就是字面转特殊
🍁特殊转字面
- 代码
int main()
{
printf("\"");
return 0;
}
👉功能为打印双引号。双引号作为字符串分隔符本身就看作特殊字符。我们加上\来获得字面意思。
🌳回车和换行符
- 基本概念
👉回车是指光标回到当前位置的最开始。
👉换行是指光标移至下一行
🤟 它们两者是不一样的概念。
- 有趣的代码
1.旋转光标(VS下)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
int main()
{
char* str = "/-|\\";//这里为了打印\,在\之前又加了\以获得字面值
int num = 0;
while (1)
{
num %= 4;//4次算一个周期
printf("%c\r", str[num]);
Sleep(300);//每次打印暂停300毫秒
num++;
}
return 0;
}
2.倒计时
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
int main() {
int i = 10;
while (i--)
{
printf("[%2d]\r", i);
Sleep(300);
}
printf("\n");
return 0;
}
🌈 单引号和双引号
- 代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
//int main()
int main()
{
printf("%d\n", sizeof(1));//4
printf("%d\n", sizeof("1"));//2
printf("%d\n",sizeof(""));//1
printf("%d\n", sizeof('1'));//4 为啥不是1?
char c = '1';
printf("%d\n", sizeof(c));//1
return 0;
}
🪐 在C99中,像 '1’整形字符常量,它会被编译器看成整形。
🛰️所以 char a = ‘1’;会发生截断。4字节数据会写入到1字节空间去。
🌈逻辑运算符和位运算符
🌳逻辑运算符
- &&
🚀级联两个(多个)逻辑表达式,必须同时为真,结果才为真
🚀什么是级联?
答:多个对象之间的映射关系
- ||
🚀级联两个(多个)逻辑表达式,必须至少一个为真,结果才为真
- 短路
Q:什么是短路
答:上面的一个条件不满足,已经不需要在看后续的条件的情况,
就叫做短路
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
int show()
{
printf("you can see me\n");
return 1;
}
int main()
{
int a = 0;
scanf("%d", &a);
10 == a && show();
return 0;
}
📡 对于&&短路,从左向右看,只要有一个结果不为真,后面就不用看了!最后结果就是假。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
int show()
{
printf("you can see me\n");
return 1;
}
int main()
{
int a = 0;
scanf("%d", &a);
10 == a || show();
return 0;
}
📡对于||短路,从左向右看,只要有一个结果为真,那么后面也不用看了!整体结果就是真。
🌳位运算符
- &
☘️二进制位遇0则为0。
- |
☘️二进制位遇1则为1。
- ^
☘️二进制位相同为0,相异为1。
☘️技巧
a^a = 0
a^0 = a
☘️支持交换律和结合律
🌳&&和&区别
☘️ &&:级联的是多个逻辑表达式,需要的是真假结果
☘️& :级联的是多个数据,逐比特位进行位运算
☘️逻辑结果:吃了吗?
☘️数据结果:吃了多少?
🌳交换两个数(不使用第三变量)
- 方法一:
int main()
{
int a = 10;
int b = 20;
a = a + b;//a == 30 ,b == 20
b = a - b;//30-20 == 10
a = a - b;//30-10 == 20
return 0;
}
🍃当数据很大就会有栈溢出问题。
为啥呢?
因为溢出的本质就是比特位增多,而加法就可能导致进位,使得比特位增多
比如 10 + 90,它们都是两位数。
结果为 100(三位数)
- 方法二
int main()
{
int a = 10;
int b = 20;
printf("before:a = %d,b = %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("after:a = %d,b = %d\n", a, b);
return 0;
}
🍃这样写就避免了栈溢出的问题。
因为 ^ 不会产生进位。
- 补充
🍃溢出(进位)其实都会被记录在寄存器的比特位当中。当你写回内存时,才会发生第二个问题。也就是截断(内存空间不足)!
🌳两个测试代码
- demo1:
🍂位操作需要用宏定义好后再使用(推荐)
- 代码一(将指定比特位设置为1)
#define SETBIT(x,n) ((x)|= (1<<(n-1)))//将指定比特位设置为1
//打印32个比特位
void ShowBits(int x)
{
int num = sizeof(int) * 8 - 1;//31
while (num >= 0)
{
if (x & (1 << num))
{
printf("1");
}
else
{
printf("0");
}
num--;
}
printf("\n");
}
int main()
{
int x = 0;
printf("before");
ShowBits(x);
SETBIT(x, 2);
printf("after:");
ShowBits(x);
return 0;
}
- 代码二(将指定比特位设置为0)
#define CLRBIT(x,n) ((x)&=~(1<<(n-1)))//将指定比特位设置为0
//打印所有比特位
void ShowBits(int x)
{
int num = sizeof(int) * 8 - 1;//31
while (num >= 0)
{
if (x & (1 << num))
{
printf("1");
}
else
{
printf("0");
}
num--;
}
printf("\n");
}
int main()
{
int x = -1;
printf("before");
ShowBits(x);
CLRBIT(x, 2);
printf("after:");
ShowBits(x);
return 0;
}
🌳一个整形提升问题
int main()
{
char c = 0;
printf("sizeof(c): %d\n", sizeof(c)); //1
printf("sizeof(c): %d\n", sizeof(~c)); //4
printf("sizeof(c): %d\n", sizeof(c << 1)); //4
printf("sizeof(c): %d\n", sizeof(c >> 1)); //4
return 0;
}
🍂无论任何位运算符,目标都是要计算机进行计算的,而计算机中只有CPU具有运算能力(先这样简单理解),但计算的数据,
都在内存中。故,计算之前(无论任何运算),都必须将数据从内存拿到CPU中,拿到CPU哪里呢?毫无疑问,在CPU 寄存器
中。
而寄存器本身,随着计算机位数的不同,寄存器的位数也不同。一般,在32位下,寄存器的位数是32位。
可是,你的char类型数据,只有8比特位。读到寄存器中,只能填补低8位,那么高24位呢?
就需要进行“整形提升”。
🍂也就是说像c<<1这样的表达式结果是 int类型。发生了整型提升!
🍂sizeof(!c)在VS中的结果为1,但在Linux中结果为4。
🌈 左移和右移
🌳基本概念
👻<<(左移): 最高位丢弃,最低位补零
👻>>(右移):
1. 无符号数:最低位丢弃,最高位补零[逻辑右移]
2. 有符号数:最低位丢弃,最高位补符号位[算术右移]
🌳代码
- demo1
int main()
{
//左移
unsigned int a = 1;//二进制位:0000 0000 0000 0000 0000 0000 0000 0001 -->1
printf("%u\n", a << 1);// 0000 0000 0000 0000 0000 0000 0000 0010 -->2
printf("%u\n", a << 2);// 0000 0000 0000 0000 0000 0000 0000 0100 -->4
printf("%u\n", a << 3);// 0000 0000 0000 0000 0000 0000 0000 1000 -->8
//逻辑右移
unsigned int b = 100;// 二进制位:0000 0000 0000 0000 0000 0000 0110 0100-->100
printf("%d\n", b >> 1);// 0000 0000 0000 0000 0000 0000 0011 0010-->50
printf("%u\n", b >> 2);// 0000 0000 0000 0000 0000 0001 0001 1001-->25
printf("%u\n", b >> 3);// 0000 0000 0000 0000 0000 0011 0000 1100-->12
//算术右移,最高位补符号位1, 虽然移出了最低位1,但是补得还是1
int c = -1;//二进制位: 1111 1111 1111 1111 1111 1111 1111 1111
printf("%d\n", c >> 1);// 1111 1111 1111 1111 1111 1111 1111 1111
printf("%d\n", c >> 2);// 1111 1111 1111 1111 1111 1111 1111 1111
printf("%d\n", c >> 3);// 1111 1111 1111 1111 1111 1111 1111 1111
return 0;
}
- demo2
int main()
{
//是算术右移,还是逻辑右移?最高位补0,为何?
unsigned int d = -1;
printf("%d\n", d >> 1);
printf("%d\n", d >> 2);
printf("%d\n", d >> 3);
return 0;
}
👻那是补0还是补1呢?
答:补0.因为判定依据:看自身类型,和变量的内容无关。
🌈 结尾
今天就分享到这,希望对你有帮助😊😊😊