KyrieChao

2026-08-10 周一 · 阶段 0 & 1 完成

2026-08-10

技术

C 语言

学习内容:变量的作用域和生存期

理论要点

变量类型 关键字 作用域 生存期 默认初值
非静态局部变量 (默认) 代码块内 代码块执行期间 随机值
静态局部变量 static 代码块内 程序整个执行期间 0
非静态全局变量 (默认) 整个项目 程序整个执行期间 0
静态全局变量 static 当前文件 程序整个执行期间 0

两个原则:作用域尽量小、生存期尽量短。能用局部不用全局,能用非静态不用静态。

多文件编程基础:.h 放声明 + extern 全局变量,.c 放实现,编译时所有 .c 一起编译。

今天写的代码

1. 统计二进制位中 1 的个数(两种实现):

// 方法一:逐位检查
int counter_binary(char *p, int n) {
    int count = 0;
    for (int i = 0; i < n; i++) {
        unsigned char byte = p[i];
        for (int k = 7; k >= 0; k--) {
            if (byte & (1 << k)) count += 1;
        }
    }
    return count;
}

// 方法二:查表法(每半个字节查一次,O(n) 但常数小)
int counter_binary2(const unsigned char *p, int n) {
    int count = 0;
    const int popcount[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
    for (int i = 0; i < n; i++) {
        unsigned char byte = p[i];
        count += popcount[byte & 0x0F];   // 低 4 位
        count += popcount[byte >> 4];      // 高 4 位
    }
    return count;
}

查表法用空间换时间,一个字节拆成两个 nibble(半字节),每个查一次 16 元素的表。

2. 冒泡排序、strcpystrcmp 手写实现

3. sizeof 与指针运算:

char str[] = "memory";
char *p = str + 1;
printf("%lu\n", sizeof(str));  // 7(数组,包含 '\0')
printf("%lu\n", sizeof(p));    // 4(指针,32 位平台)
printf("%lu\n", sizeof(a));    // 8(struct:char 1 + 对齐 3 + int 4)

unsigned char *p1 = (unsigned char *)0x80100000;
printf("%x\n", p1 + 5);        // 0x80100005

Rust

学习内容:Rust Day11 笔记——格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用

格式化宏

Rust 用 {} 替代 C 的 %,核心区分两个 trait:Display{},面向用户)和 Debug{:?},面向开发者)。完整格式说明符:{ [参数] : [填充] [对齐] [符号] [#] [0] [宽度] [.精度] [类型] }

阶段 0 热身(10 题)✅

温/摄氏度转换、BMI 计算、闰年判断、乘法表、最大公约数/最小公倍数、素数判断、阶乘 & 斐波那契、菱形打印、猜数字——10 道题覆盖基础语法。

阶段 1 所有权与借用 ✅

今天全部走完,逐个概念拆开练习,按理解顺序排列:

# 概念 一句话
1 所有权转移 传参后原变量不能再用
2 不可变借用 &T 借出去还能要回来,可同时多个
3 可变借用 &mut T 同一时刻只能有一个,且不能和 &T 共存
4 &str 切片 &String 更通用,String 和字面量都能传
5 生命周期 'a 标注引用有效范围,编译期防止悬垂引用
6 take_ownership StringVec<char>,原值被消费
7 返回切片引用 first_word:返回 &str,不复制数据
8 swap 通过 &mut 交换值,需解引用 *a
9 &[T] 切片 Vec 和数组自动转切片,函数参数首选
10 while 删除模式 删除不加 i,不删才加 i——替代有 bug 的 for
11 迭代器链 filter + copied + collect,返回新 Vec
12 Option<(&T, &T)> 返回多个引用的首尾元素
13 字符串切片 find(char::is_whitespace) 找位置切分
14 字符串反转 chars().rev().collect(),一行

核心规则: 一个值一个所有者 → 传参会转移 → 不想转移就用 & 借用 → 需要改就用 &mut → 返回引用加生命周期 → 视图用切片。

几个印象深的点:

  • for i in 0..v.len() + v.remove(i) 有 bug:v.len() 不变但数组变短,索引越界 + 跳元素
  • while i < v.len() + 删了不 i++:删完后下一个元素自动移到当前位置
  • &str 为什么总比 &String 好:&String 只能传 String 的引用,&str 可以传 &String、字符串字面量、切片——范围大得多

思考

今天突然意识到一件事:我似乎一直在要求自己的思考必须被输出。

每天写日志,慢慢形成了一种习惯——如果几天没有输出什么思考,就会觉得自己应该产生一点什么,于是强迫自己去思考、去记录。

这有点像薛定谔的猫:在我把思考写下来之前,我似乎无法确认自己今天到底有没有产生值得记录的思考。于是我会主动"打开盒子",强迫它变成一个确定的结果。

但今天特别的地方在于,我第一次比较清楚地知道自己到底在说什么。以前可能只是"感觉不对",却很难准确描述。今天我能把它说出来:

我似乎不是强迫自己思考,而是在强迫思考必须留下可观察、可输出的结果。

也许思考本身不需要每天都有结果。有时候没有什么值得记录的东西,也可以只是没有什么值得记录的东西。

至少今天,我知道了自己在想什么。

明日计划

  1. C 语言:跟课堂节奏,继续变量/函数章节
  2. Rust:进入阶段 2(结构体、枚举、错误处理、集合)