程序入门-数据类型-运算符-控制流
1.#if 1
#if 1
#endif
2.开八线程编译
make -j8 //开8线程编译
3.mkdir
mkdir build //新建一个目录
4./mnt/hgfs含义
- hgfs 代表的是 "Host-Guest File System",即宿主机与虚拟机之间的文件系统。
- 当你通过 VMware 创建共享文件夹时,虚拟机内的操作系统会将这些文件夹挂载到
/mnt/hgfs下,以便你能够访问宿主机上的文件。
通常,虚拟机启动时会自动挂载这些共享文件夹到 /mnt/hgfs目录
---mnt 其实是 mount(挂载)的缩写。
5.ASCII码表


6.scanf的注意点
6.1.注意1:要按格式输入
- scanf() 与 printf() 不同,scanf() 的格式控制串不可乱写,尤其是结尾处的 \n
- 用户必须完全按照 scanf() 中描述的输入格式控制串来输入数据,否则将出错。
- 示例:
// 此处输入时必须带逗号
scanf("%d,%d", &a, &b);
// 此处必须先输入a=,然后才能输入整数
scanf("a=%d", &a);
// 此处结束输入时按下的回车符将被scanf()误以为格式控制符,无法正常结束输入
scanf("%d\n", &a);
6.2.注意2:scanf()返回值
- scanf() 的返回值,代表成功从键盘读取的数据的个数
- 无法匹配 scanf() 指定格式的数据,将被遗留在输入缓冲区中,不会消失
- 示例:
// scanf() 试图从键盘读取两个整数
// 返回值 n 代表成功读取的个数,比如:
// 输入100 200,则 n 将等于2
// 输入100 abc,则 n 将等于1
// 输入abc xyz,则 n 将等于0;输入abc 200,n也将等于0
int n = scanf("%d%d", &a, &b);
// 根据 scanf() 的返回值,判断用户是否输入了正确的格式
while(n != 2)
{
// 需要清空缓冲区并提示用户重新输入
char s[50];
fs(s, 50, stdin);
printf("请重新输入两个整数\n");
n = scanf("%d%d", &a, &b);
}
7.true和false头文件<stdbool.h>
布尔型数据只有真、假两种取值
bool a=true/false 必须添加#include<stdbool.h>
在c语言中非0表示真, 0表示假
8.数据类型
8.1 数据类型大小
| 数据类型 | 标准说明 | 大小(字节) |
|---|---|---|
char |
存储一个字符,至少 1 字节 | 1 字节 |
short |
短整数类型,至少 2 字节 | 2 字节 |
int |
整数类型,至少 2 字节 | 4 字节 |
long |
长整数类型,至少 4 字节 | 4 字节(32 位)或 8 字节(64 位) |
long long |
长长整数类型,至少 8 字节 | 8 字节 |
float |
单精度浮点数 | 4 字节 |
double |
双精度浮点数 | 8 字节 |
long double |
扩展双精度浮点数 | 12 或 16 字节 |
void * |
指针类型(指向任意类型的指针) | 4 字节(32 位)或 8 字节(64 位) |
8.2 类型转换
- 概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。
- 转换模式:
- 隐式转换:系统按照隐式规则自动进行的转换
- 强制转换:用户显式自定义进行的转换
- 隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度

隐式转换示例代码
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
- 强制转换:用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - (int)c; // 在该表达式中a隐式自动转换为int,c被强制转为int
不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。
作业-隐式转换:
执行 int a=5, b=2; float c = a / b; 后,c 的值为(C)
A. 2.5
B. 2
C. 2.0
D. 3
9.内存存储
9.1 整型数
有符号数据和无符号数据。
有符号数据的最高位是符号位,1表示负数,0表示正数。
在内存中以补码形式存储。正数的原码与补码一致,负数的补码为原码除了符号位取反后+1,即符号位不取反。
9.1.1 数据类型取值范围
有符号数据与无符号数据取值范围不同。
1.有符号char:1个符号位,7个数据位。
这里n=7.
2.无符号char: 8个数据位。
这里n=8.
9.1.2补码转原码
负数的补码转原码的过程,实际上就是将补码转换为原码。具体的步骤如下:
- 补码转原码的规则:
- 对于负数,先获取补码,然后通过以下步骤求出原码:
- 反转所有的位(即0变1,1变0),得到反码。
- 在反码的基础上加1,得到原码。
- 对于负数,先获取补码,然后通过以下步骤求出原码:
- 符号位:
- 补码的符号位是第一个位,如果是1,表示负数;如果是0,表示正数。
- 对于负数,其补码的符号位是1,原码的符号位也应该是1。
9.1.2.1举例说明
假设我们有一个8位的补码表示:11111011,我们要求它的原码。
- 补码是
11111011。 - 因为符号位是1,说明这是一个负数。
- 反转所有的位:
11111011→00000100。 - 在反码的基础上加1:
00000100+ 1 =00000101。 - 所以,原码是
10000101,表示-5。
9.1.2.2总结
- 对于负数,补码转原码的过程是反转所有位后加1。
- 对于正数,补码和原码是相同的,符号位是0。
9.2 浮点数
9.2.1. 浮点数转二进制
整数部分除2,小数部分乘2
例如:0.25
计算过程:0.25*2=0.5---->0(十分位)
0.5*2=1.0------>1(百分位)
0.25(10)=0.01(2)
小数部分变成0后不再计算。
C语言中的浮点数在内存中的存储遵循IEEE 754标准,通常使用32位单精度浮点数或64位双精度浮点数。这些浮点数通过三部分来表示:符号位、指数部分和尾数部分。以下是具体的存储方式:
9.2.2. 单精度浮点数(32位)
32位浮点数由三部分组成:
- 符号位(1位):表示数字的符号,0表示正数,1表示负数。
- 指数(8位):指数是偏移量表示的,采用偏移二进制表示法,即实际指数加上一个偏移值(对于单精度浮点数是127)。
- 尾数(23位):表示浮点数的有效数字,实际存储的是标准化后的尾数(通常是1.xxxxx的形式,但在存储时省略了最前面的1)。
具体存储格式(32位):
| 符号位 | 指数(8位) | 尾数(23位) |
|---|---|---|
| 1 bit | 8 bits | 23 bits |
- 符号位:0表示正数,1表示负数。
- 指数:8位无符号整数表示,实际指数 = 存储的指数值 - 127。
- 尾数:表示的是一个有效数字,存储的是去掉前导1的部分。
例如,浮点数-6.75的表示:
- 符号位:1(负数)
- 6.75的二进制表示:
110.11,标准化为1.1011 × 2^2。 - 指数部分:2 + 127 = 129(
10000001)。 - 尾数部分:
10110000000000000000000(去掉前面的1,剩余1011后填充零)。
9.2.3. 双精度浮点数(64位)
双精度浮点数有64位,组成如下:
- 符号位(1位):和单精度相同,表示符号。
- 指数(11位):采用偏移二进制表示法,实际指数 + 1023。
- 尾数(52位):表示浮点数的有效数字,和单精度一样,存储的是标准化后的尾数(省略前导1)。
具体存储格式(64位):
| 符号位 | 指数(11位) | 尾数(52位) |
|---|---|---|
| 1 bit | 11 bits | 52 bits |
- 符号位:0表示正数,1表示负数。
- 指数:11位无符号整数,实际指数 = 存储的指数值 - 1023。
- 尾数:表示的是一个有效数字,存储的是去掉前导1的部分。
9.2.4. 示例
假设我们有一个双精度浮点数 -13.25,其存储过程如下:
- 将
13.25转换为二进制,得到1101.01,标准化为1.10101 × 2^3。 - 符号位为 1(负数)。
- 指数部分:3 + 1023 = 1026,二进制表示为
10000000010。 - 尾数部分:去掉前面的1,剩下
1010100000000000000000000000000000000000000000000000(52位)。
最终,-13.25 在64位双精度浮点数中的存储如下:
- 符号位:1
- 指数:
10000000010 - 尾数:
1010100000000000000000000000000000000000000000000000
9.3 特殊值
- 正零(+0):符号位是0,指数是0,尾数是0。
- 负零(-0):符号位是1,指数是0,尾数是0。
- 无穷大(Infinity):符号位可以是0或1,指数是全1,尾数是全0。
- NaN(Not-a-Number):符号位可以是0或1,指数是全1,尾数不是全0。
总结来说,C语言的浮点数是通过符号位、指数和尾数的组合来表示的,使用偏移量的方式存储指数,尾数部分是去除前导1后的有效数字。
10.格式控制符
- 格式控制符:
- 十进制整数:%d
- 八进制整数:%o
- 十六进制整数:%x
- 字符:%c
- 字符串:%s
- 单精度浮点数:%f
- 双精度浮点数:%lf
- 长双精度浮点数:%Lf
- 地址:%p
| 序号 | 格式控制符 | 对应数据类型 | 备注 |
|---|---|---|---|
| 1 | %d | int | 整型数据 |
| 2 | %u | unsigned int | 无符号整型数据 |
| 3 | %ld | long int | 长整型数据 |
| 4 | %lu | unsigned long int | 无符号长整型数据 |
| 5 | %hd | short int | 短整型数据 |
| 6 | %hu | unsigned short int | 无符号短整型数据 |
| 7 | %lld | long long int | 长长整型数据 |
| 8 | %llu | unsigned long long int | 无符号长长整型数据 |
| 9 | %hhd | char | 单字节整型数据 |
| 10 | %hhu | unsigned char | 无符号单字节整型数据 |
| 11 | %c | char | 字符 |
| 12 | %s | char * | 字符串 |
| 13 | %f 或 %e | float | 单精度浮点数 |
| 14 | %lf 或 %le | double | 双精度浮点数 |
| 15 | %Lf 或 %Le | long double | 长双精度浮点数 |
| 16 | %p | void * | 指针 |
| 17 | %.ns | char * | 取字符串前n个字符,n要改为具体数字 |
| 18 | %.nf | float | 保留n位有效数字,n要改为具体数字 |
| 19 | %x | unsigned 或 int | 十六进制整型数据 |
| 20 | %o | unsigned 或 int | 八进制整型数据 |
| 21 | %#x | unsigned 或 int | 十六进制整型数据,带0x前缀 |
| 22 | %#o | unsigned 或 int | 八进制整型数据,带0前缀 |
11.运算符
11.1.算术运算符
- 取模运算要求左右两边操作数必须是整型数据。
- 自加和自减运算不仅可以对整型操作,也可以对浮点数、指针操作。
11.1.1 作业-自增
分析下面代码输出结果
int i=1, j=2, k=3;
printf("%d %d %d\n", i++, ++j, i++ + ++k);
(1) 先i+++ ++k:
i自增前与自增后的k相加,即i=1时与k+1后的k=4相加,
即i+k=1+4=5.输出5.
然后i自增,即i+1=2.
(2) 然后++j:
j自增后输出,输出j+1=3.
(3) 最后i++
先输出i=2,然后i自增,即i+1=3.
总结,输出2 3 5.
11.2.逻辑运算符
- 特殊规则:
- 在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
- 在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。
11.3位运算符⭐
| 运算符 | 名称 | 举例 | 功能说明 |
|---|---|---|---|
| ~ | 位逻辑反 | ~a | 将变量 a 中的每一位取反 |
| & | 位逻辑与 | a & b | 将变量 a 和 b 逐位进行与操作 |
| | | 位逻辑或 | a | b | 将变量 a 和 b 逐位进行或操作 |
| ^ | 位逻辑异或 | a ^ b | 将变量 a 和 b 逐位进行异或操作 |
| << | 左移 | a << 4 | 将变量 a 中的每一位向左移动4位 |
| >> | 右移 | x >> n | 将变量 x 中的每一位向右移动4位 |
⭐注意:
11.3.1. ~a=-a-1
11.3.2. &可提取最低位:判断奇偶
n & 1 可以判断 n 的最低位是否为 1。它的效果是提取 n 的最低位(即判断 n 是否是奇数)。
if (n & 1) {
// n 是奇数
} else {
// n 是偶数
}
11.3.3 &可清除最低位:判断2的幂
n & (n - 1) 可以将 n 的最低位清零。如果 n 是一个幂次的 2(例如,1, 2, 4, 8...),那么这个操作会非常有用。例如,如果你想检查一个数是否是 2 的幂,可以通过 n & (n - 1) 来判断。
if (n & (n - 1) == 0) {
// n 是 2 的幂
}
11.3.4 如何判断第几位
位编号:31 30 29 28 ... 3 2 1 0
二进制:1 0 0 1 ... 0 0 1 1
11.3.5 同或、异或、或的区别
⭐注意:同或相当于对异或(XOR)结果进行取反操作。
| 操作符 | 异或(XOR) | 同或(XNOR) | 或(OR) |
|---|---|---|---|
| 结果 | 当两个操作数不同时,结果为 1;相同时,结果为 0 | 当两个操作数相同时,结果为 1;不同时,结果为 0 | 当两个操作数有至少一个为 1 时,结果为 1 |
| 逻辑 | ⭐只有操作数不同时结果为 1 | ⭐只有操作数相同时结果为 1 | 只要有一个操作数为 1,结果就为 1 |
| 符号 | ^ (异或) |
~(a ^ b) 或直接使用 XNOR (不常见) |
| |
| 应用 | 常用于加密、校验、差异计算 | 主要用在反向比较、逻辑判断等场合 | 常用于判断是否有真值(至少一个为真) |
11.3.6 移位运算
移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。
11.3.7 练习-置零、置位
假设有一个无符号32位整型数据
unsigned int data=0x12ff0045;
请编写一个程序用位运算把data的第14、15位置位(即赋值为1),把22、23位置零(即赋值为0), 并且输出修改后的数据。
解答:
本题考察基本位运算逻辑,要把某几位置位,只需将数据与对应掩码进行位或运算即可;要把某几位置零,只需将数据与对应掩码取反位与即可。参考代码如下:
#include <stdio.h>
#include <stdint.h>
// ⭐第14、15位掩码:
uint32_t mask1 = 0x3<<14; // 0000 0000 0000 0000 1100 0000 0000 0000
// ⭐第22、23位掩码:
uint32_t mask2 = 0x3<<22; // 0000 0000 1100 0000 0000 0000 0000 0000
int main(void)
{
unsigned int data = 0x12ff0045;
// 将bit[13]、bit[14]置位
data |= mask1;//⭐
printf("%x", data);
// 将bit[22]、bit[23]置零
data &= ~mask2;//⭐
printf("%x",data);
return 0;
}
11.3.8 作业-使用位运算符实现变量值交换
定义变量 int x=5, y=3;,使用位运算符实现 x 和 y 的值交换(不借助临时变量)
#include <stdio.h>
int main()
{
int x = 5, y = 3;
x = x ^ y;
y = x ^ y;//y=x^y^y;此时y=x
x = x ^ y;//x=x^y^x;此时x=y
printf("%d, %d\n", x, y);
return 0;
}
11.4 特殊运算符
11.4.1 赋值运算符
可以连续赋值,顺序从右到左
// 连续赋值
a = b = 50; // 先将 50 赋给 b,再将 b 的值赋给 a,正确
11.4.2 复合赋值符
-
- 当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率
- 除了下述10个复合运算符之外,生造别的复合运算符是非法的
// 加减乘除:
a += n; // 等价于 a = a+n;
a -= n; // 等价于 a = a-n;
a *= n; // 等价于 a = a*n;
a /= n; // 等价于 a = a/n;
// 求余:
a %= n; // 等价于 a = a%n;
// 位运算:
a &= n; // 等价于 a = a&n;
a |= n; // 等价于 a = a|n;
a ^= n; // 等价于 a = a^n;
a >>= n; // 等价于 a = a>>n;
a <<= n; // 等价于 a = a<<n;
11.4.3 条件运算符
- 唯一需要三个操作数的运算符
- 语法:表达式1 ? 表达式2 : 表达式3
- 释义:当表达式1为真时,取表达式2,否则取表达式3
- 举例:
int a = 100;
int b = 200;
int m = (a>b) ? a : b; // 如果 a>b 为真,则 m 取 a 的值,否则取 b 的值
11.4.4 sizeof运算符
- 含义:计算指定数据类型或者变量所占据内存的字节数
- 语法:sizeof(类型) 、sizeof(变量) ,计算变量的字节数时圆括号可以省略
- 举例:
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long double));
int a[5];
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);//⭐
⭐小细节:
int a = 10;
printf("%d\n", sizeof(a++));//4,sizeof不会进行运算
printf("%d\n", a);//10
11.4.5 优先级与结合性

11.4.6 return运算符
返回 void 类型
如果函数声明为 void 类型,则 return 语句后面不能有任何值。return 语句仅用于退出函数。
// 定义一个返回 void 类型的函数
void printMessage() {
printf("Hello, World!\n");
return; // 返回语句用于退出函数,不返回任何值
}
12.控制流
12.1 多路分支switch
- 语法:
switch(n)
{
case 1:
printf("one\n");
break;
case 2:
printf("two\n");
break;
default:
printf("其他数字\n");
}
- 要点解析:
- switch(n) 语句中的 n 必须是一个整型表达式,即 switch 判断的数据必须是整型
- case 语句只能带整型常量,包括普通整型或字符,不包括 const 型数据。
- break 语句的作用是跳出整个 swtich 结构,没有 break 程序会略过case往下执行
- default 语句不是必须的,一般放在最后面(因此不需要 break)
12.2 goto语句
- 逻辑:无条件跳转
- 示例:
int main()
{
printf("%d\n", __LINE__); // 打印第3行
// 无条件跳转到label处
goto label;
printf("%d\n", __LINE__); // 打印第7行,此处被略过
label: //⭐
printf("%d\n", __LINE__); // 打印第9行
}
- 语法:
- goto语句直接跳转到本代码块中的标签处
- ⭐标签指的是以冒号结尾的标识符
- 作用:
- goto语句的无条件跳转不利于程序的可读性,一般不建议使用
- goto语句常被用在程序的错误处理中
练习:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
bool lcd_init()
{
int data = random()%100;
if(data%2==0) return true;
else return false;
}
void lcd_destroy()
{
printf("销毁lcd\n");
}
bool ts_init()
{
int data = random()%100;
if(data%2==1) return true;
else return false;
}
void ts_destroy()
{
printf("销毁ts\n");
}
void fun(){
//1初始化显示屏
if(lcd_init()==false)
goto lcderr;
//2初始化触摸
if(ts_init()==false)
goto tserr;
printf("正常执行\n");
ts_destroy();
printf("ts销毁\n");
tserr:
lcd_destroy();
printf("lcd销毁\n");
lcderr:
printf("退出\n");
}
int main(void)
{
srand(time(NULL)); // 设置随机种子
fun();
return 0;
}
为什么ts初始化要销毁lcd:
为了避免资源泄漏(resource leak)。
分析执行流程
假设函数在第 1 步成功,lcd 初始化成功,那么程序会继续往下执行第 2 步。
如果 ts 初始化失败:
- 会执行
goto tserr,跳转到tserr标签。 tserr:下面首先执行的是lcd_destroy(),也就是说虽然 ts 初始化失败,但 lcd 已经成功初始化了,要记得销毁它,防止资源泄漏。- 然后会打印 lcd 销毁的日志,继续走到
lcderr:,打印退出。
不带goto的写法
void fun() {
bool lcd_initialized = false;
bool ts_initialized = false;
// 1. 初始化显示屏
if (lcd_init()) {
lcd_initialized = true;
// 2. 初始化触摸
if (ts_init()) {
ts_initialized = true;
printf("正常执行\n");
}
}
// 释放资源(按初始化成功的情况来判断)
if (ts_initialized) {
ts_destroy();
printf("ts销毁\n");
}
if (lcd_initialized) {
lcd_destroy();
printf("lcd销毁\n");
}
printf("退出\n");
}
13.共享文件夹重启生效
解决办法:
sudo vi /etc/fstab
在文件中最后一行添加
.host:/ /mnt/hgfs/ fuse.vmhgfs-fuse allow_other 0 0
本文来自博客园,作者:cyt2730,转载请注明原文链接:https://www.cnblogs.com/cytwjyy/p/18853302

浙公网安备 33010602011771号