Skip to content

Commit

Permalink
小修改
Browse files Browse the repository at this point in the history
  • Loading branch information
Minsecrus committed Dec 1, 2024
1 parent e3a02cb commit 3492265
Show file tree
Hide file tree
Showing 6 changed files with 493 additions and 12 deletions.
120 changes: 119 additions & 1 deletion docs/教程/正文/语法和标准库/16_文件操作.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,127 @@
# 文件操作

## `FILE` 结构体

C 语言标准未指定 `FILE` 结构体的具体实现,甚至没有说明它是不是完整类型。`FILE` 对象可能在语义上不可复制。

## 文件访问

## 直接输入输出
在 C 语言中,文件操作主要通过 `stdio.h` 头文件中定义的函数来实现。最常用的文件操作函数包括:

### 打开文件

```c
FILE* fopen(const char* filename, const char* mode);
```
常用的模式有:
| 文件访问模式字符串 | 含义 | 解释 | 若文件已存在的动作 | 若文件不存在的动作 |
| :----------------: | :--: | :--: | :----------------: | :----------------: |
| `"r"` | 读 | 打开文件以读取 | 从头读 | 打开失败 |
| `"w"` | 写 | 创建文件以写入 | 销毁内容 | 创建新文件 |
| `"a"` | 附加 | 附加到文件 | 写到结尾 | 创建新文件 |
字符串的其余部分可以包含以下任意顺序的字符,并进一步影响文件的打开方式:
- `"b"` :以二进制模式打开文件。
- `"x"` :以独占模式打开文件。
- `"p"` :以私有模式打开文件。
- `"+"` :以更新模式打开文件(可读可写)。
### 关闭文件
```c
int fclose(FILE* stream);
```

### 直接输入输出

#### 字符操作

```c
int fputc(int char, FILE* stream); // 写入单个字符
int fgetc(FILE* stream); // 读取单个字符
```
### 字符串操作
```c
int fputs(const char* str, FILE* stream); // 写入字符串
char* fgets(char* str, int n, FILE* stream); // 读取字符串
```

### 格式化输入输出

```c
int fprintf(FILE* stream, const char* format, ...); // 格式化写入
int fscanf(FILE* stream, const char* format, ...); // 格式化读取
```
## 文件定位
### 获取当前位置
```c
long ftell(FILE* stream);
```

### 设置位置

```c
int fseek(FILE* stream, long offset, int whence);
```
定位参数:
- `SEEK_SET`: 文件开头
- `SEEK_CUR`: 当前位置
- `SEEK_END`: 文件末尾
### 重置位置
```c
void rewind(FILE* stream); // 将位置重置到文件开头
```

## 对文件的操作

### 文件重命名

```c
int rename(const char* oldname, const char* newname);
```
### 删除文件
```c
int remove(const char* filename);
```

### 示例代码

```c
#include <stdio.h>

int main() {
FILE* fp;
char buffer[100];

// 写入文件
fp = fopen("test.txt", "w");
if (fp != NULL) {
fprintf(fp, "Hello, File!\n");
fclose(fp);
}

// 读取文件
fp = fopen("test.txt", "r");
if (fp != NULL) {
fgets(buffer, 100, fp);
printf("读取的内容:%s", buffer);
fclose(fp);
}

return 0;
}
```
10 changes: 4 additions & 6 deletions docs/教程/正文/语法和标准库/18_泛型选择.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 泛型选择

泛型选择(Generic Selection) 是 C23 标准引入的一个新特性,它允许我们根据表达式的类型来选择不同的值。这个特性通过 `_Generic` 关键字来实现
泛型选择(Generic Selection) 是 C23 标准引入的一个新特性,它根据表达式的类型来选择不同的值。通过 `_Generic` 关键字实现

## 基本语法

Expand All @@ -23,8 +23,6 @@ _Generic(表达式,

## 实际示例

下面是一个简单的示例,展示如何使用泛型选择:

```c
#include <stdio.h>

Expand All @@ -50,7 +48,7 @@ int main() {

## 实用场景

泛型选择最常用的场景之一是创建类型安全的泛型函数。例如:
创建类型安全的泛型函数。例如:

```c
#define MAX(x, y) _Generic((x), \
Expand All @@ -74,7 +72,7 @@ float max_float(float a, float b) {
## 注意事项
1. 泛型选择是在编译时进行的,不会产生运行时开销
1. 泛型选择在编译时进行,不会产生运行时开销
2. 每个类型只能出现一次
3. 表达式不会被求值,只用于类型判断
3. 表达式不会被求值,只会用来判断类型
4. 类型匹配必须精确,不会进行隐式类型转换
6 changes: 2 additions & 4 deletions docs/教程/正文/语法和标准库/19_属性.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,12 @@ void process(int value) {

1. **优先使用标准属性**:相比编译器特定的 `__attribute__`,标准属性具有更好的可移植性,但也要注意编译器支持情况。

2. **明确使用目的**:属性应该用于明确的目的
2. **明确使用目的**

3. **添加说明**:对于 deprecated 等属性,建议添加说明文字
3. **添加说明**:对于 deprecated 等属性,建议添加说明文字

## 实际应用示例

下面是一个综合示例:

```c
#include <stdio.h>

Expand Down
192 changes: 192 additions & 0 deletions docs/教程/正文/语法和标准库/21_线程.md
Original file line number Diff line number Diff line change
@@ -1 +1,193 @@
# 线程

C11 标准引入了线程支持库(`<threads.h>`),提供了标准的多线程编程接口。

## threads.h

### 线程类型和函数

主要类型:

- `thrd_t`: 线程标识符
- `mtx_t`: 互斥锁
- `cnd_t`: 条件变量
- `tss_t`: 线程局部存储
- `once_flag`: 一次性初始化标记

主要函数:

- `thrd_create()`: 创建线程
- `thrd_join()`: 等待线程结束
- `mtx_init()`: 初始化互斥锁
- `mtx_lock()`: 锁定互斥锁
- `mtx_unlock()`: 解锁互斥锁
- `cnd_signal()`: 发送信号
- `cnd_wait()`: 等待条件变量
- `tss_set()`: 设置线程局部存储
- `tss_get()`: 获取线程局部存储
- `tss_delete()`: 删除线程局部存储

### 创建线程

使用 `thrd_create` 函数创建新线程:

```c
#include <threads.h>
#include <stdio.h>

int thread_func(void* arg) {
printf("线程运行中...\n");
return 0;
}
int main(void) {
thrd_t thr;
int result = thrd_create(&thr, thread_func, NULL);
if (result != thrd_success) {
printf("创建线程失败\n");
return 1;
}
// 等待线程结束
thrd_join(thr, NULL);
return 0;
}
```
### 互斥锁
保护共享资源:
```c
#include <threads.h>
mtx_t mutex;
int shared_data = 0;
int main(void) {
// 初始化互斥锁
if (mtx_init(&mutex, mtx_plain) != thrd_success) {
return 1;
}
// 使用互斥锁
mtx_lock(&mutex);
shared_data++; // 临界区
mtx_unlock(&mutex);
// 销毁互斥锁
mtx_destroy(&mutex);
return 0;
}
```

### 条件变量

用于线程同步:

```c
#include <threads.h>

mtx_t mutex;
cnd_t cond;
int ready = 0;

int producer(void* arg) {
mtx_lock(&mutex);
ready = 1;
cnd_signal(&cond);
mtx_unlock(&mutex);
return 0;
}

int consumer(void* arg) {
mtx_lock(&mutex);
while (!ready) {
cnd_wait(&cond, &mutex);
}
mtx_unlock(&mutex);
return 0;
}
```
### 线程局部存储
每个线程独有的存储空间:
```c
#include <threads.h>
tss_t key;
void destructor(void* data) {
free(data);
}
int thread_func(void* arg) {
int* value = malloc(sizeof(int));
*value = 42;
// 设置线程局部存储
tss_set(key, value);
// 获取线程局部存储
int* stored = tss_get(key);
printf("值: %d\n", *stored);
return 0;
}
int main(void) {
// 创建线程局部存储键
if (tss_create(&key, destructor) != thrd_success) {
return 1;
}
// ... 创建线程等操作 ...
tss_delete(key);
return 0;
}
```

### 一次性初始化

确保初始化代码只执行一次:

```c
#include <threads.h>

once_flag flag = ONCE_FLAG_INIT;

void init_function(void) {
// 只执行一次的初始化代码
printf("初始化...\n");
}

int thread_func(void* arg) {
call_once(&flag, init_function);
return 0;
}
```
## 返回值和错误处理
线程函数的返回值:
- `thrd_success`: 成功
- `thrd_error`: 一般错误
- `thrd_nomem`: 内存不足
- `thrd_timedout`: 超时
- `thrd_busy`: 资源忙
## 其他重要函数
- `thrd_current()`: 获取当前线程标识符
- `thrd_equal()`: 比较两个线程标识符
- `thrd_sleep()`: 线程休眠
- `thrd_yield()`: 让出 CPU 时间片
## 注意事项
1. 检查编译器支持,注意是否需要特殊编译选项
2. 检查返回值,进行错误处理
3. 注意资源的正确释放
4. 避免死锁
Loading

0 comments on commit 3492265

Please sign in to comment.