[{"content":"👋 Hi！ 我是 Nehemiab，HITSZ 大三计算机专业学生。\n📖 这个博客 AI 真的很强大，能完成我能力以外的很多事。\n我的生活学习已经离不开ai了，但又不希望这些东西只是雪泥鸿爪，所以督促自己留下些记录。\n那么开始吧\n$$ \\mathfrak{ego\\ cogito\\ ergo\\ sum} $$","permalink":"https://blog.nehemiab.top/about/","summary":"\u003ch2 id=\"-hi\"\u003e👋 Hi！\u003c/h2\u003e\n\u003cp\u003e我是 \u003cstrong\u003eNehemiab\u003c/strong\u003e，HITSZ 大三计算机专业学生。\u003c/p\u003e\n\u003ch2 id=\"-这个博客\"\u003e📖 这个博客\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eAI\u003c/strong\u003e 真的很强大，能完成我能力以外的很多事。\u003c/p\u003e\n\u003cp\u003e我的生活学习已经离不开ai了，但又不希望这些东西只是雪泥鸿爪，所以督促自己留下些记录。\u003c/p\u003e","title":"About"},{"content":"为什么选择 Fcitx5 + Rime Fcitx5 是 Linux 下新一代的输入法框架，相比 ibus 更加轻量、稳定。搭配 Rime（中州韵）输入法引擎和雾凇拼音词库，可以获得非常好的中文输入体验。\n安装步骤 移除 ibus（避免冲突） sudo apt remove ibus* sudo apt autoremove 然后注销当前用户重新登录，让设置生效。\n安装 Fcitx5 和 Rime sudo apt update sudo apt install fcitx5 fcitx5-chinese-addons fcitx5-rime fcitx5-module-cloudpinyin fcitx5-config-qt 安装雾凇拼音词库 mkdir -p ~/.local/share/fcitx5 git clone https://github.com/iDvel/rime-ice.git ~/.local/share/fcitx5/rime --depth 1 添加环境变量 mkdir -p ~/.config/environment.d nano ~/.config/environment.d/fcitx.conf 写入以下内容：\nXMODIFIERS=@im=fcitx GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx SDL_IM_MODULE=fcitx 设置为默认输入法 im-config -n fcitx5 配置 Rime 输入法 打开 Fcitx5 配置界面 取消勾选左下角的「只显示当前语言」(Only Show Current Language) 在右侧搜索框中输入 rime 找到 Rime (中州韵)，点击中间的 \u0026lt; 箭头将其添加到左侧的「当前输入法」列表中（建议放在键盘 - 英语的下方） 点击「应用」 打开任意文本编辑器，按 Ctrl + Space 或 Shift 切换到 Rime 输入法 右键点击状态栏的 Fcitx5 图标，点击「部署」(Deploy) 或「重新启动」(Restart) 注意：雾凇拼音的词库很大，首次部署可能需要 30 秒到 2 分钟。期间输入法可能会暂时卡住或打不出中文，请耐心等待。\n部署完成后，尝试输入中文。如果能正常打出拼音和候选词，说明配置成功！\n设置开机自启 在 Fcitx5 配置中开启自动启动选项，或在桌面环境的「启动应用程序」中添加 Fcitx5。\n常见问题 Firefox 中候选框无法对齐 如果是 snap 版 Firefox，由于权限限制会导致无法获取键入位置。\n解决方法：卸载 snap 版 Firefox，改用 apt 或官网版本。\n操作前请先登录 Firefox 账号备份数据。\nsudo snap remove --purge firefox rm -rf ~/snap/firefox 然后从官网下载 tar.xz 包，解压后移动到 /opt/：\nsudo mv firefox /opt/ 添加菜单图标：\nsudo nano /usr/share/applications/firefox.desktop 写入以下内容：\n[Desktop Entry] Name=Firefox Comment=Browse the World Wide Web Exec=/opt/firefox/firefox %u Icon=/opt/firefox/browser/chrome/icons/default/default128.png Terminal=false Type=Application Categories=Network;WebBrowser; MimeType=text/html;text/xml;application/xhtml+xml; StartupWMClass=firefox 更新桌面数据库：\nsudo update-desktop-database 菜单中就会出现 Firefox 的图标了。\n","permalink":"https://blog.nehemiab.top/posts/ubuntu-configure/","summary":"在 Ubuntu 上安装配置 Fcitx5 输入法框架、Rime 引擎和雾凇拼音词库的完整教程。","title":"Ubuntu 配置 Fcitx5 + Rime + 雾凇拼音"},{"content":"为什么要学 Git？ Git 是现代软件开发中最基础也是最核心的工具之一。无论你是做个人项目还是团队协作，版本控制都是不可或缺的技能。\n基础概念 在开始之前，先了解几个核心概念：\n工作区（Working Directory） → 暂存区（Staging Area） → 本地仓库（Local Repo） → 远程仓库（Remote Repo） 每次文件的修改都沿着这条路径流动，最终同步到远程仓库。\n日常操作速查 1. 配置 # 设置用户名和邮箱（第一次使用 Git 时） git config --global user.name \u0026#34;Nehemiab\u0026#34; git config --global user.email \u0026#34;your-email@example.com\u0026#34; # 查看配置 git config --list 2. 基本工作流 # 克隆仓库 git clone https://github.com/username/repo.git # 查看状态 git status # 添加文件到暂存区 git add filename # 添加单个文件 git add . # 添加所有变更 # 提交到本地仓库 git commit -m \u0026#34;feat: 添加了新功能\u0026#34; # 推送到远程仓库 git push origin main 3. 分支管理 # 创建并切换分支 git checkout -b feature/new-feature # 查看分支 git branch -a # 查看所有分支 # 合并分支 git checkout main git merge feature/new-feature # 删除分支 git branch -d feature/new-feature Commit Message 规范 一个好的 commit message 应该清晰描述\u0026quot;为什么改\u0026quot;而非\u0026quot;改了什么\u0026quot;。推荐使用 Conventional Commits 规范：\n\u0026lt;type\u0026gt;(\u0026lt;scope\u0026gt;): \u0026lt;description\u0026gt; [optional body] 常用 type 类型：\nType 说明 feat 新功能 fix 修复 bug docs 文档更新 refactor 代码重构 test 测试相关 chore 构建/工具相关 .gitignore 配置 # 编译产物 *.o *.class build/ dist/ # 依赖目录 node_modules/ vendor/ # 环境配置 .env .env.local # IDE 配置 .vscode/ .idea/ # 操作系统文件 .DS_Store Thumbs.db 我的 Git 配置推荐 这是我目前在用的 .gitconfig 配置：\n[alias] st = status co = checkout br = branch ci = commit lg = log --oneline --graph --all --decorate undo = reset --soft HEAD~1 [core] editor = nvim autocrlf = input [push] default = current 总结 Git 的学习曲线确实存在，但只要掌握了上述核心操作，日常开发就完全够用了。更多的技巧可以在实际使用中慢慢积累。\n推荐资源：\nPro Git 中文版 Learn Git Branching — 交互式学习 Git 记住：熟能生巧，多用自然就记住了。\n","permalink":"https://blog.nehemiab.top/posts/git-workflow-guide/","summary":"一篇面向新手的 Git 工作流指南，涵盖日常开发中最常用的操作和最佳实践。","title":"Git 工作流入门：从零开始管理你的代码"},{"content":"为什么选择 RISC-V RISC-V 作为一个开放标准的指令集架构（ISA），近年来在学术界和工业界都获得了广泛关注。与 ARM 和 x86 不同，RISC-V 采用完全开源的设计哲学，任何人都可以免费使用和实现。\nRISC-V 的设计特点 简洁优雅 — 基础指令集仅有不到 50 条指令 模块化设计 — 基础指令集（I 后缀）+ 可选扩展（M/A/F/D/C 等） 开源免费 — 无需授权费用 可定制 — 允许添加自定义指令 RV32I 基础指令集 RV32I 是 RISC-V 的基础 32 位整数指令集，包含以下类型的指令：\n1. 算术运算指令 # 加法：rd = rs1 + rs2 add t0, t1, t2 # 减法：rd = rs1 - rs2 sub t0, t1, t2 # 立即数加法：rd = rs1 + imm addi t0, t1, 42 2. 逻辑运算指令 # 按位与 and t0, t1, t2 # 按位或 or t0, t1, t2 # 异或 xor t0, t1, t2 3. 内存访问指令 # 加载字：rd = MEM[rs1 + imm] lw t0, 0(sp) # 存储字：MEM[rs1 + imm] = rs2 sw t0, 4(sp) 实践：用 RISC-V 汇编实现斐波那契数列 下面是一个简单的斐波那契数列实现：\n.section .text .globl fibonacci fibonacci: # 参数：a0 = n # 返回值：a0 = fib(n) li t0, 0 # fib(0) = 0 li t1, 1 # fib(1) = 1 beqz a0, return_0 # if n == 0, return 0 li t2, 1 beq a0, t2, return_1 # if n == 1, return 1 mv t2, a0 # t2 = n (counter) loop: add t3, t0, t1 # t3 = fib(n-2) + fib(n-1) mv t0, t1 # shift: t0 = t1 mv t1, t3 # shift: t1 = t3 addi t2, t2, -1 # counter-- bgtz t2, loop # if counter \u0026gt; 0, continue mv a0, t1 # return value ret return_0: li a0, 0 ret return_1: li a0, 1 ret 下一步学习计划 RV32I 基础指令集 RV32M 乘除法扩展 RV32F/D 浮点扩展 特权架构和中断处理 尝试用 Verilog 实现一个简易 RISC-V 核心 RISC-V 的世界非常广阔，期待在\u0026quot;烹饪\u0026quot;的过程中不断深入学习！\n","permalink":"https://blog.nehemiab.top/posts/riscv-learning/","summary":"记录 RISC-V 指令集架构的学习历程，从 RV32I 基础指令到实际编程实践。","title":"RISC-V 学习笔记：从基础指令集到实践"},{"content":"Programming with C Basic Data Type Constant Integer : Base Representation of 17 and -31\nBase Base Representation Of 17 Base Representation Of -31 Decimal 17 -31 Binary 00010001 -00011111 Octal \\(021=2*8^1+1*8^0\\) (Three binary digits as a group) \\(-037=-(3*8^1+7*8^0)\\) (Three binary digits as a group) Hexadecimal \\(0x11=(1*16^1+1*16^0)\\) (Four binary digits as a group) \\(-0x1F=-(1*16^1+F*16^0)\\) (Four binary digits as a group) Length\nInt 12 Unsigned 12u Long Int 12l Unsigned Long Int 12lu -原码、反码(解决减法问题)、补码(解决+0,-0问题 , 整数以补码的形式储存)\n正数 12 原码：00001100 反码：00001100（正数的反码与原码相同） 补码：00001100（正数的补码与原码相同） 负数 -12 原码：10001100（最高位为符号位，负数符号位为 1） 反码：11110011（原码除符号位外，其余各位取反） 补码：11110100（反码加 1） 补码的补码就是原码 12h进制：8+(-2) = 8 + 10 (mod 12) \\(12+(-12)=00001100[原]+10001100[原]=00001100[补]+11110100[补]=00000000[补]\\) 数值 原码 反码 补码 12 00001100 00001100 00001100 -12 10001100 11110011 11110100 Memory\n\\(8\\space bit\\space =\\space 1\\space Byte\\) \\(1\\space KB\\space = \\space 1024(2^{10})B\\) sizeof 是关键字不是函数\nOperator Increment Operator m = -(n++)equals m = -n++ equals m = -n; n++ In And Out Character 转义字符 描述 \\n 换行 \\t 水平制表符（Tab） \\\\ 反斜杠 \\' 单引号 \\\u0026quot; 双引号 \\r 回车 \\b 退格 \\f 换页 \\a 响铃 \\v 垂直制表符 \\? 问号 \\0 空字符(NULL) \\ddd 1-3位八进制ASCII码所代表的字符 \\xhh 1-2位十六进制ASCII码所代表的字符 getchar() putchar() getchar() 是先将所有字符送入缓冲区，直到键入回车换行符后才从缓冲区中逐个读出并赋值给变量 Printf printf 格式说明 描述 %d 以十进制形式输出整数 %i 以十进制形式输出整数（与 %d 相同） %u 以十进制形式输出无符号整数 %o 以八进制形式输出无符号整数 %x 以十六进制形式输出无符号整数（小写字母） %X 以十六进制形式输出无符号整数（大写字母） %f 以小数形式输出浮点数 %e 以指数形式输出浮点数（小写字母） %E 以指数形式输出浮点数（大写字母） %g 以最简形式输出浮点数（自动选择 %f 或 %e） %G 以最简形式输出浮点数（自动选择 %f 或 %E） %c 输出单个字符 %s 输出字符串 %p 输出指针的值 %% 输出百分号 printf格式修饰符 描述 - 左对齐（默认右对齐） + 输出符号（正数前加 +，负数前加 -） 在正数前加空格（负数前加 -） # 对于 o、x、X 类型，添加前缀 0、0x 或 0X 0 用零填充（而不是空格） m 最小字段宽度m .n (输入实型报错)精度（对于浮点数，表示小数点后的位数n；对于字符串，表示最大字符数n） printf返回输出的字符数 Scanf scanf 格式说明 描述 %d 读取十进制整数 %i 读取十进制、八进制或十六进制整数 %u 读取无符号十进制整数 %o 读取八进制整数 %x 读取十六进制整数 %f %e 读取浮点数(小数指数均可) %c 读取单个字符,空白字符也读取 %s 读取字符串（遇到空白字符结束） %p 读取指针 %% 读取百分号 %g 读取浮点数（自动选择最简形式） scanf 格式修饰符 描述 * 跳过当前匹配的输入项 用法：%*c m 最大字段宽度m 用法: %md,%mc h 读取短整数（short int 或 unsigned short int） l 读取长整数（long int 或 unsigned long int），或双精度浮点数（double） ll 读取长长整数（long long int 或 unsigned long long int） L 读取长双精度浮点数（long double） scanf 非法处理： scanf(\u0026quot;%2d %2d\u0026quot;,\u0026amp;a,\u0026amp;b);若输入12 3a，则a=12,b=3;若输入123a，则a=123,b未赋值**\nscanf() 返回有输入的数据项数，如上例返回2 空格 i. getchar()读掉 ii. scanf(\u0026quot; %c\u0026quot;,\u0026amp;c);表达式加空格忽略空白字符\n缓存区清除fflush(stdin),不清除stdin会导致下一次读取到scanf终止时留下的非法字符或空白字符\nwhile(getchar()!='\\n'); 清除非法字符\nSelection And Control exit(code) 需调用\u0026lt;stdlib.h\u0026gt;，退出程序，并将int型的code传给系统的调用进程 fabs()需调用\u0026lt;math.h\u0026gt;,对浮点数求绝对值 判断 \\(\\Delta==0\\)：( fabs(\\(\\Delta\\) - 0) \u0026lt; = \\(\\xi\\) ) \\(\\xi\\)为精度 Logic Operator 优先级! \u0026gt;\u0026amp;\u0026amp; \u0026gt;|| Test 白盒测试 边界测试 Bitwise Operator 对二进制数位进行操作，只能操作int,char类型 运算符 描述 示例 结果 \u0026amp; 按位与 5 \u0026amp; 3 1 | 按位或 5 | 3 7 ^ 按位异或 5 ^ 3 6 ~ 按位取反(可用于加密) ~5 -6 \u0026lt;\u0026lt;n 左移 每左移一位相当于乘以2 5 \u0026lt;\u0026lt; 1 10 \u0026gt;\u0026gt;n 右移 每右移一位相当于除以2 5 \u0026gt;\u0026gt; 1 2 Loops For Loops for(初始化表达式;循环控制表达式;增值表达式) 其中初始化表达式，增值表达式可以省略 延时器：for(int i=0;i\u0026lt;99999;i++); rand() rand()需要调用\u0026lt;stdlib.h\u0026gt; 产生一个0~RAND_MAX(32767)的随机数 用rand() %b + a将随机数平移到[a,a +b -1]区间内 srand()设置随机种子 使用time()返回的1970.1.1 00 : 00到现在的秒数作为种子 time() 参数：(time_t)Pointer保存至Pointer 指向的内存，NULL 不保存 返回1970.1.1 00 : 00到现在的秒数 Transference goto break continue return TypeOverflow Unsigned char overflow unsigned char uc = 255; // 最大值为 255 uc = uc + 1; // 溢出 uc=0 Int Overflow int i = INT_MAX; // 最大值为 2147483647 i = i + 1; // 溢出 i=-2147483648 Function Name Windows应用程序的标识符通常采用“大小写”混排的单词组合而 成。例如，函数名采用FunctionName这样的形式，变量名采用newValue这样的形式。 Linux/UNIX应用程序的标识符通常采用“小写加下划线”的方式。例如函数名采用function, name这样的形式，变量名采用new_value这样的形式。 Entrance Security 有的编译器会把printf的返回值作为函数的返回值返回给主函数 处理：为非法输入规定一个返回值 Defensive Programming 函数接口中清楚地定义函数的行为，包括入口参数、出口参数、返回状态、异常处理 尽量不使用全局变量向函数传递信息。 功能要单一 检查操作数及其类型的合法性，以避免发生除零、数据溢出、类型转换、类型不匹配等错误 判断函数调用是否成功和函数调用失败的处理 函数没有返回值时应用void声明 Scope 全局变量不指定初始值会自动初始化位零 存储类型 描述 auto 自动变量（局部变量），默认存储类型 register 寄存器变量，建议编译器将变量存储在 CPU 寄存器中 static 1.静态变量未初始化自动初始化为0，局部变量在函数调用结束后仍然存在，全局变量在整个程序运行期间存在 2.即仅在函数内可被访问 3. 静态局部变量是在静态存储区分配内存的，仅在第一次调用函数时被初始化一次 extern 用以声明该变量为外部变量，引用其他文件中定义的全局变量,未初始化自动初始化为 0 。不同于变量定义，变量声明时编译器并不对其分配内存 typedef 类型定义，为现有类型定义新的名称 assert() 当括号内条件为假时，终止程序并报错，输出所在行数 Conditional Compilation 指令 描述 #define 定义宏 #undef 取消宏定义 #ifdef 如果宏已定义，则编译以下代码 #ifndef 如果宏未定义，则编译以下代码 #if 如果条件为真，则编译以下代码 #else 如果前面的 #if 或 #ifdef 或 #ifndef 条件不满足，则编译以下代码 #elif 如果前面的 #if 或 #ifdef 或 #ifndef 条件不满足，且当前条件为真，则编译以下代码 #endif 结束条件编译块 #error 产生编译错误，输出错误信息 #pragma 用于实现编译器特定的功能 a.h 中的内容： #ifndef _A_H_ #define _A_H_//没有指定值，默认为1 //主程序 #endif Array 当数组在所有函数外定义，或用static定义为静态存储类型时，即使不给数组元素赋初值, 那么数组元素也会自动初始化为0,这是在编译阶段完成的 C89规定在定义数组时不能使用变量定义数组的大小 2-Dimension Array 初始化可省略第一维的长度 初始值少于数组元素个数，未初始化的自动初始化为0 short matrix[3][4] = { {1,2,3} , {4,5} , {6} }; short matrix[3][4] = { {1,2,3,0} , {4,5,0,0} , {6,0,0,0} }; Linear Search 循环扫描整个数组 Binary Search Pointer 定义指针变量并用NULL对其初始化 Function Pointer 一个函数名就是这个函数的源代码在内存中的起始地址 调用函数指针 void Function( int (*f) , int a , int b ,···) (*f)(a,b)：两个整型形参、返回值为整型的函数 *f(a,b)：两个整型形参并返回 整型指针 的函数 可以不用指针解引用来调用函数，即把函数指针当做函数名来直接使用，编译器会自动把f解释为函数指针，格式如下f(a,b) void type pointer void 类型指针是一种通用指针类型，可以指向任何类型的数据,针，即声明了一个指针变量，但未指定它可以指向哪一种基类型的数据。由于 void 类型指针不具有具体的数据类型，因此不能直接进行算术运算或解引用操作。 将 void 类型指针转换为具体类型的指针，以便进行解引用操作： int *intPtr = (int *)ptr; Character Array 若定义时省略长度，务必加上\\0：char str[]={'H','e','l','l','o','\\0'}; char str[]={'H','e','l','l','o'};不是字符串！ Character Pointers 字符串字面量本身代表的就是存放它的常量存储区的首地址，是一 个地址常量\nchar *str=\u0026quot;Hello\u0026quot;;(将保存在常量存储区中的\u0026quot;Hello\u0026quot;的首地址赋值给ptr)\n字符串\u0026quot;Hello\u0026quot;保存在只读的常量存储区中，此时不能修改*str的值\nchar a[]=\u0026quot;Hello\u0026quot;;char *str=a;此时可以修改\n注：str是常量，不能str++\nscanf() vs gets() gets() puts()也是\u0026lt;stdio.h\u0026gt;的函数 scanf以空格为分隔 gets()以回车符作为字符串的终止符，同时读走回车符 puts() 以’\\0\u0026rsquo;为输出结束，并且自动输出一个换行符 缓冲区溢出 用 fgets(name, sizeof(name), stdin);限制输入长度 Const 声明 描述 const int a a 是一个常量整数，值不能被修改 int const a a 是一个常量整数，值不能被修改（与 const int a 相同） const int *p p 是一个指向常量整数的指针，不能通过 p 修改整数的值，但可以修改 p 本身 int * const p p 是一个常量指针，指向一个整数，不能修改 p 本身，但可以通过 p 修改整数的值 const int * const p p 是一个指向常量整数的常量指针，不能修改 p 本身，也不能通过 p 修改整数的值 atoi() atoi()函数需要调用\u0026lt;stdlib\u0026gt; , 当某个字符可能是转换后的数值的组成部分时，函数开始执行转换，当某个字符不可能为数值的组成部分时，转换结束。 Array Pointers 一维数组 P + 1 相当于执行P + 1*sizeof(p的基类型)\nP + + 相当于执行p = p+sizeof(p的基类型)\n注：数组名a是指针常量，代表一个地址常量，其值是不能被改变的\n用一维数组作函数形参与用指针变量作函数形参本质上是一样的，因为它们接收的都是数组的起始地址，都需按此地址对主调函数中的实参数组元素进行间接寻址，因此在被调函数中既能以下标形式也能以指针形式来访问数组元素。\n但数组和指针并非在所有的情况下都是等同的。例如,\nsizeof(数组名)返回的是数组的大小\nsizeof(指针变量名)返回的是指针的大小\n二维数组 p [i][j] = * ( p[i]+j ) = *( *( p+i )+ j) = (*(p+i) )[j]\n数组名p类型为int (*)[N],但值p==\u0026amp;p[0][0],(注意不是二级指针类型!)\np =a[0] == p=*a == p = \u0026amp;a[0][0]\n指针数组 可加快字符串排序的速度 char *p[]中[ ]优先级高于* char *str[]={\u0026quot;Hello\u0026quot;,World\u0026quot;}将两字符串的首地址分别给str[0],str[1] 命令行传参 int main(int argc,char *argv[]) 其中argc为用户输入指令个数，argv储存指令 Dynamic Memory Allocation Memmory 动态内存分配的内存区域称为堆（Heap），与静态存储区和栈（Stack）不同。 静态存储区 静态存储区用于存储全局变量和静态变量，这些变量在程序的整个生命周期内都存在。 静态存储区的内存分配在程序编译时完成，并在程序结束时释放。 栈 栈用于存储局部变量和函数调用的上下文（如返回地址、参数等）。 栈的内存分配在函数调用时自动进行，并在函数返回时自动释放。 栈的大小通常较小，并且由系统自动管理。 堆 堆用于动态内存分配，程序员可以在运行时请求和释放内存。 堆的内存分配由程序员手动管理，使用 malloc、calloc、realloc 等函数分配内存，使用 free 函数释放内存。 堆的大小通常较大，但需要注意内存泄漏和碎片化问题。 malloc p = (int *)malloc(sizeof(int)) calloc p=(int *)calloc(10,sizeof(int)) 相当于声明了一个一维数组，第一个参数表示分配的数量，决定了数组的大小，第二个表示类型的大小 malloc所没有的功能：初始化内存为0 free 为动态分配的内存不会在函数调用结束后被自动释放，必须使用free才能释放 realloc p = (int *)realloc(p, new_size * sizeof(int)) realloc 用于调整之前分配的动态内存的大小。new_size 是新的大小，单位是字节。 如果 realloc 成功，它返回指向新内存块的指针，并且原来的内存块被释放。如果失败，它返回 NULL，原来的内存块保持不变。 Note 判断指针是否非空，是否成功分配内存 初始化memset(变量名，要初始化为的字符，变量大小)，指针初始化为NULL 释放内存 能用自动变量就不要用malloc，重复利用malloc申请到的内存 Struct Memory Alignment Union 不能比较，不能作为函数参数 Initialise union UNION{ int score; int num; }u; u = { .score = 100 }; enum enum tag{MON,TUE,...} Day 默认从0开始，也可以自定义赋值，后面未指定值的为前一个的值加1 Linked Table establishment 若链表为空表，新建节点为头节点 若非空，新建节点添加至末尾 NodeType *Append(NodeType *head,int data){ NodeType *p = NULL,*pr = head; p=(NodeType *)malloc(sizeof(NodeType)); if(p == NULL){ printf(\u0026#34;Allocation error\u0026#34;); exit(0); } if(head == NULL)head = p; else{ while(pr-\u0026gt;next!=NULL) { pr = pr-\u0026gt;next; } pr-\u0026gt;neat = p; } p-\u0026gt;data=data; p-\u0026gt;next=NULL; return head; } Delete 若为空列表，退出 若待删除的节点为头节点，head指向下一节点，并释放内存 若不是头节点，前一指针跳过当前指针指向下一指针，释放内存 若已搜索到链表的末尾，仍未找到，输出No Found NodeType *DeleteNode(NodeType *head,int data) { NodeType *p = head,*pr = head; if(head == NULL) { printf(\u0026#34;Empty\u0026#34;); return head; } while (data != p-\u0026gt;data \u0026amp;\u0026amp; p-\u0026gt;next != NULL) { pr = p; p = p-\u0026gt;next; } if (data == p-\u0026gt;data) { if (p == head) { head = p-\u0026gt;next; } else{ pr-\u0026gt;neat = p-\u0026gt;next; } free(p); } else{ printf(\u0026#34;No Found\u0026#34;); } return head; } Insert 若链表为空，新节点p作为头节点，head指向头节点 若非空，确定插入位置 若在头节点前，新节点的指针指向原头节点，head指向新节点 若在中间，新节点指针指向下一节点，前一节点指针指向新节点 若在末尾，末指针指向新节点 NodeType *InsertNode(NodeType *head, int data) { NodeType *p = head, *temp = NULL; p = (NodeType *)malloc(sizeof(NodeType)); if (p == NULL) { printf(\u0026#34;Allocation error\u0026#34;); exit(0); } p-\u0026gt;next = NULL; p-\u0026gt;data =data; if (head ==NULL) { head = p; } else { while (pr-\u0026gt;data \u0026lt; data \u0026amp;\u0026amp; pr-\u0026gt;next !=NULL)//condition { temp = pr; pr = pr-\u0026gt;next; } if (pr-\u0026gt;data \u0026gt;= data) { if (pr == head) { p-\u0026gt;next = head; head = p; } else { pr = temp; p-\u0026gt;next = pr-\u0026gt;next; pr-\u0026gt;next = p; } } else{ pr-\u0026gt;next = p; } } return head; } stack 顺序储存 Initialise typedef struct node{ int data; }NodeType; typedef struct stack{ NodeType data[N]; int top; }STACK; int main() { STACK stack; stack.top = 0; ··· } push void Push(STACK *stack,NodeType data) { memcpy(\u0026amp;stack-\u0026gt;data[stack-\u0026gt;top],\u0026amp;data, sizeof(NodeType)); stack-\u0026gt;top = stack-\u0026gt;top + 1; } pop NOdeType Pop(STACK *stack) { stack-\u0026gt;top = stack-\u0026gt;top - 1; return stack-\u0026gt;data[stack-\u0026gt;top]; } 链式储存 Initialize typedef struct node{ int data; }NodeType; typedef struct stack{ NodeType data; struct stack *next; }STACK; int main() { STACK *top = NULL; ··· } push STACK *Push (STACK *top,NodeType data) { STACK *p; p = (STACK *)malloc(sizeof(STACK)); p-\u0026gt;data = data; p-\u0026gt;next = top; top = p; return top; } STACK *Pop(STACK *top) { STACK *p; if(top ==NULL) { return NULL; } else { p = top; top =top-\u0026gt;next; free(p); } return top; } queue 顺序储存 Initialise typedef struct queue { int data[N]; int front; int rear; }QUEUE; 链式储存 Initialize typedef struct queue{ int data; struct queue *front; struct queue *rear; }QUEUE; 循环队列 rear不存数据 有数据进队时，rear = (rear +1) % QMAX 有数据进队时，front = (front +1) % QMAX 取模的意义在于如果front达到队列的末尾（即QMAX），则通过取模操作 % QMAX 将 front 重置为 0，从而实现循环 队空：rear==front 队满：front == (rear + 1) % QMAX 保留了一个空rear,目的时防止front == rear产生假满 tree Binary Tree typedef struct BiTNode{ int data; struct BiTNode * lchild;//左子树 struct BiTNode * rchild;//右子树 }BI_TREE; Concept 概念 描述 根节点 二叉树的顶点节点，没有父节点。 叶节点 没有子节点的节点。 内部节点 至少有一个子节点的节点。 左子树 一个节点的左子节点及其所有后代组成的子树。 右子树 一个节点的右子节点及其所有后代组成的子树。 深度 从根节点到某个节点的路径长度。 高度 从某个节点到叶节点的最长路径长度。 完全二叉树 除了最后一层外，每一层的节点都是满的，且最后一层的节点从左到右排列。 满二叉树 每一层的节点都是满的，即每个节点都有两个子节点或没有子节点。满二叉树也是完全二叉树 二叉搜索树 对于每个节点，左子树的所有节点值小于该节点值，右子树的所有节点值大于该节点值。 遍历 遍历方法 描述 前序遍历 先访问根节点，然后遍历左子树，最后遍历右子树。 中序遍历 先遍历左子树，中间访问根节点，最后遍历右子树。 后序遍历 先遍历左子树，然后遍历右子树，最后访问根节点。 层序遍历 按层次从上到下、从左到右依次访问每一层的节点。 graph 概念 描述 顶点 图中的一个节点。 边 连接两个顶点的线段。 无向图 边没有方向的图。 有向图 边有方向的图。 带权图 权重 —— 边的一个数值属性，表示从一个顶点到另一个顶点的代价。 邻接 如果两个顶点之间有边，则称这两个顶点是邻接的。 度 一个顶点的度是与其邻接的边的数量。 路径 从一个顶点到另一个顶点的顶点序列。 回路 起点和终点相同的路径。 连通图 如果图中任意两个顶点之间都有路径，则称该图是连通的。 子图 由图的部分顶点和边组成的图。 圈 起点与终点重合的路径 有向无环图 没有圈的有向图 树 没有圈的连接图 连接 邻接矩阵 初始化为inf一个较大的数，有连接为1，无连接为0 邻接表 对每个顶点建立单向链表，记录与该点连接的节点 DFS 、BFS Files fopen 模式 描述 \u0026ldquo;r\u0026rdquo; 以只读方式打开文件。文件必须存在。 \u0026ldquo;w\u0026rdquo; 以写入方式打开文件。文件存在则清空其内容，不存在则创建新文件。 \u0026ldquo;a\u0026rdquo; 以追加方式打开文件。文件存在则在末尾追加内容，不存在则创建新文件。 \u0026ldquo;r+\u0026rdquo; 以读写方式打开文件。文件必须存在。 \u0026ldquo;w+\u0026rdquo; 以读写方式打开文件。文件存在则清空其内容，不存在则创建新文件。 \u0026ldquo;a+\u0026rdquo; 以读写方式打开文件。文件存在则在末尾追加内容，不存在则创建新文件。 \u0026ldquo;rb\u0026rdquo; 以二进制模式只读方式打开文件。文件必须存在。 \u0026ldquo;wb\u0026rdquo; 以二进制模式写入方式打开文件。文件存在则清空其内容，不存在则创建新文件。 \u0026ldquo;ab\u0026rdquo; 以二进制模式追加方式打开文件。文件存在则在末尾追加内容，不存在则创建新文件。 \u0026ldquo;rb+\u0026rdquo; 以二进制模式读写方式打开文件。文件必须存在。 \u0026ldquo;wb+\u0026rdquo; 以二进制模式读写方式打开文件。文件存在则清空其内容，不存在则创建新文件。 \u0026ldquo;ab+\u0026rdquo; 以二进制模式读写方式打开文件。文件存在则在末尾追加内容，不存在则创建新文件。 不建议以读写方式打开文件，因为读写其实 共用 一个缓冲区，每次读写都会 改变文件位置指针 ，很容易写乱，破坏原来文件的内容，并且需要调用文件定位函数才能在读写之间转换 FILE *fp; fp = fopen(\u0026#34;···\u0026#34;,\u0026#34;w\u0026#34;); 打开错误返回NULL fclose 返回一个整型数，文件关闭成功返回0值 fgetc fgetc(fp)//fp: FILE * 是从fp所指的文件中读取一个字符，并将位置指针指向下一个字符 读取成功返回字符，都到末尾返回EOF fputc fputc(int c,FILE *fp);//c只写入低字节 写入错误返回EOF,正确则返回字符c feof ch = fgetc(fp); while (!=feof(fp)) { putchar(ch); ch = fgetc(fp); } 返回0值, 当且仅当fp指向文件结束符时才返回真值 isprint 判断字符是否可输出 打印1-127ASCII码在不同的机器上可能显示的乱码有所不同，用记事本或写字板打开文件看到的也是乱码，而且与屏幕显示的结果可能并不完全一致。 这是因为在Windows文本编辑器中打开文件时,显示的那些拐来拐去、方块箭头等符号都不是可打印字符。 使用不同的文本编辑软件会有不同的处理，而且还会配合字符集等进行转码，甚至换用不同的字体时会有不同的显示结果 iscntrl 判断是否为控制字符 fgets fgets(str,n,fp); 最多读入n-1个字符，在末尾添加\\0，返回指针给str，读取失败返回NULL ferror 检测文件错误 fputs 错误返回EOF,成功返回非负数 不会在末尾加上换行符 fscanf fprintf fscanf(fp,\u0026#34;%d\u0026#34;,\u0026amp;n); fprintf(fp,\u0026#34;%d\u0026#34;,n); 但输入时要将ASCII字符转换成二进制数，输出时要将二进制数转换为ASCII字符，耗时较多 fread fwrite 将fp指向的文件中读取数据块并存储到buffer指向的内存中 (读取buffer指向的内存写入fp指向的文件)，buffer为起始地址，count为最多读取 (写入) 的数据块数 返回读入 (写入) 的数据块数 fread (buffer, size, count, fp); fwrite (buffer, size, count, fp); Random Access fseek 将fp的文件位置指针从fromwhere开始移动offset个字节，指示下一个要读取的数据的位置。 offset是一个偏移量，它告诉文件位置指针要跳过多少个字节。offset为正时，向后移动，为负时，向前移动。ANSI C要求位移量offset是长整型数据(常量数据后要加L),这样当文件的长度大于64kb时不至于出问题 fromwhere用于确定偏移量计算的起始位置，它的可能取值有3种: SEEK_SET或0,代表文件开始处； SEEK_CUR或1,代表文件当前位置; SEEK_END或2,代 表文件结尾处。 通过指定fromwhere和offset的值，可使位置指针移动到文件的任意位置，从而实现文件的随机读取。 如果函数fseekO调用成功，则返回0值，否则返回非0值。 rewind 重置位置指针到文件首字节rewind(fp) ftell ftell(fp)返回当前读取位置(相对起始字节的偏移量L),调用失败返回-1L fflush fflush(fp)强行将缓冲区数据写入物理设备，清空缓冲区 redirect 3个特别的文件指针常数：stdin stdout stderr 标准错误输出不能进行重定向 fgets( str, size, stdin)与gets(str)相比限制了输入长度，更安全 命令行： ezample.exe \u0026lt; file.in从file.in读入数据 example.exe \u0026gt; file.out输出到file.out freopen 用法 描述 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;r\u0026quot;, stdin) 将标准输入重定向到 file.txt，以只读模式打开。 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;w\u0026quot;, stdout) 将标准输出重定向到 file.txt，以写入模式打开。 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;a\u0026quot;, stderr) 将标准错误输出重定向到 file.txt，以追加模式打开。 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;r+\u0026quot;, stdin) 将标准输入重定向到 file.txt，以读写模式打开。 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;w+\u0026quot;, stdout) 将标准输出重定向到 file.txt，以读写模式打开。 freopen(\u0026quot;file.txt\u0026quot;, \u0026quot;a+\u0026quot;, stderr) 将标准错误输出重定向到 file.txt，以读写模式打开。 freopen(\u0026quot;CON\u0026quot;,\u0026quot;r\u0026quot;,stdin) 将输入流还原到键盘 freopen(\u0026quot;CON\u0026quot;, \u0026quot;w\u0026quot; , stdout) 将输出流还原到屏幕 ","permalink":"https://blog.nehemiab.top/posts/programming-with-c/","summary":"大一C语言笔记","title":"Programming with C"}]