容易忘记的点
001-028
int32_t myInt32 = 2147483647 ;uint32_t myUInt32 = 4294967295U ;int64_t myInt64 = 9223372036854775807LL ;uint64_t myUInt64 = 18446744073709551615ULL ;printf ("Size of int16_t: %zu byte(s)\n" , sizeof (myInt16));printf ("Size of uint16_t: %zu byte(s)\n" , sizeof (myUInt16));printf ("Size of int32_t: %zu byte(s)\n" , sizeof (myInt32));printf ("Size of uint32_t: %zu byte(s)\n" , sizeof (myUInt32));printf ("Size of int64_t: %zu byte(s)\n" , sizeof (myInt64));printf ("Size of uint64_t: %zu byte(s)\n" , sizeof (myUInt64));
uint16_t uSmallNum = 12345 ;int16_t sSmallNum = (int16_t )uSmallNum;
029. 固定宽度整数类型的格式化宏输出 inttypes. h
#include <stdio.h> #include <stdint.h> int main () { int32_t myInt32 = INT32_MAX; uint32_t myUInt32 = UINT32_MAX; uint64_t myUInt64 = UINT64_MAX; printf ("int32_t: %d\n" , myInt32); printf ("uint32_t: %u\n" , myUInt32); printf ("uint64_t: %llu\n" , myUInt64); return 0 ; }
头文件<inttypes.h>的使用
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { int32_t myInt32 = INT32_MAX; uint32_t myUInt32 = UINT32_MAX; uint64_t myUInt64 = UINT64_MAX; printf ("int32_t: %" PRId32 "\n" , myInt32); printf ("uint32_t: %" PRIu32 "\n" , myUInt32); printf ("uint64_t: %" PRIu64 "\n" , myUInt64); return 0 ; }
030. least 和 fast 整型的企业用途与区别
//int_leastN_t number;
//至少有N位,可能更多,适用于需要保证最小存储容量的可移植代码。
//int_fastN_t 特效8bit 16bit
//至少有N位,但是选择运算最快的类型,适用于需要性能敏感的场景。
//标准整数类型 → 固定宽度整数类型
//固定的位数,不可以越界,适用于需要精确数据大小的场景。
032-033. (选修) 浮点数 IEEE754指数偏移存储原理-(选修) 浮点数计算范围的原理
由于指数是以无符号形式存储的,因此指数的偏差为其可能值的一半。 对于 float 类型,偏差为 127;对于 double 类型,偏差为 1023。 您可以通过将指数值减去偏差值来计算实际指数值。
存储为二进制分数的尾数大于或等于 1 且小于 2。 对于 float 和 double 类型,最高有效位位置的尾数中有一个隐含的前导 1,这样,尾数实际上分别为 24 和 53 位长,即使最高有效位从未存储在内存中也是如此。
类型
有效位(十进制)
字节数
指数长度
尾数长度
float
6-7
4
8 位
23 位
double
15-16
8
11 位
52 位
如果存储比精度更重要,请考虑对浮点变量使用 float 类型。 相反,如果精度是最重要的条件,则使用 double 类型。
034-035. float 类型的定义和输出-float 丢失精度以及%E 与%A 科学计数法输出
#include <stdio.h> int main (void ) { float temperature = 36.5f ; float humidity = 48.3f ; float speed_of_sound = 343.5e2 f; float length = 12.34f , width = 23.45f , height = 34.56f ; printf ("Temperature: %f\n" , temperature); printf ("Humidity: %f\n" , humidity); printf ("Speed of Sound: %f\n" , speed_of_sound); printf ("Dimensions (LxwxH): %f x %f x %f\n" , length, width, height); return 0 ; }
#include <stdio.h> int main () { float num = 123.456f ; printf ("Using %f: %f\n" , num); printf ("Using %e: %e\n" , num); printf ("Using %EE: %E\n" , num); printf ("Using %a: %a\n" , num); printf ("Using %A: %A\n" , num); return 0 ; }
036. 浮点数 overflow 上溢与 underflow 下溢
#include <stdio.h> #include <float.h> int main () { float max_float = FLT_MAX; float overflow = max_float * 1000.0f ; float min_float = FLT_MIN; float underflow = min_float / 1000.0f ; printf ("Maximum Float: %e\n" , max_float); printf ("Overflow: %e\n" , overflow); printf ("Minimun Positive Float: %e\n" , min_float); printf ("Underflow: %e\n" , underflow); return 0 ; }
037. Infinity 与 Nan
#include <stdio.h> #include <float.h> #include <math.h> int main () { float positive_infinity = INFINITY; printf ("Positive Infinity: %f\n" , positive_infinity); float negative_infinity = -INFINITY; printf ("Negative Infinity: %f\n" , negative_infinity); float num = 1.0f ; float infinity = num / 0.0f ; printf ("1.0 / 0.0 = %f\n" , infinity); float negative_sqrt = sqrt (-1.0f ); printf ("sqrt(1.0f) = %f\n" , negative_sqrt); return 0 ; }
038-1. (选修-难-可忽略) 最近偶数舍入 (银行家舍入标准
#include <stdio.h> #include <float.h> #include <math.h> int main () { float a = 3.15f ; float b = 3.25f ; printf ("尾数为奇数:%.1f\n" ,a); printf ("尾数为偶数:%.1f\n" ,b); return 0 ; }
039. double, long double 科研与企业用途的差别
小数默认是 double 类型,加上 f 后缀隐式转换 float 类型
#include <stdio.h> int main () { printf ("sizeof(3.12) = %zu (double=%zu)\n" , sizeof (3.12 ), sizeof (double )); printf ("sizeof(3.12f) = %zu (float=%zu)\n" , sizeof (3.12f ), sizeof (float )); printf ("sizeof(3.12l) = %zu (long double=%zu)\n" , sizeof (3.12l ), sizeof (long double )); if (sizeof (3.12 ) == sizeof (double )) { printf ("\n结论: 3.12 默认是 double 类型\n" ); } return 0 ; }
double 类型用 %f 输出仍然会是 double 类型
long double
long double a = 1.233L ;printf ("long double:%Lf" ,a);
040. float 和 double 有效精度对比原理与计算
float.h 头文件:FLT_DIG,DBL_DIG
#include <stdio.h> #include <float.h> int main () { float float_num = 1.0 / 3.0 ; double double_num = 1.0 / 3.0 ; printf ("Float precision: %.20f\n" , float_num); printf ("Double precision: %.20lf\n" , double_num); printf ("Defined max precision for float: %d\n" , FLT_DIG); printf ("Defined max precision for double: %d\n" , DBL_DIG); return 0 ; }
041-2. (选修) 补录 Decimal
042. char 与 ASCII
char a = 'A' ;printf ("char a:%c\n" ,a);
043-044. 转义序列-转义序列练习
转义序列
表示
\a
响铃(警报)
\b
Backspace
\f
换页
\n
换行
\r
回车(相当于现在键盘的 home) (早期 window 系统是\r\n 现在是系统内部\n 自动转为\r\n)
\t
水平制表符
\v
垂直制表符
%>
单引号
:"
双引号
\
反斜杠
%>
文本问号
** ooo
八进制表示法的 ASCII 字符
\x hh
十六进制表示法的 ASCII 字符
\x hhhh
十六进制表示法的 Unicode 字符(如果此转义序列用于宽字符常量或 Unicode 字符串文本)。 例如,WCHAR f = L'\x4e00' 或 WCHAR b[] = L"The Chinese character for one is \x4e00"。
045-046. bool 类型与实际案例-char 范围与无符号 char
c99 添加 bool 类型
unsigned char/uint8
<stdbool. h>头文件
047. 常量 const 与#define 宏
#define PI 3.12 int main () { const int a = 3 ;
049. 运算符的介绍
基本运算符:+ - * / %
关系运算符:== != > < >= <=
逻辑运算符:! && ||
赋值语句:=
050-053
数据对象与左值和右值
多重赋值:x=y=z=10
算数运算符的应用
一元与二元运算符:
-a:只对一个变量操作→一元运算符
a+b:二元运算符
054. 前缀后缀递增与递减
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { int32_t value = 5 ; uint32_t result; result = value + 1 ; result = value++; printf ("After postfix increment, result = %" PRId32 ", value = %" PRId32"\n" , result, value); value = 5 ; result = ++value; }
055-056. 按位移位运算符-按位移位的另外问题
uint32_t num = 25 ;uint32_t result = num << 10 ;
ALU:Arithmetic Logic unit
057. 逻辑的真与假、C关系运算符
058. 条件表达式运算符
059-062. 按位运算符
操作
描述
&
按位与:如果两个位均为 1,则对应的结果位将设置为 1。 否则,为 0。
^
按位异或:如果一个位是 0,另一个位是 1,则相应的结果位将设置为 1。 否则,为 0。
|
按位或:如果其中一个位是 1,则将对应的结果位设置为 1。 否则,为 0。
~
按位取反
ab 交换值 (其实还是 temp 最优解):
temp 中间变量:
temp = a; a = b; b = temp;
三次运算:
a = a+b; b = a-b; a = a-b;
按位异或:
a = a^b; b = a^b; a = a^b;
064. C逻辑运算符
操作
描述
&&
逻辑与:串联开关控制灯
and
||
逻辑或:并联
or
优先级&&比||高
A || B && C→A || (B && C)
065. 复合赋值
+= -= *= /= %= <<= >>=&= |= ^=
066. 逗号运算符
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { int32_t a = 1 ; int32_t b = 2 ; int32_t c = 3 ; int32_t d = (a += 1 , b += 1 , c += 1 ); printf ("result:a = %d,b = %d,c = %d,result = %d" , a, b, c, d); return 0 ; }
067-069. 计算的优先级和顺序
符号
操作类型
结合性
[ ] ( ) . -> ++ -- (后缀)
表达式
从左到右
sizeof & * + - ~ ! ++ -- (前缀)
一元
从右到左
typecasts
一元
从右到左
* / %
乘法性的
从左到右
+ -
累加性
从左到右
<< >>
按位移位
从左到右
< > <= >=
关系
从左到右
== !=
平等
从左到右
&
Bitwise-AND
从左到右
^
Bitwise-exclusive-OR
从左到右
|
Bitwise-inclusive-OR
从左到右
&&
Logical-AND
从左到右
||
Logical-OR
从左到右
? :
Conditional-expression
从右到左
= *= /= %= += -= <<= >>= &= ^= |=
简单和复合赋值
从右到左
,
顺序评估
从左到右
1 运算符按优先顺序降序列出。 如果多个运算符出现在同一行或组中,则它们具有相等的优先级。
2 所有简单和复合赋值运算符都具有相等的优先级。
表达式可以包含多个优先级相等的运算符。 当多个此类运算符出现在表达式中的同一级别时,计算会根据运算符的关联性(从右到左或从左到右)进行。 计算方向不会影响在同一级别包含多个乘法()、加法(*或二元位(+``&或|)^运算符的表达式结果。 作顺序不是由语言定义的。 如果编译器可以保证一致的结果,编译器可以按任意顺序计算此类表达式。
只有顺序求值()、logical-AND(,``&&)、logical-OR(||)、条件表达式(? :)和函数调用运算符构成序列点,因此保证其作数的特定计算顺序。 函数调用运算符是函数标识符后面的括号集。 保证顺序求值运算符 (,) 从左到右计算其作数。 (函数调用中的逗号运算符与顺序计算运算符不同,不提供任何此类保证。有关详细信息,请参阅 序列点 。
逻辑运算符还保证从左到右计算其作数。 但是,它们计算确定表达式结果所需的最小作数。 这称为“短路”评估。 因此,可能无法计算表达式的某些作数。 例如,在表达式x && y++中,仅当为 true(非零)时x,才会计算第二个作数y++。 因此, y 如果 x 为 false(0),则不会递增。
072-077
if (true ){ printf ("true" ); } else if (){ printf ("..." ); } else { printf ("false" ); }
080. switch… case的用途
switch (role){case 1 : case 2 : break ; default : break ; }
081. 再探条件运算符
grade = (score > 90 ) ? "A" : (score > 80 ) ? "B" : (score > 70 ) ? "C" : "D" ;
082. 检查账户锁定案例与提前return出
#include <stdio.h> #include <stdbool.h> #include <stdint.h> int main () { uint32_t wrong_attempts = 3 ; bool is_locked = wrong_attempts >= 3 ; if (is_locked){ puts ("账户被锁定!" ); return 0 ; } puts ("继续往后执行》。。" ); return 0 ; }
083-084. 卫语句的使用:租车案例-简化逻辑表达式
#include <stdio.h> #include <stdbool.h> #include <stdint.h> int main () { uint8_t age = 23 ; uint8_t driving_exp_years = 2 ; if (age < 21 ){ puts ("不符合条件,年龄不足。" ); return 0 ; } if (driving_exp_years < 1 ){ puts ("不符合条件,驾驶经验不足。" ); return 0 ; } puts ("满足租车条件" ); return 0 ; }
卫语句(Guard Clause)是编程中的一种代码结构,用于在函数或方法的开头提前检查条件,如果不满足条件就立即返回或退出,从而避免深层嵌套的 if-else 结构。
086. switch-case与if-else的根本区别
switch 判断变量 expression 必须是 int 整型(理论上比 if 快)
090. 初探while循环
while (current_laps < total_laps){ current_laps++; printf ("ok" ); }
常量要大写!!!
093. break与利用死循环-求和案例
while 的判断条件不能没有初始值
break 来提前跳出
094. 处理字符和字符串的退出检测问题(选修)
#include <stdio.h> #include <stdbool.h> #include <stdint.h> #include <inttypes.h> int main () { char input; uint16_t sum = 0 ; while (true ) { puts ("please input a number(0-9) or q to exit:" ); scanf_s(" %c" , &input,1 ); if (input == 'q' ) { printf ("sum = %" PRId16 "\n" ,sum); break ; } if (input > '0' && input < '9' ) { sum += (uint16_t )input - '0' ; } else { puts ("invalid input!!!" ); } } return 0 ; }
095-096. do-while与while的区别
098-099. continue的用法-continue和break联用条件判断的实际用途
100. 初探for循环
for (uint32_t current_laps = 1 ; current_laps <= 5 ; current_lap++){ printf (); }
101. 训练:求平方和
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { uint32_t input; uint32_t sum = 0 ; puts ("please input number:" ); scanf_s("%" SCNu32,&input); for (uint32_t index = 1 ; index <= input; index++) { sum += index * index; } printf ("从1到%" PRIu32 "的平方和是:%" PRIu32 "\n" , input, sum); return 0 ; }
103. 训练:延迟毫秒扩展Sleep
#include <windows.h> Sleep(1000 );
105-106. sqrt开根与素数的概念-训练:判断素数
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <math.h> int main () { uint32_t input; puts ("请输入正整数:" ); scanf_s("%" SCNu32, &input); for (uint32_t index = 2 ; index <= sqrt (input); index++) { if (input % index == 0 ) { printf ("您输入的%" PRIu32 "不是素数" , input); break ; } } printf ("您输入的%" PRIu32 "是素数" ,input); }
110. 训练:金字塔数字
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { uint32_t input; puts ("请输入:" ); scanf_s("%" SCNu32, &input); puts ("金字塔:" ); for (uint32_t index = 1 ; index <= input; index++) { for (uint32_t kongge = 1 ; kongge <= input - index; kongge++) { printf (" " ); } for (uint32_t num = 1 ; num <= index; num++) { printf ("%" PRIu32 " " ,num); } for (uint32_t num = index -1 ; num > 0 ; num--) { printf ("%" PRIu32 " " , num); } puts ("\n" ); } return 0 ; }
111. 案例:进度条
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <windows.h> int main () { uint32_t finish; const uint32_t total = 50 ; printf ("进度条:\n" ); for (uint32_t i = 0 ; i <= total; i++) { printf ("\r[" ); for (uint32_t j = 1 ; j <= i; j++) { printf ("#" ); } for (uint32_t j = i; j <= total; j++) { printf (" " ); } printf ("]" ); printf ("%" PRIu32 "%%\r" , (i*100 )/total); fflush(stdout ); Sleep(100 ); } printf ("结束!!" ); return 0 ; }
113. 使用VS进行debug调试以及for的注意事项
116-1. 数组 array[index]=element
116. 数组的初步使用
uint32_t numbers[10 ] = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 };
n必须是常量,数组必须初始化才可以使用
这里的常量不包括const定义的常量,#define宏定义或者直接123
uint32_t numbers[5 ] = {1 };uint32_t numbers[5 ] = {1 ,0 ,0 ,0 ,0 };
119.★数组的注意事项
错误1:越界访问
错误2:未初始化
错误3:数组大小错误uint32_t numbers[-1];
警告1:部分初始化uint32_t numbers[5]={1};
124. 数组的案例:字母次数统计
'\0' 在 C 语言中是空字符(null character) ,也称为字符串终止符 或空字节 。
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main () { char text[] = "Example text for a frequency analysis." ; uint32_t frequency[26 ] = { 0 }; for (uint32_t i = 0 ; text[i] != '\0' ; i++) { if (text[i] >= 'a' && text[i] <= 'z' ) { frequency[text[i] - 'a' ]++; } } for (uint32_t i = 0 ; i < 26 ; i++) { printf ("%c出现的次数是%d\n" , 'a' + i, frequency[i]); } return 0 ; }
126. 二维数组. mp4
int main () { uint32_t array [3 ][5 ] = { {1 ,2 ,3 ,4 ,5 }, {1 ,2 ,3 ,4 ,5 }, {1 ,2 ,3 ,4 ,5 } }; printf ("%" PRIu32, array [2 ][3 ]); return 0 ; }
126-2. 补录:隐式确定数组大小的初始化
127
127-1. Unicode字符编码与wchar
头文件<locale.h> 是 C 标准库中用于本地化 的头文件,主要功能是让程序适应不同国家/地区的语言、字符集、数字格式、日期格式等习惯 。
127-3. 五子棋棋盘绘制
wchar_t XXX = L''; wprintf(L"%lc");
unicode:0x25CB,0x25CF,0x00B7
129. 游戏案例:农场作物成长
int farm[ROWS][COLS] = { [3 ][3 ] = STONE, [6 ][6 ] = STONE };
132-133. 函数声明与定义的规则-函数的参数
void XXX () ;int main (void ) { XXX(12 ,1 ); } void XXX (int XXX,bool XX) { } int XXX () { return YY; }
135. 函数编写要领
1.明确函数目的,明确它的功能将帮助你决定哪些输入是必须的(形式参数/模板),以及如何报告它的工作结果
2.识别所需要的输入
3.确定函数的输出
4.使用适当的数据类型
5.函数的命名清晰,并且先声明后定义
136. 初步认识全局变量、局部变量
#define XXX 123 int XXX; int main (void ) {}
137. 游戏案例:石头剪刀布与软件工程的-规则映射-设计技巧
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <time.h> #include <stdlib.h> #define ROCK 1 #define PAPER 2 #define SCISSORS 3 uint32_t input;uint32_t robot_input;void print_rule () ;int get_input_move () ;int get_robot_move () ;void determine_winner (input, robot_input) ;int main () { srand(time(NULL )); print_rule(); input = get_input_move(); while (input == 0 ) { printf ("\n" ); input = get_input_move(); } robot_input = get_robot_move(); printf ("你选择了%" PRIu32 "\n" , input); printf ("机器人选择了%" PRIu32 "\n" , robot_input); determine_winner(input, robot_input); return 0 ; } void print_rule () { printf ("石头剪刀布游戏规则:\n" ); printf ("1:石头,2:布,3:剪刀\n" ); } int get_input_move () { printf ("请选择:" ); scanf_s("%" SCNu32, &input); if (input != 1 && input != 2 && input != 3 ) { printf ("无效的输入!!!" ); return 0 ; } return input; } int get_robot_move () { return (rand() % (SCISSORS - ROCK + 1 )) + ROCK; } void determine_winner (uint32_t input, uint32_t robot_input) { int arr[4 ] = { 0 ,PAPER,SCISSORS,ROCK}; if (input == arr[robot_input]) { printf ("你赢啦!!!" ); } else { printf ("你输了呜呜呜~~" ); } }
138. 案例:软件工程设计技巧:表驱动法 之再探成绩评分系统、再探闰年返回月份天数
const int days in month[MONTH COUNT]={ 31 , is leap year(year)?29 :28 , 31 ,30 ,31 ,30 ,31 ,31 ,30 ,31 ,30 ,31 };
存档闰年/平年判断:
def is_leap_year (year ): if year % 400 == 0 : return True if year % 100 == 0 : return False if year % 4 == 0 : return True return False
139-141. 递归,尾递归
uint32_t factorial (uint32_t n) { if (n == 0 ){ return 1 ; } else { return n*factorial(n-1 ); } }
uint32_t factorial (uint32_t n,uint32_t acc) { if (n == 0 ){ return acc; } else { return factorial(n-1 ,n*acc); } }
141. 为什么不建议使用递归以及递归和尾递归用途区别
套娃,有溢出风险,浪费内存,可读性差,依赖编译器支持
142. 企业使用迭代方法来替代递归
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int flib (uint32_t n) ;int main (void ) { printf ("%" PRIu32, flib(4 )); return 0 ; } int flib (uint32_t n) { if (n <= 1 ) { return n; } uint32_t flib_0 = 0 ; uint32_t flib_1 = 1 ; uint32_t flib_n; for (uint32_t i = 2 ; i <= n; ++i) { flib_n = flib_0 + flib_1; flib_0 = flib_1; flib_1 = flib_n; } return flib_n; }
143. 企业规范之void作为函数参数的必要性
144-153
作用域Scope
生命周期Lifetime
局部变量的作用域限定、自动存储期、初始值未定义
全局变量的跨越边界性与程序范围可见性
静态存储期:程序整个运行期间 都存在,不是临时创建、用完销毁 的存储方式
默认初始化:默认会有值(0)
静态局部变量static variables:在函数内部声明,并且程序的期间只初始化一次
多次调用函数该变量会累计改变
全局静态变量:在文件开头声明,只在文件内可见
register寄存器变量,不能用&获取地址(尽可能 放到寄存器)
块作用域 Block Scope 与链接性 Linkage
154-155. 地址-取地址的含义
printf ("%d的地址是:%p\n" ,target floor , (void *)&target floor );
指针与数组
156. 指针
157. 指针与修改
int * ptr_floor_103 = &building_floors[2 ];printf ("找到了业主的门牌号%d\n" ,*ptr_floor_103);printf ("找到了业主的门牌号%d\n" ,building_floors[2 ]);
158. 指针星号的企业风格规范以及容易引发的问题
int * XXX = &XXY; int *XXX = &XXY; int * p,q;int * p;int q;int *p,*q;int *p,* q;
160. 野指针的初步介绍
野指针:指向了一个无效的内存地址或者是已经释放的内存地址的指
针
161-162. 空指针的初步介绍-初始化
空指针:通常指没有指向任何有效内存地址的指针。
164. 数组的首地址与指针的算数运算
int num[] = {...};int * ptr = num;int * ptr = &num[0 ];
// Definitions of common types #ifdef _WIN64 typedef unsigned __int64 size_t; typedef __int64 ptrdiff_t; typedef __int64 intptr_t;
#include <stdio.h> #include <inttypes.h> int main (void ) { int num[] = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 }; int * addr_start = num; int size = sizeof (num) / sizeof (num[0 ]); printf ("num的大小是:%d\n" ,size); printf ("num开头的地址是:%p\n" , addr_start); addr_start += 4 ; printf ("num的第五个元素是%d\n" , *addr_start); addr_start -= 4 ; printf ("num的第一个元素是%d\n" , *addr_start); int * addr_end = &num[size - 1 ]; printf ("num的大小是:%td\n" , addr_end - addr_start); return 0 ; }
165. 续上集:指针的算数运算与比较运用
int * addr_4 = &num[3 ];if (addr_4 < addr_end) { printf ("第四个元素在最后一个元素前面\n" ); } printf ("反向遍历num:{" );for (int * i = addr_end; i >= addr_start; i--) { printf ("%d," , *i); } printf ("}" );
166. 再探size_t与数组与指针的使用
size_t 是 无符号整数类型 ,专门用于表示内存大小长度 和数组索引
// Definitions of common types #ifdef _WIN64 typedef unsigned __int64 size_t;
特性
size_t
int
有符号性
❌ 无符号 (只能 ≥ 0)
✅ 有符号(可正可负)
位数
32位系统:32位 64位系统:64位
通常 32 位(即使64位系统)
用途
内存大小、数组长度、对象大小
通用整数、计数、循环
最大值
非常大(64位上约 1.8×10¹⁹)
约 21 亿(2³¹-1)
printf 格式
%zu
%d
size_t size = sizeof (num) / sizeof (num[0 ]);for (int * i = addr_start; i <= addr_end; i++) { *i += 5 ; } printf ("\n\n直接通过指针修改num:{" );for (int * i = addr_start; i <= addr_end; i++) { printf ("%d," , *i); } printf ("}" );
168-169. 指针访问多维数组-指针数组
int (*ptr)[3 ] = num;int * ptr[3 ] = num;
#include <stdio.h> int main (void ) { int num[2 ][3 ] = { {1 ,2 ,3 }, {4 ,5 ,6 } }; int (*ptr)[3 ] = num; printf ("num:\n" ); for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { printf ("%d" , ptr[i][j]); } printf ("\n" ); } return 0 ; }
170. 函数的值传递与地址引用传递
地址作为值传递给函数内指针,从而直接编辑任何该地址(附件)的变量的值
171.案例:员工薪资系统:指针作为函数返回值
结构体
176-178. 初识结构体 structures -创建结构体变量与访问方式-匿名结构体、函数参数为结构体
struct 自己定义类型的类型名{ char name[50 ]; int age; float height; }; struct 类型名 变量名 = {‘luoyinhui’,123 ,123 };typedef struct human { char name[50 ]; int age; float height; }human01; human01 luoyinhui = {‘luoyinhui’,123 ,123 };
定义一般不在 main 里面从而让它全局可被使用
访问方式:1)通过.;2)指针
printf ("%d" ,human01.age);human01* luoyinhui_ptr = &luoyinhui; printf ("%d" ,luoyinhui_ptr->age);
#include <stdio.h> #include <inttypes.h> #define NEW_ID 23 typedef struct { char name[50 ]; uint32_t id; float height; }Student; void print_student (Student stu) ;void change_student (Student* stu) ;int main (void ) { Student luoyinhui = { "luoyinhui" ,12 ,123 }; print_student(luoyinhui); change_student(&luoyinhui); printf ("\n\nchange:\n" ); print_student(luoyinhui); return 0 ; } void print_student (Student stu) { printf ("stu name:%s\n" , stu.name); printf ("stu id:%" PRIu32"\n" , stu.id); printf ("stu name:%.2f\n" , stu.height); } void change_student (Student* stu) { stu->id = NEW_ID; }
声明
类型
含义
能否 %s
char* p
指针
指向一个字符串
✅ 可以
char* arr[100]
指针数组
100 个字符串指针
❌ 不行,arr 是数组
char s[100]
字符数组
存 100 个字符
✅ 可以
179. 值语义初始化结构体变量
值语义 values semantics 通过返回值是结构体的函数来初始化结构体
更加安全(反外挂,立刻销毁结构体从而无法通过地址来改变值)、简单
180. 结构体数组
#include <stdio.h> typedef struct { int x; int y; }Point; int main (void ) { Point points[2 ] = { {1 ,2 },{3 ,4 } }; for (int i = 0 ; i < 2 ; i++) { printf ("%d,%d\n" , points[i].x, points[i].y); } return 0 ; }
181. 嵌套结构体
#include <stdio.h> typedef struct { char city[50 ]; char street[50 ]; }Address; typedef struct { char name[50 ]; Address address; }People; int main (void ) { People luoyinhui = { "luoyinhui" , {"moumoudi" ,"moustreet" } }; printf ("%s:%s %s\n" ,luoyinhui.name,luoyinhui.address.city,luoyinhui.address.street); People* ptr = &luoyinhui; printf ("%s:%s %s" ,ptr->name,ptr->address.city,ptr->address.street); return 0 ; }
182. Enumeration 枚举
typedef enum { XXX, YYY, ZZZ }Weekday; int main (void ) { Weekday day = YYY; printf ("%d" , day); return 0 ; }
183. Union 联合
允许在相同的内存位置存储不同的数据类型
联合体的所有成员共享一块内存空间,大小等于其最大成员的大小
这就意味着在任一时刻,联合体只能存储一个成员的值
一个变量可能存储多种类型的数据,但是在一个给定时刻里,只是用其中一种的数据类型,这样可以节省内存
typedef union { int int_value; float float_value; char char_value[50 ] }Data;
#include <stdio.h> typedef enum { Int, Float, String }Datatype; typedef union { int int_value; float float_value; char char_value[50 ]; }Data; typedef struct { Datatype datatype; Data data; }Typeddata; void print_typeddata (Typeddata data) ;int main (void ) { Typeddata data_01 = { Int,{.int_value = 123 } }; print_typeddata(data_01); Typeddata data_02 = { Float,{.float_value = 1.23 } }; print_typeddata(data_02); Typeddata data_03 = { String,{.char_value = "hello" } }; print_typeddata(data_03); return 0 ; } void print_typeddata (Typeddata data) { switch (data.datatype) { case Int:printf ("%d\n" , data.data.int_value); break ; case Float:printf ("%.2f\n" , data.data.float_value); break ; case String:printf ("%s\n" , data.data.char_value); break ; } }
184.游戏设计:结构体、枚举、联合与多文件编程
字符串
186. 字符串
char c = 't' ;const char arr[] = { "Hello" }; const char * ptr = "Hello" ;
187. strcpy_s 的用法
strcpy (a,b); strcpy_s(a,size_of_a,b); strcpy_s(a,ssizeof(a),b);
192. strncpy_s
errno_t result = strncpy_s(dest,sizeof (dest),src,max_copy);if (result == 0 ){ printf ("Copied string: %s\n" , dest); } else { printf ("Error copying string.\n" ); }
189. strlen
char dest[50 ] = "Hello" ;printf ("%zd\n" , strlen (dest));char dest[50 ] = { 0 };strcy_s(dest,50 ,"Hello" ); printf ("%zd\n" , strlen (dest));
190. strcat_s:拼接字符串
char dest[50 ] = { 0 };strcpy_s(dest, sizeof (dest), "Hello" ); const char * src = ", World!\n" ;strcat_s(dest, sizeof (dest), src); printf ("%s" , dest);
193. strncat_s
char dest[50 ] = { 0 };strcpy_s(dest, sizeof (dest), "Hello" ); const char * src = ", World!\n" ;size_t max_append = 7 ;int result = strncat_s(dest, sizeof (dest), src,max_append);if (result == 0 ){ printf ("%s" , dest); } else { printf ("Error concatenating string.\n" ); }
191. sprintf_s
char buffer[50 ] = { 0 };int number = 3 ;double pi = 3.14159 ;int ret = sprintf_s(buffer, sizeof (buffer), "Integer:%d,Double:%.2f" , number, pi);if (ret > 0 ){ printf ("Formatted string: %s\n" , buffer); } else {printf ("Error Formatting string!\n" );}
194. gets_s
char buffer[100 ];gets_s(buffer,sizeof (buffer)); if (gets_s(buffer,sizeof (buffer)) == NULL ){ printf ("Error or end od fle encountered.\n" ); } else { printf ("your entered:%S\n" ,buffer); }
195. strtok_s
char str[] = "This is a sample string" ;char delim[] = " " ;char * token;char ** context = { 0 }; token = strtok_s(str, delim, &context); while (token != NULL ) { printf ("Token: %s\n" , token); token = strtok_s(NULL , delim, &context); }
196. strcmp
int result = strcmp (str1,str2);
197.strncmp
198. strchr 与 strrchr
const char * str = "XXXXXX" ;char * ptr_char = strchr (str, what_to_find) printf ("找到了位置是:%td\n" ,ptr_char - str + 1 );
199. strstr
const char * text = "XXXXXXXXXXXXXXYYYXXX" ;const char * sub = "YYY" ;char * result = strstr (text, sub); if (result != NULL ){ printf ("Found '%s' in '%s' at position:%%td\n" , sub, text,result - text +1 ); } else { printf ("not found" ); }
200. strspn 与 strcspn
const char * str1 = "123456abcdefghi678910" ;const char * str2 = "h2345678910" ;size_t len = strspn (str1, str2); printf ("%zu\n" , len);char input[] = "filename.txt" ;char invalid_chars[] = "/\\:*?\"<>|." ;if (strcspn (input, invalid_chars) < strlen (input)) { printf ("Input contains invalid characters.\n" ); } else { printf ("Input is valid.\n" ); }
201. 再次废话
声明
类型
含义
修改
能否 %s
const char* p
指针
指向一个字符串常量
❌ 不行
✅ 可以
char* arr[100]
指针数组
100 个字符串指针
✅ 可以
❌ 不行,arr 是数组
char s[100]
字符数组
存 100 个字符
✅ 可以
✅ 可以
202. 案例(略难):关于 string.h
替换单词:
char* text传进去只是指针所以如果函数最后一步是strcpy_s(text, sizeof(text), new_str);就是指针大小而不是数组大小导致替换不完全
字数统计:ff
while (text) while (*text)
#include <stdio.h> #include <inttypes.h> #include <string.h> #define SIZE 500 char change_text[] = "test" ;char new_text[] = "example" ;char change_char = 'a' ;int uniquecount = 0 ;char uniqueword[SIZE][SIZE] = {0 };void f_change_text (char * text, char change[],char new[]) ;int f_calculate_s (char * text, char simple) ;int f_cal (char * text) ;void f_extract_words (char * text) ;int uniquecount;int main (void ) { char text[SIZE] = "This is a test do you want to know why it is a test ye ye ye" ; f_change_text(text,change_text,new_text); printf ("%s\n" , text); uint32_t count; count = f_calculate_s(text, change_char); printf ("新句子里面有 %" PRIu32" 个'a'\n" , count); count = f_cal(text); printf ("新句子里面有 %" PRIu32" 个单词\n" , count); f_extract_words(text); printf ("独一无二的单词有:\n" ); for (int i = 0 ; i < uniquecount; i++) { printf ("%s\n" , uniqueword[i]); } return 0 ; } void f_change_text (char * text, char change[], char new[]) { char str[SIZE] = { 0 }; strcpy_s(str, sizeof (str), text); char new_str[SIZE] = { 0 }; char delim[] = " " ; char * token; char * context = NULL ; token = strtok_s(str, delim, &context); while (token != NULL ) { if (strcmp (token,change)==0 ) { strcat_s(new_str, sizeof (new_str), new); strcat_s(new_str, sizeof (new_str), " " ); } else { strcat_s(new_str, sizeof (new_str), token); strcat_s(new_str, sizeof (new_str), " " ); } token = strtok_s(NULL , delim, &context); } strcpy_s(text, SIZE, new_str); } int f_calculate_s (char * text, char simple) { int count = 0 ; while (*text) { if (*text == simple) { count++; } text++; } return count; } int f_cal (char * text) { int count = 0 ; char str[SIZE] = { 0 }; strcpy_s(str, sizeof (str), text); char * context = NULL ; char * token; token = strtok_s(str, " " , &context); while (token != NULL ) { count++; token = strtok_s(NULL , " " , &context); } return count; } void f_extract_words (char * text) { char str[SIZE] = { 0 }; strcpy_s(str, sizeof (str), text); char * context = NULL ; char * token; token = strtok_s(str, " " , &context); while (token != NULL ) { int found = 0 ; for (int j = 0 ; j < uniquecount; ++j) { if (strcmp (token, uniqueword[j]) == 0 ) { found = 1 ; break ; } } if (!found) { strcpy_s(uniqueword[uniquecount], SIZE, token); uniquecount++; } token = strtok_s(NULL , " " , &context); } }
204. 输入输出初步认识
输入/输出流 input/output stream
buffer 缓冲区暂存数据的位置 有助于批量读取数据
标准输入/输出 stdin/stdout
205-206. 复习 scanfs-scanf s 返回值
#include <stdio.h> #include <inttypes.h> #include <string.h> #define SIZE 50 int main (void ) { char text[SIZE]; scanf_s("%49s" , text, (unsigned int )sizeof (text)); printf ("%s" , text); char ch; scanf_s("%c" , &ch, 1 ); printf ("%c" , ch); int age; scanf_s("%d" , &age); printf ("%d" , age); }
文件流
207. stream 流的概述
流
文件流:磁盘 用于读取/写入在磁盘上的文件
标准 I/O 流:
stdin:默认连接到键盘用于程序的输入 scanf_s()
stdout:默认连接到控制台或者屏幕用于程序输出 printf()
stderr:默认链接控制台/屏幕,专门输出错误信息和警告
管道流:用于进程之间的通信 (IPC),允许一个进程的输出成为另一个进程的输入 popen()
内存流:允许你将流与内存缓冲区关联
fmemopen 是 POSIX 提供的一个函数,用来创建内存流
网络流:套接字 sockets
设备流
FILE* stream
208. fopen_s, fgetc, fgets, fclose 读取 r 模式
#include <stdio.h> #include <inttypes.h> #include <stdlib.h> #include <errno.h> int main (void ) { char buffer[256 ]; FILE* stream = NULL ; errno_t error = fopen_s(&stream, "C:\\Users\\luoyinhui\\Desktop\\1.txt" ,"r" ); if (error != 0 || stream == NULL ) { perror("Error happened:" ); return EXIT_FAILURE; } while (fgets(buffer, sizeof (buffer), stream) != NULL ) { printf ("%s" , buffer); } memset (buffer,0 ,sizeof (buffer)); if (fclose(stream) != 0 ){ perror("Error happened:" ); return EXIT_FAILURE; } return 0 ; }
int ch;while ((ch = fgetc(stream)) != EOF) { putchar (ch); }
209. fputs,fputc 与 w 模式
#include <stdio.h> #include <inttypes.h> #include <stdlib.h> #include <errno.h> int main (void ) { FILE* ptr = NULL ; errno_t error = fopen_s(&ptr, "C:\\Users\\luoyinhui\\Desktop\\1.txt" , "w" ); if (error != 0 || ptr == NULL ) { perror("Error happened:" ); return EXIT_FAILURE; } fputc('a' , ptr); fputc('b' , ptr); fputc('\n' , ptr); fputs ("nifdhfhskfdjsjfjs" , ptr); char name[] = "luoyinhui" ; fprintf (ptr,"your name is %s" , name); if (fclose(ptr) != 0 ) { perror("Error happened:" ); return EXIT_FAILURE; } else { printf ("successfully write!" ); } return 0 ; }
mode
访问
"r"
打开以便读取。 如果文件不存在或找不到,fopen_s 调用将失败。
"w"
打开用于写入的空文件。 如果给定文件存在,则其内容会被销毁。
"a"
在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。 如果文件不存在,则创建文件。
"r+"
打开以便读取和写入。 文件必须存在。
"w+"
打开用于读取和写入的空文件。 如果文件存在,则其内容会被销毁。
"a+"
打开以进行读取和追加。 追加操作包括在新数据写入文件之前移除 EOF 标记。 写入完成后,EOF 标记不会还原。 如果文件不存在,则创建文件。
210. ftell,fseek,rewind
#include <stdio.h> #include <inttypes.h> #include <stdlib.h> #include <errno.h> int main (void ) { char buffer[256 ]; FILE* stream = NULL ; errno_t error = fopen_s(&stream, "C:\\Users\\luoyinhui\\Desktop\\1.txt" , "r" ); if (error != 0 || stream == NULL ) { perror("Error happened:" ); return EXIT_FAILURE; } if (fgets(buffer, sizeof (buffer), stream) != NULL ) { printf ("%s" , buffer); } long ptr = ftell(stream); printf ("position is:%ld\n" , ptr); fseek(stream, 0 , SEEK_SET); ptr = ftell(stream); printf ("position is:%ld\n" , ptr); if (fgets(buffer, sizeof (buffer), stream) != NULL ) { printf ("%s" , buffer); } ptr = ftell(stream); printf ("position is:%ld\n" , ptr); rewind(stream); ptr = ftell(stream); printf ("position is:%ld\n" , ptr); memset (buffer, 0 , sizeof (buffer)); if (fclose(stream) != 0 ) { perror("Error happened:" ); return EXIT_FAILURE; } return 0 ; }
211-1. fscanf_s
#include <stdio.h> #include <stdlib.h> int main (void ) { long l; float fp; char s[81 ]; char c; FILE* stream; errno_t err = fopen_s(&stream, "C:\\Users\\luoyinhui\\Desktop\\1.txt" , "r" ); if (err) printf_s("The file fscanf.out was not opened\n" ); else { if (fscanf_s(stream, "%80s" , s, (unsigned )_countof(s)) != 1 ) { printf ("fail to read words\n" ); } if (fscanf_s(stream, "%ld" ,&l) != 1 ) { printf ("fail to read ld\n" ); } if (fscanf_s(stream, "%f" ,&fp) != 1 ) { printf ("fail to read lf\n" ); } if (fscanf_s(stream, " %c" , &c,1 ) != 1 ) { printf ("fail to read c\n" ); } printf ("%s\n" , s); printf ("%ld\n" , l); printf ("%f\n" , fp); printf ("%c\n" , c); fclose(stream); } return 0 ; }
211-2. fprintf
#include <stdio.h> #include <stdlib.h> int main (void ) { FILE* stream; int age = 12 ; float height = 1.75f ; char name[] = "luoyinhui" ; char grade = 'A' ; errno_t err = fopen_s(&stream,"C:\\Users\\luoyinhui\\Desktop\\1.txt" ,"w+" ); if (err != 0 ){ printf ("fail to open the txt\n" ); return EXIT_FAILURE; } fprintf (stream, "name:%s\n" , name); fprintf (stream, "height:%.2f\n" , height); fprintf (stream, "age:%d\n" , age); fprintf (stream, "grade:%c\n" , grade); if (fclose(stream) != 0 ) { perror("Error happened:" ); return EXIT_FAILURE; } return 0 ; }
212. ferror,feof,clearerr
if (ferror(stream)) { perror("error happened\n" ); clearerr(stream); } if (feof(stream)) { printf ("reach eof\n" ); } else { printf ("can't reach eof,maybe error happened\n" ); }
213. 抽离读写函数
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> void safe_read (char * file) ;int main (void ) { char * filename = "C:\\Users\\luoyinhui\\Desktop\\1.txt" ; safe_read(filename); return 0 ; } void safe_read (char * file) { FILE* stream; errno_t error = fopen_s(&stream,file,"r" ); if (error != 0 || stream == NULL ) { char error_msg[256 ]; strerror_s(error_msg, sizeof (error_msg), errno); fprintf (stderr , "error happened:%s\n" , error_msg); exit (EXIT_FAILURE); } char buffer[256 ]; while (fgets(buffer, sizeof (buffer), stream) != NULL ) { printf ("%s" , buffer); } fclose(stream); }
214. a 模式追加
mode
访问
"a"
在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。 如果文件不存在,则创建文件。
"a+"
打开以进行读取和追加。 追加操作包括在新数据写入文件之前移除 EOF 标记。 写入完成后,EOF 标记不会还原。 如果文件不存在,则创建文件。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> void safe_append (char * file,char * append) ;int main (void ) { char * logname = "C:\\Users\\luoyinhui\\Desktop\\log.txt" ; char * append = "select something from log" ; safe_append(logname,append); return 0 ; } void safe_append (char * file,char * append) { FILE* stream; errno_t error = fopen_s(&stream, file, "a" ); if (error != 0 || stream == NULL ) { char error_msg[256 ]; strerror_s(error_msg, sizeof (error_msg), errno); fprintf (stderr , "error happened:%s\n" , error_msg); exit (EXIT_FAILURE); } fprintf (stream, "%s\n" ,append); fclose(stream); }
int numclosed = _fcloseall();printf ("number of files closed by _fcloseall:%u\n" , numclosed);
215. w 模式清空
mode
访问
"w"
打开用于写入的空文件。 如果给定文件存在,则其内容会被销毁 。
"w+"
打开用于读取和写入的空文件。 如果文件存在,则其内容会被销毁 。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> void safe_read (char * file) ;void safe_append (char * file,char * append) ;void clear_log (char * file) ;int main (void ) { char * filename = "C:\\Users\\luoyinhui\\Desktop\\1.txt" ; safe_read(filename); char * logname = "C:\\Users\\luoyinhui\\Desktop\\log.txt" ; char * append = "select something from log" ; safe_append(logname,append); clear_log(logname); int numclosed = _fcloseall(); printf ("number of files closed by _fcloseall:%u\n" , numclosed); return 0 ; } void clear_log (char * file) { FILE* stream; errno_t error = fopen_s(&stream, file, "w" ); if (error != 0 || stream == NULL ) { char error_msg[256 ]; strerror_s(error_msg, sizeof (error_msg), errno); fprintf (stderr , "error happened:%s\n" , error_msg); exit (EXIT_FAILURE); } fclose(stream); }
216. 企业实际案例(难):修改 log,r+模式
mode
访问
"r+"
打开以便读取和写入。 文件必须存在,指针在文件中间使输入是覆盖而不是插入。
EINVAL(error invalid),代表 “Invalid argument” (无效参数)
ERANGE:字符串长度超出范围
错误码
全称
含义
ENOENT
Error No Entry/Entity
文件或目录不存在
EACCES
Error Access
权限拒绝
ENOMEM
Error No Memory
内存不足
EINVAL
Error Invalid
无效参数
EIO
Error I/O
输入输出错误
EEXIST
Error Exist
文件已存在
ENOSPC
Error No Space
设备无剩余空间
EPERM
Error Permission
操作不允许
ESRCH
Error Search
没有该进程
EINTR
Error Interrupt
系统调用被中断
208. fopen_s, fgetc, fgets, fclose 读取 r 模式 memset 用于缓冲区释放:memset(buffer,0,sizeof(buffer)); //释放缓冲区
听完课第一次自己写的遗漏:
返回无效参数错误
指针没有初始化 FILE* ptr = 0;
fopen_s 返回值报错遗漏 ptr == NULL:if (err != 0) {
position 是文件中的字节偏移量,所以不是指针
strstr 判断完忘记 break 逻辑
遗漏清空行前后字符串大小判断逻辑:ERANGE
最后fputs的返回值未与 EOF 判断:ENOENT
fputs()返回值:
返回值
含义
非负数 (通常是 0)
成功
EOF (通常是 -1)
失败
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define SIZE 1024 errno_t change_file_s (char * file, const char search_words[SIZE], const char change_words[SIZE]) ;int main (void ) { const char search_words[] = "Automatic reconnection attempt 1/3..." ; const char change_words[] = "[2026-03-03 09:34:16] Automatic reconnection attempt 1/3..." ; char log_file[] = "C:\\Users\\luoyinhui\\Desktop\\log.txt" ; errno_t err = change_file_s(log_file, search_words, change_words); if (err != 0 ) { char error_msg[256 ]; strerror_s(error_msg, sizeof (error_msg), err); fprintf (stderr , "error happened:%s\n" , error_msg); } else { printf ("successfully change log!!!" ); } _fcloseall(); return 0 ; } errno_t change_file_s (char * file, const char search_words[SIZE], const char change_words[SIZE]) { char buffer[SIZE]; int found = 0 ; long position = 0 ; FILE* file_ptr = NULL ; if (file == NULL || search_words == NULL || change_words == NULL ) { return EINVAL; } errno_t err = fopen_s(&file_ptr,file,"r+" ); if (err != 0 ||file_ptr == NULL ) { char err_msg[SIZE]; strerror_s(err_msg, sizeof (err_msg), errno); fprintf (stderr , "error happened:%s\n" , err_msg); exit (EXIT_FAILURE); } while (fgets(buffer, sizeof (buffer),file_ptr) != NULL ) { if (strstr (buffer, search_words) != NULL ) { position = ftell(file_ptr) - (long )strlen (buffer); found = 1 ; break ; } } if (found == 1 ) { fseek(file_ptr, position, SEEK_SET); size_t search_len = strlen (search_words); size_t change_len = strlen (change_words); if (search_len > SIZE - 1 || change_len > SIZE - 1 ) { fclose(file); return ERANGE; } memset (buffer, " " , strlen (buffer) - 1 ); buffer[strlen (buffer) - 1 ] = '\n' ; fputs (buffer, file_ptr); fseek(file_ptr, position, SEEK_SET); int end = fputs (change_words, file_ptr); if (end == EOF) { fclose(file_ptr); return errno; } } else { fclose(file_ptr); return ENOENT; } if (fclose(file_ptr) != 0 ) { perror("Error happened:" ); return EXIT_FAILURE; } }
216-2.临时文件的方案
上节方案会由于 c 特性导致覆盖问题,用临时文件解决该 bug
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define SIZE 1024 errno_t change_file_s (char * file, const char search_words[SIZE], const char change_words[SIZE]) ;int main (void ) { const char search_words[] = "666" ; const char change_words[] = "[2026-03-04] [INFO] [Auth] PAM service ready" ; char log_file[] = "C:\\Users\\luoyinhui\\Desktop\\log.txt" ; errno_t err = change_file_s(log_file, search_words, change_words); if (err != 0 ) { char error_msg[256 ]; strerror_s(error_msg, sizeof (error_msg), err); fprintf (stderr , "error happened:%s\n" , error_msg); } else { printf ("successfully change log!!!" ); } _fcloseall(); return 0 ; } errno_t change_file_s (char * file, const char search_words[SIZE], const char change_words[SIZE]) { char buffer[SIZE]; int found = 0 ; long position = 0 ; char temp_filename[L_tmpnam_s]; errno_t err = NULL ; snprintf (temp_filename, sizeof (temp_filename), "%s.tmp" , file); FILE* file_ptr = NULL ; FILE* temp_file_ptr = NULL ; if (file == NULL || search_words == NULL || change_words == NULL ) { return EINVAL; } err = fopen_s(&temp_file_ptr, temp_filename, "w" ); if (err != 0 || temp_file_ptr == NULL ) { return errno; } err = fopen_s(&file_ptr, file, "r+" ); if (err != 0 || file_ptr == NULL ) { char err_msg[SIZE]; strerror_s(err_msg, sizeof (err_msg), errno); fprintf (stderr , "error happened:%s\n" , err_msg); exit (EXIT_FAILURE); } while (fgets(buffer, sizeof (buffer), file_ptr) != NULL ) { if (found == 0 && strstr (buffer, search_words) != NULL ) { fprintf (temp_file_ptr, "%s\n" , change_words); found = 1 ; } else { fputs (buffer, temp_file_ptr); } } fclose(file_ptr); fclose(temp_file_ptr); file_ptr = NULL ; temp_file_ptr = NULL ; if (found) { if (remove(file) != 0 ) { remove(temp_filename); return errno; } if (rename(temp_filename, file) != 0 ) { return errno; } } else { remove(temp_filename); return ENOENT; } return 0 ; }
217. fflush 简单略过
218. 游戏设置案例:bin 二进制文件存储与读取,wb 与 rb 模式的使用
fread/fwrite 读写二进制
219.复制文件
220.第11章结束语
221-1.章节开头8
221-2.math.h 头文件的概述
222.三角函数与 MPI
223.双曲函数
224.指数和对数
225.常见 math 类别函数汇总
226.pow 函数
227.对于 math 类别的错误处理:EDOM,ERANGE,HUGE_VAL
228.检查浮点数类别与属性
229.浮点数的比较与差值
230.舍入和剩余函数
231.time.h 与时间戳的使用
232.简单回顾错误处理函数
233.传统数组的问题
234.★重点:栈内存和堆内存的对比
235.malloc 函数动态内存分配的使用与释放
236.企业案例:realloc 函数与释放
237.malloc 与结构体的使用以及防止内存的泄露
238.calloc 函数
239.多级指针
239-2.多级指针的用途
240.游戏案例:游戏服务器动态玩家列表管理之 realloc 与多级指针的应用
241.续上节:卫语句与 log 编写
242.三级指针案例:字符串无限追加的应用
243.案例:动态数据结构的管理
244.FucntionPointer 函数指针的概念
245-1.再探 typedef
245-2.练习:函数指针用途
246-1.函数指针与 callback 回调函数的作用
246-2.实际用途
247.事件处理框架 Event Handling Framework
248.游戏架构事件设计:事件类型、事件处理函数、事件注册、事件分发机制
249.再谈头文件与编译
250.编写头文件:函数声明与函数实现
251.泛型编程:比较与排序
252.企业案例:自定义函数处理比较器
253.闲聊休息
254.指针的作用域和生命周期
255.悬挂指针 Dangling pointer
256.可变参数 Variadic function final
257.练习:自定义日志函数
258.assert 断言
259.断言的 debug 与练习
260.企业案例:日志系统与指针问题处理的架构设计第1部分 logger
261.企业案例:日志系统与指针问题处理的架构设计第2部分 内存管理
262.续上节
263.企业案例:日志系统与指针问题处理的架构设计第3部分 error_handling
264.企业案例:日志系统与指针问题处理的架构设计第4部分 pointer safety 空指针野指针悬挂指针的处理
265.企业案例:日志系统与指针问题处理的架构设计第5部分 application_logic 模块
266.企业案例:日志系统与指针问题处理的架构设计第6部分 测试
267.企业案例:日志系统与指针问题处理的架构设计第7部分 写入文件
268.环境变量的读写8
269.命令行参数
270.小案例:命令行程序的编写
271.案例:自定义泛型对列第1部分泛型对列节点结构模块
272.案例:自定义泛型对列第2部分安全内存处理模块
273.案例:(选修)自定义泛型对列第3部分测试运行模块
274.函数传递数组
275.数组作为函数参数的练习
276.小总结8
277.案例:小仓库 items 管理
278.事件驱动注册器触发器:完善之前游戏触发机制
279.会再探数组指针的用途
280.★结构体指针成员:再探动态内存,堆内存和占内存
281.★动态数组 Dynamic Array
282★软件工程:组合、聚合、嵌套、构造、析构一结构体指针的高级应用