程序入门-数据类型-运算符-控制流

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码表

image
image

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 类型转换

  • 概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。
  • 转换模式:
    • 隐式转换:系统按照隐式规则自动进行的转换
    • 强制转换:用户显式自定义进行的转换
  • 隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度

image

隐式转换示例代码

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个数据位。

\[[-2^n,2^n-1] \]

这里n=7.

2.无符号char: 8个数据位。

\[[0,2^n-1] \]

这里n=8.

9.1.2补码转原码

负数的补码转原码的过程,实际上就是将补码转换为原码。具体的步骤如下:

  1. 补码转原码的规则:
    • 对于负数,先获取补码,然后通过以下步骤求出原码:
      1. 反转所有的位(即0变1,1变0),得到反码。
      2. 在反码的基础上加1,得到原码。
  2. 符号位:
    • 补码的符号位是第一个位,如果是1,表示负数;如果是0,表示正数。
    • 对于负数,其补码的符号位是1,原码的符号位也应该是1。

9.1.2.1举例说明

假设我们有一个8位的补码表示:11111011,我们要求它的原码。

  1. 补码是 11111011。
  2. 因为符号位是1,说明这是一个负数。
  3. 反转所有的位:11111011 → 00000100。
  4. 在反码的基础上加1:00000100 + 1 = 00000101。
  5. 所以,原码是 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.格式控制符

  • 格式控制符:
    1. 十进制整数:%d
    2. 八进制整数:%o
    3. 十六进制整数:%x
    4. 字符:%c
    5. 字符串:%s
    6. 单精度浮点数:%f
    7. 双精度浮点数:%lf
    8. 长双精度浮点数:%Lf
    9. 地址:%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 优先级与结合性

image

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");
}
  • 要点解析:
  1. switch(n) 语句中的 n 必须是一个整型表达式,即 switch 判断的数据必须是整型
  2. case 语句只能带整型常量,包括普通整型或字符,不包括 const 型数据。
  3. break 语句的作用是跳出整个 swtich 结构,没有 break 程序会略过case往下执行
  4. 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

img

posted @ 2025-04-29 11:12  cyt2730  阅读(129)  评论(0)    收藏  举报