C语言的库文件包括:pthread线程、assert断言、string字符串、time时间、math数学运算、std开头的标准库、sys开头的系统库等。其中,标准库有stdalign.h、stdarg.h、stdatomic.h、stdbool.h、stddef.h、stdint.h、stdio.h、stdlib.h。系统库有sys/mman.h、sys/stat.h、sys/ioctl.h、sys/file.h。本篇文章主要介绍std标准库和sys系统库。
上一篇C语言库文件的介绍可查看:C语言最佳实践之库文件介绍(上)
目录
一、stddef.h预先宏定义
1、offsetof
2、ptrdiff_t
3、__func__与__LINE__
二、stdio.h
1、打开文件
2、读写数据
3、移动文件指针
4、获取文件长度
5、重命名文件
6、判断是否到末尾
7、重置文件到开头位置
9、删除文件
10、关闭文件
三、stdlib.h
四、string.h
五、stdatomic.h
1、原子类型
2、原子初始化
3、原子读写
4、原子加减
5、原子交换
6、内存屏障
六、stdarg.h
七、sys/mmap.h
八、sys/stat.h
1、stat结构体
2、统计文件信息
九、signal.h
1、信号分类
2、信号捕获
一、stddef.h预先宏定义 1、offsetof
stddef提供offsetof、size_t、ptrdiff等宏定义。其中offsetof用于获取成员变量在结构体的偏移位置。offsetof(t, d)的示例代码如下:
struct Programmer {
int age;
char* name;
char* post;
};
void print_offset() {
printf("offset of struct =%lu", offsetof(struct Programmer, name));
}
2、ptrdiff_t
ptrdiff_t用于计算两个指针相减的差值。比如一个指针指向字符串的头,另一个指针指向字符串的尾,那么两个指针的差值等于字符串长度。示例代码如下:
char* str = "hello, world!";
char* ptr_start = str;
char* ptr_end = str + strlen(str);
ptrdiff_t diff = ptr_end - ptr_start;
printf("ptr diff=%td\n", diff);
3、__func__与__LINE__
在调试时,我们需要打印哪个文件、哪个函数、哪一行有问题,__FILE__、__func__、__LINE__就派上用场了。如果要获取当前格式化时间戳,可以用__TIMESTAMP__。这些宏定义都是以双下划线开头。示例代码如下:
printf("func=%s\n", __func__);
printf("file=%s\n", __FILE__);
printf("line=%d\n", __LINE__);
printf("timestamp=%s\n", __TIMESTAMP__);
对应的打印输出如下:
func=macro_define
file=/Users/frank/Documents/FFmpegAndroid/app/src/main/cpp/test_api.c
line=357
timestamp=Sat Jun 4 17:19:22 2022
二、stdio.h
stdio.h提供文件的常用 *** 作,包括:打开、读写、关闭、重命名、删除、刷新、标准输出等等。具体介绍如下表所示:
函数 | 描述 |
FILE* fopen(const char* filename, const char* mode) | 打开文件,mode包括:只读、只写、读写、追加等 |
size_t fread(void* ptr, size_t size, size_t count, FILE* stream) | 读取文件,读取指定大小的内容到缓冲区 |
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream) | 写入文件,如果是读写模式会覆盖原始内容,追加是从末尾开始写 |
int fseek(FILE* stream, long offset, int whence) | 移动文件指针,seek模式:SEEK_SET、SEEK_CUR\SEEK_END |
long ftell(FILE* stream) | 获取当前位置,结合fseek使用 |
int fflush(FILE* stream) | 刷新文件缓冲区 |
void rewind(FILE* stream) | 重置文件指针到文件开头 |
int remove(const char* filename) | 删除文件,返回成功或失败 |
int rename(const char* old, const char* new) | 重命名文件 |
int fclose(FILE* stream) | 关闭文件 |
int printf(const char* format, ...) | 打印输出到控制台 |
int scanf(const char* format, ...) | 读入内容 |
int sprintf(char* s, const char* format, ...) | 输出到字符串 |
int fprintf(FILE* stream, const char* format, ...) | 输出到文件 |
int vprintf(const char* format, va_list arg) | 输出到可变参数列表 |
int getc(FILE* stream) | 从文件获取一个字符 |
int putc(int c, FILE* stream) | 写入一个字符到文件 |
int feof(FILE* stream) | 判断文件是否到末尾,eof=-1 |
int ferror(FILE* stream) | 获取文件错误码 |
void clearerr(FILE* stream) | 清除异常信息 |
void perror(const char* s) | 错误描述信息输出到标准错误 |
void setbuf(FILE* stream, char* buf) | 设置文件缓冲区 |
char* path = "sdcard/hello.txt";
FILE* file = fopen(path, "wb+");
2、读写数据
char *buf0 = "Just do it";
size_t size = strlen(buf0);
fwrite(buf0, size, 1, file); // 写文件
fseek(file, 0, SEEK_SET);
char *buf1 = (char*) malloc(size * sizeof(char));
fread(buf1, size * sizeof(char), 1, file); // 读文件
3、移动文件指针
fseek(file, 10, SEEK_SET);
4、获取文件长度
fseek(file, 0, SEEK_END);
long len = ftell(file);
5、重命名文件
rename("sdcard/2.txt", "sdcard/222.txt");
6、判断是否到末尾
int ret = feof(file);
7、重置文件到开头位置
rewind(file);
9、删除文件
remove("sdcard/1.txt");
10、关闭文件
fclose(file);
三、stdlib.h
stdlib.h提供内存分配与释放、随机数的生成、进程退出、执行命令行、字符串转数值类型、二分查找算法、快排算法、求绝对值等 *** 作。其中内存分配包括:malloc、calloc、realloc、aligned_alloc。具体对比如下表所示:
malloc | 内存分配,默认内存对齐 |
calloc | 内存分配,并且初始化 |
realloc | 重新分配,拷贝旧内存到新区域 |
aligned_alloc | 对齐分配,指定对齐单位大小 |
stdlib相关的函数如下:
void lib_api() {
// 内存分配
void* malloc(size_t size);
// 重新分配内存,拷贝旧内存到新内存空间,释放旧内存
void* realloc(void* ptr, size_t size);
// 内存对齐分配
void *aligned_alloc(size_t alignment, size_t size);
// 内存分配并且初始化,相当于malloc + memset
void* calloc(size_t nmemb, size_t size);
// 释放内存
void free(void* ptr);
// 异常的进程终止
void abort(void);
// 注册终止函数,在exit退出时调用
int atexit(void (*func)(void));
// status=0为正常退出,status!=0为异常退出
void exit(int status);
// 字符串转数值类型
double atof (const char* nptr);
int atoi (const char* nptr);
long atol (const char* nptr);
// 执行命令行,system在原进程开辟新的进程,exec用新进程覆盖原进程
int system(const char* string);
// 二分法查找
void* bsearch(const void* key, const void* base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
// 快排算法
void qsort(void* base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
// 求绝对值
int abs(int j);
// 生成随机数
int rand(void);
void srand(unsigned int seed);
long random();
}
四、string.h
string.h提供字符串的常见 *** 作:拷贝、比较、拼接、查找、分割。其中,部分的mem *** 作和str *** 作功能一样。比如,memcpy拷贝内存,可以拷贝结构体、类、数组等,但需要指定长度;strcpy拷贝字符串,仅限于字符串,不需要指定长度。两者对比如下:
void* memcpy(void* dest, const void* src, size_t n) 拷贝内存,包括结构体、类、数组等 | char* strcpy (char* dest, const char* src) 拷贝字符串 |
int memcmp(const void* str1, const void* str2, size_t n) 内存值比较 | int strcmp (const char* str1, const char* str2) 字符串比较 |
void* memchr(const void* str, int c, size_t n) 内存区域查找字符 | char* strchr(const char* str, int c) 字符串中查找字符 |
其他的常用函数如下:
void string_api() {
// 移动指定长度的源内存到目的内存
void* memmove(void* dest, const void* src, size_t n);
// 设置内存值,用于内存初始化
void* memset(void* str, int c, size_t n);
// 拼接字符串
char* strcat (char* dest, const char* src);
// 查找字符在字符串最后一次出现的位置
char* strrchr(const char* str, int c);
// 查找str2在str1字符串出现的位置
char* strstr(const char* str1, const char* str2);
// 分割字符串
char* strtok(char* src, const char* delim);
// 把错误码转换为字符串
char* strerror(int errnum);
// 获取字符串长度
size_t strlen(const char* s);
}
五、stdatomic.h
1、原子类型
stdatomic.h提供原子 *** 作,线程安全,比锁更加轻量级。支持的原子类型包括:bool、char、int、short、long等。宏定义如下(包括但不限于):
typedef _Atomic(bool) atomic_bool;
typedef _Atomic(char) atomic_char;
typedef _Atomic(short) atomic_short;
typedef _Atomic(int) atomic_int;
typedef _Atomic(long) atomic_long;
2、原子初始化
使用atomic_init()进行初始化,需要注意,第一个参数是对象的地址,代码如下:
atomic_int count;
atomic_init(&count, 1);
3、原子读写
使用atomic_load()进行读取,atomic_store_explicit()进行写入,代码如下:
// 读取
atomic_load(&count);
// 写入
atomic_store_explicit(&count, 3, memory_order_seq_cst);
4、原子加减
原子运算支持加、减、或、异或、与。以加减运算为例:
atomic_fetch_add(&count, 5);
atomic_fetch_sub(&count, 3);
5、原子交换
原子交换步骤分为三步:读取、比较、写入。代码如下:
atomic_exchange(&count, 6);
6、内存屏障
原子 *** 作提供fence内存屏障,与关键字volatile类似,保证内存有序性。有一个memory_order枚举类型,包括获取 *** 作、释放 *** 作、获取与释放、消费 *** 作、无序性、有序性等,具体如下:
typedef enum {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;
对应的函数API如下:
atomic_thread_fence(memory_order order);
六、stdarg.h
stdarg.h提供可变参数的遍历,由va_start()、va_arg()和va_end()三个函数组成,还有一个va_list可变参数列表。其中va_start()是参数列表的开始,va_arg()是获取列表的下一个,va_end()结束遍历释放内存。示例代码如下:
void sum_args(int args, ...) {
int sum = 0;
va_list ap;
va_start(ap, args);
for (int i=0; i
大家可以猜猜这个函数调用的结果:
sum_args(3, 11, 22, 33);
七、sys/mmap.h
mmap.h提供内存映射的函数,相关函数声明如下:
/**
* [mmap](http://man7.org/linux/man-pages/man2/mmap.2.html)
* creates a memory mapping for the given range.
*
* Returns the address of the mapping on success,
* and returns `MAP_FAILED` and sets `errno` on failure.
*/
void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset);
/**
* [munmap](http://man7.org/linux/man-pages/man2/munmap.2.html)
* deletes a memory mapping for the given range.
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
int munmap(void* addr, size_t size);
mmap()函数的第四个参数prot类型包括:读、写、执行、无权限。如下图所示:
第五个参数flag标志位包括:共享的、私有的、固定的。如下图所示:
内存映射的示例代码如下:
void mapping() {
char *buf = "hello, world";
// file:use data in buf
int fd = open("sdcard/hello.txt", O_RDWR);
write(fd, buf, strlen(buf));
// mapping:use data at address
fd = open("sdcard/hello.txt", O_RDWR);
void* address = mmap(0, strlen(buf), PROT_READ, MAP_PRIVATE, fd, 0);
}
八、sys/stat.h
1、stat结构体
stat.h用于获取文件状态信息,包括:设备id、文件大小、文件访问权限、文件修改时间、文件访问时间、序列号、文件链接的数量等。stat结构体如下:
struct stats {
dev_t st_dev; // ID of device containing file
ino_t st_ino; // file serial number
mode_t st_mode; // mode of file
nlink_t st_nlink; // number of links to the file
uid_t st_uid; // user ID of file
gid_t st_gid; // group ID of file
dev_t st_rdev; // device ID
off_t st_size; // file size in bytes
int st_blksize; // a filesystem-specific preferred I/O block size
long st_blocks; // number of blocks allocated for this object
struct timespec st_atim; // time of last access
struct timespec st_mtim; // time of last data modification
struct timespec st_ctim; // time of last status change
};
2、统计文件信息
stat()函数的使用示例如下:
void file_stat() {
struct stat buf;
const char* path = "sdcard/hello.txt";
stat(path, &buf);
printf("file mode:%u\n",buf.st_mode); // 文件访问权限
printf("file size:%lu\n",buf.st_size); // 文件大小
printf("file access time:%lu\n",buf.st_atime); // 文件访问时间
printf("file modify time:%lu\n",buf.st_mtime); // 文件修改时间
}
九、signal.h
1、信号分类
signal.h是系统提供的信号,包括:非法指令、函数陷阱、异常终止、非法地址、杀掉进程、内存异常、进程退出、浮点异常等。比如,访问一个空指针,或者已经释放的指针,或者访问指针指向的内存区域超出边界,系统会发送SIGSEGV信号。再比如,使用命令行杀掉进程kill -9 pid,系统会发生SIGKILL信号。具体信号与数值如下表所示:
信号 | 描述 | 信号 | 描述 |
SIGHUP 1 | 信号挂起 | SIGCHLD 17 | 子进程结束 |
SIGINT 2 | 信号中断 | SIGCONT 18 | 进程恢复 |
SIGQUIT 3 | 进程退出 | SIGSTOP 19 | 程序停止 |
SIGILL 4 | 非法指令 | SIGTSTP 20 | 停止进程 |
SIGTRAP 5 | 函数陷阱 | SIGTTIN 21 | 读取数据 |
SIGABRT 6 | 异常终止 | SIGTTOU 22 | 写入数据 |
SIGBUS 7 | 非法地址 | SIGURG 23 | 紧急处理 |
SIGFPE 8 | 浮点溢出 | SIGXCPU 24 | 超出CPU限制 |
SIGKILL 9 | 杀掉进程 | SIGXFSZ 25 | 扩大文件 |
SIGUSR1 10 | 用户保留 | SIGVTALRM 26 | 虚拟时钟 |
SIGSEGV 11 | 内存异常 | SIGPROF 27 | CPU时钟 |
SIGUSR2 12 | 用户保留 | SIGWINCH 28 | 窗口变化 |
SIGPIPE 13 | 信号管道 | SIGIO 29 | 文件描述符就绪 |
SIGALRM 14 | 定时时钟 | SIGPWR 30 | 电源异常 |
SIGTERM 15 | 程序结束 | SIGSYS 31 | 非法系统调用 |
SIGSTKFLT 16 | 处理器栈异常 | SIGRTMIN 32 | 实时信号 |
信号处理函数包括:信号捕获、中断、等待、挂起、生成信号、杀掉进程。具体如下:
int sigaction(int signal, struct sigaction* new_action, struct sigaction* old_action); // 捕获信号
int siginterrupt(int signal, int flag); // 信号中断
int sigwait(const sigset_t* set, int* signal); // 信号等待
int sigsuspend(const sigset_t* mask); // 信号挂起
int raise(int signal); // 发送信号
int kill(pid_t pid, int signal); // 杀掉进程
下面我们来模拟发送信号与捕获信号的过程:
void process_signal() {
printf("receive sig=%d\n", SIGFPE);
}
void test_signal() {
struct sigaction action;
action.sa_handler = process_signal; // 声明信号处理函数
sigaction(SIGFPE, &action, NULL); // 捕获信号
raise(SIGFPE); // 发送信号
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)