一.结构体、枚举与联合(Structure,Enumeration & Union)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>

// 枚举Enumeration
// 可替代多个#define

// 联合Union
// 它允许在相同的内存位置存储不同的数据类型。
// 联合体的所有成员共享一块内存空间,大小等于其最大成员的大小。
// 这就意味着在任一时刻,联合体只能存储一个成员的值

// 一个变量可能存储多种类型的数据,但是在一个给定时刻里,只是用其中一种的数据类型,这样可以节省内存。

typedef union
{
int int_value;
float float_value;
char* string_value;
} Data;

typedef enum
{
INT,
FLOAT,
STRING
} DataType;

typedef struct
{
DataType type;
Data data;
} TypedData;

void print_data(TypedData* typed_data)
{
switch(typed_data->type)
{
case INT:
printf("Integer: %d\n",typed_data->data.int_value);;
break;
case FLOAT:
printf("Float: %f\n",typed_data->data.float_value);
break;
case STRING:
printf("String: %s\n",typed_data->data.string_value);
break;
}
}
// 实现了三者的联用。自己定义一个类型,并按照不同类型去输出;也就是说我想要什么类型就可以做出什么类型,十分灵活。
// Union可以存储不同的数据类型,那我们可以通过这种方式来去控制它什么时候是什么类型
int main(void)
{
TypedData data1 = {INT,{.int_value = 10}}; //我们.这样的写法意思就是它自己data.···

TypedData data2 = {FLOAT,{.float_value = 3.14}};

TypedData data3 = {INT,{.string_value = "Hello,Chaos!"}};

print_data(&data1);
print_data(&data2);
print_data(&data3);

return 0;
}

二.VS编译器捕获字符串异常的简单方法

Release方式运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <crtdbg.h>

void my_invalid_pram_handler(const wchar_t* expression,const wchar_t* function,const wchar_t* file,unsigned int line,uimtptr_t p_reserved)
{
wprintf(L"Invalid parameter detected in function %s. File: %s Line %d\n",function,file,line);
wprintf(L"Expression: %s\n",expression);
}

int main()
{
_set_invalid_parameter_handler(my_invalid_parm_handler);

const char src[] = "Hello";

char dest[2];

erron_t err = strcpy_s(dest,sizeof(dest),src); // strcpy_s有一个返回值叫作errno_t,实际上是个int整形

if(err != 0)
{
printf("Error copying string. Error code: %d\n",err);
}
else
{
printf("%s\n",dest);
}

return 0;
}

三.字符串函数

非法字符匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 验证一个字符串是否有非法的那些字符(重命名)
#include <string.h>
#include <stdio.h>

int main(void)
{
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.");
}
return 0;
}

常用函数案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TEXT_SIZE 100
#define WORD_SIZE 50
#define DELIMS ",.!?\n "

void replaceWord(const char* text, const char* oldWord, const char* newWord, char* result);
int countChar(const char* text, char ch);
int countWords(const char* text);
void extractUniqueWords(const char* text, char uniqueWords[][WORD_SIZE], int* uniqueCount);


int main(void)
{
char text[TEXT_SIZE] = "This is a simple text.This text is for texting.";

char replacedText[TEXT_SIZE] = { 0 };
char oldWord[] = "text";
char newWord[] = "example";
char countCharTarget = 's';

char uniqueWords[TEXT_SIZE][WORD_SIZE] = { 0 };
int uniqueCount = 0;

replaceWord(text, oldWord, newWord, replacedText);
printf("Repaced Text : %s\n", replacedText);

int charCount = countChar(replacedText, countCharTarget);
printf("Character '%c' appears %d times.\n", countCharTarget, charCount);

int wordCount = countWords(replacedText);
printf("Total number of words: %d\n", wordCount);

extractUniqueWords(replacedText, uniqueWords, &uniqueCount);
puts("Unique words:");

for (int i = 0; i < uniqueCount; i++)
{
printf("%s\n", uniqueWords[i]);
}

return 0;
}

void replaceWord(const char* text, const char* oldWord, const char* newWord, char* result)
{
char buffer[TEXT_SIZE] = { 0 };
const char* pos = text;
const char* temp = text;
size_t oldlen = strlen(oldWord);

while ((temp = strstr(pos, oldWord)) != NULL)
{
strncat_s(buffer, sizeof(buffer), pos, temp - pos);
strcat_s(buffer, sizeof(buffer), newWord);

pos = temp + oldlen;
}

strcat_s(buffer, sizeof(buffer), pos);
strcpy_s(result, TEXT_SIZE, buffer);
}

int countChar(const char* text, char ch)
{
int count = 0;
while (*text)
{
if (*text == ch)
{
count++;
}
text++;
}
return count;
}

int countWords(const char* text)
{
int count = 0;
char buffer[TEXT_SIZE] = { 0 };

strcpy_s(buffer, TEXT_SIZE, text);

char* context = NULL;

char* token = strtok_s(buffer, DELIMS, &context);

while (token != NULL)
{
count++;
token = strtok_s(NULL, DELIMS, &context);
}
return count;
}

void extractUniqueWords(const char* text, char uniqueWords[][WORD_SIZE], int* uniqueCount)
{
char tempText[TEXT_SIZE] = { 0 };
strcpy_s(tempText, TEXT_SIZE, text);

char* context = NULL;
char* token = strtok_s(tempText, DELIMS, &context);

while (token != NULL)
{
int found = 0;

for (int j = 0; j < *uniqueCount; ++j)
{
if (strcmp(token, uniqueWords[j]) == 0)
{
found = 1;
break;
}
}

if (!found)
{
strcpy_s(uniqueWords[*uniqueCount], WORD_SIZE, token);
(*uniqueCount)++;
}
token = strtok_s(NULL, DELIMS, &context);
}
}

四.文件

安全读取,追加,清空文件 + 更新文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define BUFFER_SIZE 1024

// 安全读取配置文件
void read_config_safe(const char* filename);
// 安全追加日志文件
void append_log_safe(const char* filename, const char* msg);
// 清空日志文件
void clear_log(const char* filename);
// 安全更新日志记录
errno_t update_log_record_s(const char* filrname, const char* search_str, const char* replace_str);

int main(void)
{
// const char* config_filename = "D:\\Desktop\\game_config.txt";
// const char* log_filename = "D:\\Desktop\\log.txt";
const char* log_file = "D:\\Desktop\\chaos.log";
const char* search_str = "Memory usage exceeds 80% of available memory.";
const char* replace_str = "[2024-08-20T01:01:00Z] [INFO] Memory usage id back to normal levels";
// read_config_safe(config_filename);

// const char* str = "select * from student";
// append_log_safe(log_filename,str);
errno_t result = update_log_record_s(log_file,search_str,replace_str);
// clear_log(log_filename);

if (result != 0)
{
char error_msg[256];
strerror_s(error_msg, sizeof(error_msg), errno);
fprintf(stderr, "An error occuered: %s\n", error_msg);
}
else
{
printf("Record updated successfully.\n");
}
_fcloseall();
// 确保文件流全部关闭
// int numclosed = _fcolseall();
// printf("Number of files closed by _fclosed: %u\n", numclosed);

return 0;
}

void read_config_safe(const char* filename)
{
FILE* file_ptr = NULL;

errno_t err = fopen_s(&file_ptr, filename, "r");

if (err != 0 || file_ptr == NULL)
{
char error_msg[256];
// 把错误消息保存到字符串(缓冲区)
strerror_s(error_msg,sizeof(error_msg),errno);
// 把缓冲区(错误信息)放到标准输出stderr上,弹窗警告
// 当然也不一定是输出在屏幕,所以不能简单的利用printf处理
fprintf(stderr, "Failed to open config file for reading: %s\n", error_msg);

exit(EXIT_FAILURE);
}

char buffer[256];
while (fgets(buffer,sizeof(buffer),file_ptr != NULL)
{
printf("%s",buffer);
}

fclose(file_ptr);
}

void append_log_safe(const char* filename, const char* msg)
{
FILE* file_ptr = NULL;

errno_t err = fopen_s(&file_ptr, filename, "a");

if (err != 0 || file_ptr == NULL)
{
char error_msg[256];
// 把错误消息保存到字符串(缓冲区)
strerror_s(error_msg, sizeof(error_msg), errno);
// 把缓冲区(错误信息)放到标准输出stderr上,弹窗警告
// 当然也不一定是输出在屏幕,所以不能简单的利用printf处理
fprintf(stderr, "Failed to open log file for appending: %s\n", error_msg);

exit(EXIT_FAILURE);
}

fprintf(file_ptr, "%s\n", msg);

fclose(file_ptr);

}

void clear_log(const char* filename)
{
FILE* file_ptr = NULL;

errno_t err = fopen_s(&file_ptr, filename, "w");

if (err != 0 || file_ptr == NULL)
{
char error_msg[256];
// 把错误消息保存到字符串(缓冲区)
strerror_s(error_msg, sizeof(error_msg), errno);
// 把缓冲区(错误信息)放到标准输出stderr上,弹窗警告
// 当然也不一定是输出在屏幕,所以不能简单的利用printf处理
fprintf(stderr, "Failed to open log file for writing: %s\n", error_msg);

exit(EXIT_FAILURE);
}
// 文件已经打开为"w"模式,不进行其他操作,文件被清空,无需写入。

fclose(filename);
}

errno_t update_log_record_s(const char* filrname, const char* search_str, const char* replace_str)
{
if (filename == NULL || search_str == NULL || replace_str == NULL) return EINVAL; // 返回无效参数错误

FILE* file_ptr = NULL;

errno_t err = fopen_s(&file_ptr, filename, "r+");

if (err != 0 || file_ptr == NULL)
{
char error_msg[256];
// 把错误消息保存到字符串(缓冲区)
strerror_s(error_msg, sizeof(error_msg), errno);
// 把缓冲区(错误信息)放到标准输出stderr上,弹窗警告
// 当然也不一定是输出在屏幕,所以不能简单的利用printf处理
fprintf(stderr, "Failed to open log file for reading+: %s\n", error_msg);

exit(EXIT_FAILURE);
}

char* buffer[BUFFER_SIZE];
long position = 0;
int found = 0;

while (fgets(buffer, sizeof(buffer), file_ptr) != NULL)
{
if (strstr(buffer, search_str) != NULL)
{
found = 1;
position = ftell(file_ptr) - (long)strlen(buffer) - 1; // -1确保从行首开始替换
break; // 找到第一个匹配项,立刻停止
}
}

if (found)
{
fseek(file_ptr, position, SEEK_SET); // 移动回到找到记录的位置

// 计算原文本和替换文本的长度差异
size_t replace_len = strlen(replace_str);
size_t search_len = strlen(search_str);

if (replace_len > BUFFER_SIZE - 1 || search_len > BUFFER_SIZE - 1)
{
fclose(file_ptr);

return ERANGE; // 返回错误码,表示字符串长度超出范围
}

// 写入新数据前,清楚所在位置的行数据
memset(buffer, ' ', strlen(buffer) - 1); // 用空格填充,保持文件大小不变

buffer[strlen(buffer) - 1] = '\n'; // 保留换行符

fseek(file_ptr, position, SEEK_SET); // 重新回到标记行的开始

fputs(buffer, file_ptr); // 清除原来行的内容

int result = fputs(replace_str, file_ptr); // 写入替换的字符串

if (result == EOF)
{
fclose(file_ptr);
/*
char error_msg[256];
// 把错误消息保存到字符串(缓冲区)
strerror_s(error_msg, sizeof(error_msg), errno);
// 把缓冲区(错误信息)放到标准输出stderr上,弹窗警告
// 当然也不一定是输出在屏幕,所以不能简单的利用printf处理
fprintf(stderr, "Failed to open config file for reading: %s", error_msg);
*/
exit(EXIT_FAILURE);
return errno;
}
}
else
{
fclose(file_ptr);
return ENOENT; // 返回未找到匹配项
}
fclose(file_ptr);

return 0;
}

fflush()

  • 立刻刷新缓冲区

  • 单线程:错误日志

  • 多线程

二进制文件操作

游戏设置小案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

typedef struct GameSettings
{
float volume;
int resolution_x;
int resolution_y;
int difficulty;
}GameSettings;

void save_game_settings(const GameSettings* settings, const char* filename);
void load_game_settings(const GameSettings* settings, const char* filename)


int main(void)
{
// fread fwrite
// 读写二进制

/*
GameSettings settings = {0.75,1920,1080,1};

save_game_settings(&settings, "D:\\Desktop\\game_settings.bin");
*/

GameSettings loaded_settings;

load_game_settings(&loaded_settings,"D:\\Desktop\\game_settings.bin");

printf("游戏设置已加载! \n");
printf("音量: %f\n", loaded_settings.volume);
printf("分辨率: %dx%d\n", loaded_settings.resolution_x,loaded_settings.resolution_y);
printf("难度: %d\n", loaded_settings.difficulty);

return 0;
}

void save_game_settings(const GameSettings* settings, const char* filename)
{
FILE* file;
errno_t err = fopen_s(&file, filename, "wb");

if (err != 0 || filename == NULL)
{
perror("无法打开文件进行写入操作");
return;
}

fwrite(settings, sizeof(GameSettings), 1, file);

fclose(file);
}

void load_game_settings(const GameSettings* settings, const char* filename)
{
FILE* file;
errno_t err = fopen_s(&file, filename, "wb");

if (err != 0 || filename == NULL)
{
perror("无法打开文件进行读取操作");
return;
}

fread(settings, sizeof(GameSettings), 1, file);

fclose(file);
}
文件复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(void)
{
FILE* source_file, * target_file;

char source_path[] = "D:\\Desktop\\chaos.txt";
char target_path[] = "D:\\Desktop\\chaos-copy";

char buffer[1024];

size_t bytes_read;

errno_t err = fopen_s(&source_file, source_path, "rb");

if (err != 0 || source_file == NULL)
{
perror("无法打开源文件");
return EXIT_FAILURE;
}

err = fopen_s(&target_file, target_path, "wb");
if (err != 0 || target_file == NULL)
{
perror("无法打开目标文件");
fclose(source_file);
return EXIT_FAILURE;
}

while ((bytes_read = fread(buffer,1,sizeof(buffer),
source_file)) > 0 )
{
fwrite(buffer, 1, bytes_read, target_file);
}

_fcloseall();

puts("文件复制完成!\n");

return 0;
}

五.math.h

对于math类别的错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS // 允许使用 C语言 中非安全的函数,忽略报错
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <math.h>

int main(void)
{
double number = 0.0;
double result = 0.0;

printf("请输入一个数字以计算其平方根: ");
scanf("%lf", &number);

errno = 0;

result = sqrt(number);

// 检查 sqrt 是否出错 (只列举了一部分)
if (errno == EDOM)
{
printf("错误: 输入值为负数,无法计算其平方根!\n");
}
else if (errno == ERANGE)
{
printf("错误: 结果超出范围!\n");
}
else if (errno == HUGE_VAL)
{
printf("错误: 结果超出范围!返回 HUGE_VAL!\n");
}
else
{
printf("数字 %.2f的平方根为:%.2f\n", number,result);
}


return 0;
}