C 动态内存扩展
前言
今天看 cppreference.com 的时候偶然发现这么个有意思的C语言特性(扩展),由于相应的中文资料很少(英文资料也不多),所以决定写这篇文章分享一下。
本文只挑选重要的函数进行介绍。
该扩展为C语言标准中可选实现内容,只有部分编译器能实现(POSIX/Linux)。本文所有内容测试环境如下:
- archlinux x86_64 (kernel: 6.4.12-zen1-1-zen)
- gcc 12.2.1 (20230801) (POSIX)
- glibc 2.38-3
函数定义
以下定义均经过处理,方便阅读。
分配动态内存
FILE *open_memstream (char **bufloc, size_t *sizeloc);- 分配动态内存到 bufloc,其大小为 sizeloc 指向的数字。返回的文件指针可用于写入数据。
写入动态内存
size_t getline (char **lineptr, size_t *n, FILE *stream); // 1
size_t getdelim (char **lineptr, size_t *n, int delimiter, FILE *stream); // 2- 传入下列参数调用函数 3。
- 读取 stream 中内容直至读取到 delimiter,将读取数据存入 lineptr 指向的内存块并自动扩充该内存块大小,将读取结束后动态内存大小存入 n 指向的数字。
getdelim(lineptr, n, '\n', stream); // getline分配并写入动态内存
int asprintf (char **ptr, const char *fmt, ...);- 以类似于 printf 的方式将格式化内容输出至 ptr 指向的内存块并自动扩充该内存块大小。
示例代码
// #define _GNU_SOURCE // 使 IDE 不要报错
#include <stdio.h>
#include <stdlib.h>
#include <time.h> // time()
int main(void) {
char *buffer = NULL;
size_t size = 0;
FILE *stream;
size_t read;
read = getline(&buffer, &size, stdin);
printf("buffer 中的内容:\n%s大小:%zu\n字符数:%zu\n", buffer, size, read);
free(buffer);
stream = open_memstream(&buffer, &size);
if (stream == NULL) {
puts("无法打开动态内存数据流。");
exit(EXIT_FAILURE);
}
srand((unsigned int)time(NULL));
for (int i = 0; i < rand() % 100; i++) {
fprintf(stream, "%d ", i);
}
fflush(stream);
puts("在一个字符串中的随机个(0~99)数字:");
puts(buffer);
free(buffer);
asprintf(&buffer, "一个随机数:%d", rand());
puts(buffer);
free(buffer);
return 0;
}原理分析
直接看 glibc 源码。其实就是初始分配 120 字节,当超过上限时 realloc 翻倍。
// glibc/libio/iogetdelim.c __getdelim
// ...
if (*lineptr == NULL || *n == 0)
{
*n = 120;
*lineptr = (char *) malloc (*n); // 初始内存大小 120
if (*lineptr == NULL)
{
fseterr_unlocked (fp);
result = -1;
goto unlock_return;
}
}
// ...
/* Make enough space for len+1 (for final NUL) bytes. */ // 考虑字符串末尾 '\0'
needed = cur_len + len + 1;
if (needed > *n)
{
char *new_lineptr;
if (needed < 2 * *n)
needed = 2 * *n; /* Be generous. */
new_lineptr = (char *) realloc (*lineptr, needed); // 达到内存上限后再分配一倍内存
if (new_lineptr == NULL)
{
fseterr_unlocked (fp);
result = -1;
goto unlock_return;
}
*lineptr = new_lineptr;
*n = needed;
}
memcpy (*lineptr + cur_len, (void *) fp->_IO_read_ptr, len);
fp->_IO_read_ptr += len;
cur_len += len;
// ...总结
好像没啥用。(笑)