第16章 系统函数库

1. 系统函数库概述

1.1 什么是系统函数库

系统函数库是操作系统提供的一组函数集合,用于访问系统功能和资源。这些函数封装了底层的系统调用,为应用程序提供了一个统一的接口,使应用程序能够与操作系统进行交互。

1.2 系统函数库的分类

C语言中的系统函数库主要包括:

  • 标准I/O库:用于文件输入/输出操作
  • 字符串处理库:用于字符串操作
  • 数学库:用于数学计算
  • 时间和日期库:用于时间和日期操作
  • 内存分配库:用于动态内存分配
  • 进程控制库:用于进程管理
  • 网络编程库:用于网络通信
  • 信号处理库:用于信号处理
  • 线程编程库:用于多线程编程
  • 系统调用库:用于直接调用系统服务

1.3 系统函数库的使用

使用系统函数库需要:

  1. 包含相应的头文件:如#include <stdio.h>
  2. 链接相应的库:如使用-lm链接数学库
  3. 遵循函数的调用约定:正确传递参数,处理返回值

2. 标准I/O库

2.1 标准I/O库概述

标准I/O库(<stdio.h>)提供了文件输入/输出操作的函数,是C语言中最常用的库之一。

2.2 常用函数

2.2.1 文件操作函数

函数名功能原型
fopen打开文件FILE *fopen(const char *filename, const char *mode);
fclose关闭文件int fclose(FILE *stream);
fread读取文件size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite写入文件size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fseek设置文件位置int fseek(FILE *stream, long offset, int whence);
ftell获取文件位置long ftell(FILE *stream);
rewind重置文件位置void rewind(FILE *stream);
feof检查文件结束int feof(FILE *stream);
ferror检查文件错误int ferror(FILE *stream);
clearerr清除文件错误void clearerr(FILE *stream);

2.2.2 格式化输入/输出函数

函数名功能原型
printf格式化输出到标准输出int printf(const char *format, ...);
scanf格式化输入到标准输入int scanf(const char *format, ...);
fprintf格式化输出到文件int fprintf(FILE *stream, const char *format, ...);
fscanf格式化输入到文件int fscanf(FILE *stream, const char *format, ...);
sprintf格式化输出到字符串int sprintf(char *str, const char *format, ...);
sscanf格式化输入到字符串int sscanf(const char *str, const char *format, ...);
snprintf安全的格式化输出到字符串int snprintf(char *str, size_t size, const char *format, ...);

2.2.3 字符输入/输出函数

函数名功能原型
getchar从标准输入读取字符int getchar(void);
putchar向标准输出写入字符int putchar(int c);
fgetc从文件读取字符int fgetc(FILE *stream);
fputc向文件写入字符int fputc(int c, FILE *stream);
ungetc放回字符到输入流int ungetc(int c, FILE *stream);

2.2.4 字符串输入/输出函数

函数名功能原型
gets从标准输入读取字符串(不安全)char *gets(char *s);
puts向标准输出写入字符串int puts(const char *s);
fgets从文件读取字符串char *fgets(char *s, int size, FILE *stream);
fputs向文件写入字符串int fputs(const char *s, FILE *stream);

2.3 使用示例

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
#include <stdio.h>

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

// 打开文件
fp = fopen("example.txt", "w+");
if (fp == NULL) {
perror("fopen");
return 1;
}

// 写入文件
fprintf(fp, "Hello, world!\n");
fprintf(fp, "This is a test.\n");

// 重置文件位置
rewind(fp);

// 读取文件
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}

// 关闭文件
fclose(fp);

return 0;
}

3. 字符串处理库

3.1 字符串处理库概述

字符串处理库(<string.h>)提供了字符串操作的函数,是C语言中最常用的库之一。

3.2 常用函数

3.2.1 字符串操作函数

函数名功能原型
strlen获取字符串长度size_t strlen(const char *s);
strcpy复制字符串(不安全)char *strcpy(char *dest, const char *src);
strncpy安全的复制字符串char *strncpy(char *dest, const char *src, size_t n);
strcat连接字符串(不安全)char *strcat(char *dest, const char *src);
strncat安全的连接字符串char *strncat(char *dest, const char *src, size_t n);
strcmp比较字符串int strcmp(const char *s1, const char *s2);
strncmp比较字符串前n个字符int strncmp(const char *s1, const char *s2, size_t n);
strchr查找字符char *strchr(const char *s, int c);
strrchr反向查找字符char *strrchr(const char *s, int c);
strstr查找子字符串char *strstr(const char *haystack, const char *needle);
strtok分割字符串char *strtok(char *str, const char *delim);
memset填充内存void *memset(void *s, int c, size_t n);
memcpy复制内存void *memcpy(void *dest, const void *src, size_t n);
memmove安全的复制内存void *memmove(void *dest, const void *src, size_t n);
memcmp比较内存int memcmp(const void *s1, const void *s2, size_t n);

3.3 使用示例

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>

int main() {
char str1[50] = "Hello, ";
char str2[] = "world!";
char str3[50];

// 连接字符串
strcat(str1, str2);
printf("str1: %s\n", str1);

// 复制字符串
strcpy(str3, str1);
printf("str3: %s\n", str3);

// 比较字符串
if (strcmp(str1, str3) == 0) {
printf("str1 and str3 are equal\n");
}

// 获取字符串长度
printf("Length of str1: %zu\n", strlen(str1));

// 查找子字符串
char *ptr = strstr(str1, "world");
if (ptr != NULL) {
printf("Found 'world' at position: %ld\n", ptr - str1);
}

return 0;
}

4. 数学库

4.1 数学库概述

数学库(<math.h>)提供了数学计算的函数,使用时需要链接数学库(-lm)。

4.2 常用函数

4.2.1 基本数学函数

函数名功能原型
sin正弦函数double sin(double x);
cos余弦函数double cos(double x);
tan正切函数double tan(double x);
asin反正弦函数double asin(double x);
acos反余弦函数double acos(double x);
atan反正切函数double atan(double x);
atan2反正切函数(两个参数)double atan2(double y, double x);
sinh双曲正弦函数double sinh(double x);
cosh双曲余弦函数double cosh(double x);
tanh双曲正切函数double tanh(double x);

4.2.2 指数和对数函数

函数名功能原型
exp指数函数double exp(double x);
log自然对数double log(double x);
log10以10为底的对数double log10(double x);
pow幂函数double pow(double x, double y);
sqrt平方根double sqrt(double x);
cbrt立方根double cbrt(double x);

4.2.3 取整和绝对值函数

函数名功能原型
ceil向上取整double ceil(double x);
floor向下取整double floor(double x);
round四舍五入double round(double x);
fabs绝对值double fabs(double x);
abs整数绝对值int abs(int x);
labs长整数绝对值long labs(long x);
llabs长 long 整数绝对值long long llabs(long long x);

4.3 使用示例

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
#include <stdio.h>
#include <math.h>

int main() {
double x = 1.0;
double y = 2.0;

// 基本数学函数
printf("sin(1.0) = %f\n", sin(x));
printf("cos(1.0) = %f\n", cos(x));
printf("tan(1.0) = %f\n", tan(x));

// 指数和对数函数
printf("exp(1.0) = %f\n", exp(x));
printf("log(1.0) = %f\n", log(x));
printf("pow(2.0, 3.0) = %f\n", pow(y, 3.0));
printf("sqrt(4.0) = %f\n", sqrt(y * y));

// 取整和绝对值函数
printf("ceil(1.5) = %f\n", ceil(1.5));
printf("floor(1.5) = %f\n", floor(1.5));
printf("round(1.5) = %f\n", round(1.5));
printf("fabs(-1.5) = %f\n", fabs(-1.5));

return 0;
}

5. 时间和日期库

5.1 时间和日期库概述

时间和日期库(<time.h>)提供了时间和日期操作的函数。

5.2 常用函数

5.2.1 时间操作函数

函数名功能原型
time获取当前时间time_t time(time_t *t);
ctime转换时间为字符串char *ctime(const time_t *t);
asctime转换结构体为字符串char *asctime(const struct tm *tm);
localtime转换时间为本地时间struct tm *localtime(const time_t *t);
gmtime转换时间为GMT时间struct tm *gmtime(const time_t *t);
mktime转换结构体为时间time_t mktime(struct tm *tm);
difftime计算时间差double difftime(time_t time1, time_t time0);
strftime格式化时间为字符串size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *tm);

5.3 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <time.h>

int main() {
time_t current_time;
struct tm *local_time;
char time_string[50];

// 获取当前时间
current_time = time(NULL);
printf("Current time: %ld\n", current_time);

// 转换为本地时间
local_time = localtime(&current_time);

// 格式化为字符串
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
printf("Formatted time: %s\n", time_string);

// 使用ctime
printf("ctime: %s", ctime(&current_time));

return 0;
}

6. 内存分配库

6.1 内存分配库概述

内存分配库(<stdlib.h>)提供了动态内存分配的函数,是C语言中管理内存的重要工具。

6.2 常用函数

6.2.1 内存分配函数

函数名功能原型
malloc分配内存void *malloc(size_t size);
calloc分配并清零内存void *calloc(size_t nmemb, size_t size);
realloc重新分配内存void *realloc(void *ptr, size_t size);
free释放内存void free(void *ptr);
aligned_alloc分配对齐内存void *aligned_alloc(size_t alignment, size_t size);

6.2.2 其他常用函数

函数名功能原型
exit终止程序void exit(int status);
atoi字符串转整数int atoi(const char *nptr);
atol字符串转长整数long atol(const char *nptr);
atoll字符串转长 long 整数long long atoll(const char *nptr);
strtod字符串转双精度浮点数double strtod(const char *nptr, char **endptr);
strtol字符串转长整数long strtol(const char *nptr, char **endptr, int base);
strtoll字符串转长 long 整数long long strtoll(const char *nptr, char **endptr, int base);
rand生成随机数int rand(void);
srand设置随机数种子void srand(unsigned int seed);
qsort快速排序void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
bsearch二分查找void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

6.3 使用示例

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
#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}

int main() {
int *arr;
int n = 5;
int i;

// 分配内存
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
perror("malloc");
return 1;
}

// 初始化数组
srand(time(NULL));
for (i = 0; i < n; i++) {
arr[i] = rand() % 100;
printf("arr[%d] = %d\n", i, arr[i]);
}

// 排序数组
qsort(arr, n, sizeof(int), compare);

// 打印排序后的数组
printf("Sorted array:\n");
for (i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}

// 释放内存
free(arr);

return 0;
}

7. 进程控制库

7.1 进程控制库概述

进程控制库(<unistd.h>)提供了进程管理的函数,主要用于Unix-like系统。

7.2 常用函数

函数名功能原型
fork创建子进程pid_t fork(void);
exec 系列执行程序int execl(const char *path, const char *arg, ...);
wait等待子进程结束pid_t wait(int *status);
waitpid等待指定子进程结束pid_t waitpid(pid_t pid, int *status, int options);
getpid获取进程IDpid_t getpid(void);
getppid获取父进程IDpid_t getppid(void);
sleep睡眠指定秒数unsigned int sleep(unsigned int seconds);
usleep睡眠指定微秒数int usleep(useconds_t usec);
nanosleep睡眠指定纳秒数int nanosleep(const struct timespec *req, struct timespec *rem);
getuid获取用户IDuid_t getuid(void);
geteuid获取有效用户IDuid_t geteuid(void);
getgid获取组IDgid_t getgid(void);
getegid获取有效组IDgid_t getegid(void);
chdir改变当前目录int chdir(const char *path);
getcwd获取当前目录char *getcwd(char *buf, size_t size);
unlink删除文件int unlink(const char *pathname);
rename重命名文件int rename(const char *oldpath, const char *newpath);

7.3 使用示例

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
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
pid_t pid;

// 创建子进程
pid = fork();

if (pid < 0) {
// 错误
perror("fork");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process: PID = %d, PPID = %d\n", getpid(), getppid());
sleep(1);
printf("Child process exiting\n");
return 0;
} else {
// 父进程
int status;
printf("Parent process: PID = %d, Child PID = %d\n", getpid(), pid);
// 等待子进程结束
wait(&status);
printf("Parent process: Child exited with status %d\n", WEXITSTATUS(status));
return 0;
}
}

8. 网络编程库

8.1 网络编程库概述

网络编程库(<sys/socket.h>)提供了网络通信的函数,是C语言中进行网络编程的基础。

8.2 常用函数

8.2.1 套接字函数

函数名功能原型
socket创建套接字int socket(int domain, int type, int protocol);
bind绑定地址int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen监听连接int listen(int sockfd, int backlog);
accept接受连接int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
connect连接服务器int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
send发送数据ssize_t send(int sockfd, const void *buf, size_t len, int flags);
recv接收数据ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sendto发送数据(UDP)ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
recvfrom接收数据(UDP)ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
close关闭套接字int close(int fd);
getaddrinfo获取地址信息int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
freeaddrinfo释放地址信息void freeaddrinfo(struct addrinfo *res);
inet_ntop网络地址转字符串const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
inet_pton字符串转网络地址int inet_pton(int af, const char *src, void *dst);

8.3 使用示例

服务器端

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
char *hello = "Hello from server";

// 创建套接字文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 绑定套接字到端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 开始监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}

// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}

// 读取客户端消息
read(new_socket, buffer, BUFFER_SIZE);
printf("Client: %s\n", buffer);

// 发送响应
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");

// 关闭套接字
close(new_socket);
close(server_fd);

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
char *hello = "Hello from client";

// 创建套接字文件描述符
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("Socket creation error\n");
return -1;
}

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);

// 转换IPv4地址
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
printf("Invalid address\n");
return -1;
}

// 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("Connection Failed\n");
return -1;
}

// 发送消息
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");

// 读取响应
read(sock, buffer, BUFFER_SIZE);
printf("Server: %s\n", buffer);

// 关闭套接字
close(sock);

return 0;
}

9. 信号处理库

9.1 信号处理库概述

信号处理库(<signal.h>)提供了信号处理的函数,用于处理系统发送的各种信号。

9.2 常用函数

函数名功能原型
signal设置信号处理函数void (*signal(int signum, void (*handler)(int)))(int);
sigaction设置信号处理函数(更灵活)int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
kill发送信号int kill(pid_t pid, int sig);
raise向自身发送信号int raise(int sig);
alarm设置闹钟unsigned int alarm(unsigned int seconds);
pause暂停进程直到信号到达int pause(void);
sigprocmask阻塞/解除阻塞信号int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigemptyset初始化信号集为空int sigemptyset(sigset_t *set);
sigfillset初始化信号集为所有信号int sigfillset(sigset_t *set);
sigaddset向信号集添加信号int sigaddset(sigset_t *set, int signum);
sigdelset从信号集删除信号int sigdelset(sigset_t *set, int signum);
sigismember检查信号是否在信号集中int sigismember(const sigset_t *set, int signum);

9.3 使用示例

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
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void signal_handler(int signum) {
printf("Received signal %d\n", signum);
if (signum == SIGINT) {
printf("Ctrl+C pressed, exiting...\n");
_exit(0);
}
}

int main() {
// 设置信号处理函数
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

printf("Waiting for signals... (Press Ctrl+C to exit)\n");

// 无限循环
while (1) {
sleep(1);
printf("Still waiting...\n");
}

return 0;
}

10. 线程编程库

10.1 线程编程库概述

线程编程库(<pthread.h>)提供了多线程编程的函数,使用时需要链接线程库(-lpthread)。

10.2 常用函数

函数名功能原型
pthread_create创建线程int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
pthread_join等待线程结束int pthread_join(pthread_t thread, void **retval);
pthread_detach分离线程int pthread_detach(pthread_t thread);
pthread_exit终止线程void pthread_exit(void *retval);
pthread_self获取线程IDpthread_t pthread_self(void);
pthread_equal比较线程IDint pthread_equal(pthread_t t1, pthread_t t2);
pthread_mutex_init初始化互斥锁int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
pthread_mutex_lock加锁int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_trylock尝试加锁int pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_unlock解锁int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_destroy销毁互斥锁int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_cond_init初始化条件变量int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_wait等待条件变量int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_signal唤醒一个等待的线程int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast唤醒所有等待的线程int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_cond_destroy销毁条件变量int pthread_cond_destroy(pthread_cond_t *cond);

10.3 使用示例

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
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define NUM_THREADS 5

pthread_mutex_t mutex;
int counter = 0;

void *thread_function(void *arg) {
int thread_id = *((int *)arg);

// 加锁
pthread_mutex_lock(&mutex);

// 临界区
counter++;
printf("Thread %d: counter = %d\n", thread_id, counter);

// 解锁
pthread_mutex_unlock(&mutex);

pthread_exit(NULL);
}

int main() {
pthread_t threads[NUM_THREADS];
int thread_ids[NUM_THREADS];
int i;

// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);

// 创建线程
for (i = 0; i < NUM_THREADS; i++) {
thread_ids[i] = i;
if (pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]) != 0) {
perror("pthread_create");
return 1;
}
}

// 等待所有线程结束
for (i = 0; i < NUM_THREADS; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("pthread_join");
return 1;
}
}

// 销毁互斥锁
pthread_mutex_destroy(&mutex);

printf("Final counter value: %d\n", counter);

return 0;
}

11. 系统调用库

11.1 系统调用库概述

系统调用库(<sys/syscall.h>)提供了直接调用系统服务的函数,是操作系统与应用程序之间的接口。

11.2 常用函数

函数名功能原型
syscall直接调用系统调用long syscall(long number, ...);
read读取文件ssize_t read(int fd, void *buf, size_t count);
write写入文件ssize_t write(int fd, const void *buf, size_t count);
open打开文件int open(const char *pathname, int flags, mode_t mode);
close关闭文件int close(int fd);
lseek设置文件位置off_t lseek(int fd, off_t offset, int whence);
mkdir创建目录int mkdir(const char *pathname, mode_t mode);
rmdir删除目录int rmdir(const char *pathname);
chmod修改文件权限int chmod(const char *pathname, mode_t mode);
chown修改文件所有者int chown(const char *pathname, uid_t owner, gid_t group);
link创建硬链接int link(const char *oldpath, const char *newpath);
symlink创建符号链接int symlink(const char *target, const char *linkpath);
readlink读取符号链接ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
unlink删除文件int unlink(const char *pathname);
rename重命名文件int rename(const char *oldpath, const char *newpath);

11.3 使用示例

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
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

int main() {
int fd;
char buffer[100];
ssize_t n;

// 打开文件
fd = open("example.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}

// 写入文件
n = write(fd, "Hello, world!\n", 13);
if (n < 0) {
perror("write");
close(fd);
return 1;
}

// 关闭文件
close(fd);

// 重新打开文件读取
fd = open("example.txt", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}

// 读取文件
n = read(fd, buffer, sizeof(buffer));
if (n < 0) {
perror("read");
close(fd);
return 1;
}

// 打印读取的内容
write(STDOUT_FILENO, buffer, n);

// 关闭文件
close(fd);

return 0;
}

12. 系统函数库的最佳实践

12.1 一般原则

  1. 包含正确的头文件:确保包含了所有需要的头文件
  2. 链接必要的库:如数学库(-lm)、线程库(-lpthread)等
  3. 检查函数返回值:始终检查函数的返回值,处理错误情况
  4. 释放资源:确保所有分配的资源都被释放,如文件描述符、内存等
  5. 使用安全的函数:避免使用不安全的函数,如getsstrcpy
  6. 遵循函数的调用约定:正确传递参数,处理返回值
  7. 了解函数的限制:如缓冲区大小、参数范围等
  8. 使用适当的错误处理:如perrorstrerror
  9. 测试边界情况:测试函数在边界情况下的行为
  10. 参考文档:查阅系统函数库的文档,了解函数的详细用法

12.2 性能优化

  1. 减少函数调用:对于频繁调用的函数,考虑内联或使用宏
  2. 缓存计算结果:对于计算密集型函数,缓存结果
  3. 使用适当的数据结构:选择高效的数据结构
  4. 避免不必要的转换:如字符串和数字之间的转换
  5. 使用编译器优化:如-O2-O3等优化选项

12.3 安全性

  1. 检查参数:验证所有函数参数的有效性
  2. 防止缓冲区溢出:使用安全的字符串处理函数
  3. 释放资源:确保所有分配的资源都被释放
  4. 避免使用不安全的函数:如getsstrcpy
  5. 使用地址随机化:编译时启用地址随机化
  6. 限制权限:最小化程序的权限
  7. 加密敏感数据:对敏感数据进行加密
  8. 防止注入攻击:如SQL注入、命令注入等

13. 系统函数库的跨平台兼容性

13.1 跨平台兼容性问题

  • 头文件差异:不同平台的头文件可能不同
  • 函数差异:不同平台的函数可能有不同的实现或行为
  • 类型差异:不同平台的类型大小可能不同
  • 系统调用差异:不同平台的系统调用可能不同
  • 路径分隔符:不同平台的路径分隔符可能不同(/ vs \

13.2 解决方案

  1. 使用条件编译:根据不同平台使用不同的代码
  2. 使用宏定义:定义平台相关的宏
  3. 使用抽象层:为平台相关的功能创建抽象层
  4. 使用跨平台库:如SDL、Boost等
  5. 测试多个平台:确保代码在多个平台上都能正常工作
  6. 参考标准:遵循C语言标准,避免使用非标准功能

13.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

#ifdef _WIN32
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#elif defined(__linux__)
#include <unistd.h>
#define SLEEP(ms) usleep((ms) * 1000)
#elif defined(__APPLE__)
#include <unistd.h>
#define SLEEP(ms) usleep((ms) * 1000)
#else
#error Unsupported platform
#endif

int main() {
printf("Sleeping for 1 second...\n");
SLEEP(1000);
printf("Woke up!\n");
return 0;
}

14. 系统函数库的调试

14.1 调试工具

  • gdb:GNU调试器
  • valgrind:内存分析工具
  • strace:系统调用跟踪工具
  • ltrace:库函数调用跟踪工具
  • gprof:性能分析工具
  • addr2line:地址转换工具

14.2 调试技巧

  1. 使用printf:在关键位置添加打印语句
  2. 使用assert:在关键位置添加断言
  3. 检查返回值:始终检查函数的返回值
  4. 使用调试器:使用gdb等调试器逐步执行代码
  5. 使用内存分析工具:使用valgrind等工具检查内存问题
  6. 使用系统调用跟踪:使用strace等工具跟踪系统调用
  7. 查看核心转储:分析程序崩溃时生成的核心转储文件
  8. 使用日志:使用日志记录程序的执行情况

14.3 常见问题

14.3.1 段错误

原因:访问了无效的内存地址
解决:使用gdb或valgrind检查内存访问

14.3.2 内存泄漏

原因:分配的内存未被释放
解决:使用valgrind检查内存泄漏

14.3.3 文件操作错误

原因:文件不存在、权限不足等
解决:检查文件路径、权限,使用perror查看错误信息

14.3.4 网络连接错误

原因:网络不可达、端口未开放等
解决:检查网络连接、防火墙设置,使用perror查看错误信息

15. 示例代码

15.1 综合示例:文件复制程序

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
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 4096

int main(int argc, char *argv[]) {
FILE *src, *dest;
char buffer[BUFFER_SIZE];
size_t n;

// 检查参数
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
return 1;
}

// 打开源文件
src = fopen(argv[1], "rb");
if (src == NULL) {
perror("fopen source");
return 1;
}

// 打开目标文件
dest = fopen(argv[2], "wb");
if (dest == NULL) {
perror("fopen destination");
fclose(src);
return 1;
}

// 复制文件
while ((n = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {
if (fwrite(buffer, 1, n, dest) != n) {
perror("fwrite");
fclose(src);
fclose(dest);
return 1;
}
}

// 检查读取错误
if (ferror(src)) {
perror("fread");
fclose(src);
fclose(dest);
return 1;
}

// 关闭文件
if (fclose(src) != 0) {
perror("fclose source");
fclose(dest);
return 1;
}

if (fclose(dest) != 0) {
perror("fclose destination");
return 1;
}

printf("File copied successfully\n");

return 0;
}

15.2 综合示例:简单的HTTP服务器

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 4096

int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nHello World!";

// 创建套接字文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 绑定套接字到端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 开始监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}

printf("Server listening on port %d...\n", PORT);

while (1) {
// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}

// 读取请求
read(new_socket, buffer, BUFFER_SIZE);
printf("Request:\n%s\n", buffer);

// 发送响应
send(new_socket, response, strlen(response), 0);
printf("Response sent\n");

// 关闭连接
close(new_socket);
}

return 0;
}