从《The.C.Programming.Language.2Nd.Ed 》5.17题出发,写两个分别使用和不使用动态分配内存拷贝字符串的程序。默认getline函数使用了malloc动态分配内存。
“Exercise 5−7. Rewrite readlines to store lines in an array supplied by main, rather than calling alloc to maintain storage. How much faster is the program?”
exercise_5_7_with_alloc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINES 1000
#define MAXLEN 1000
int readline(char *lineptr[], int maxlines);
void writelines(char *lineptr[], int nlines);
int main() {
char *lines[MAXLINES];
int nlines;
nlines = readline(lines, MAXLINES);
if (nlines >= 0) {
writelines(lines, nlines);
return 0;
} else {
printf("Error: input too big to handle\n");
return 1;
}
}
int readline(char *lineptr[], int maxlines) {
int nlines = 0;
char *line = NULL;
size_t len = 0;
while (nlines < maxlines && getline(&line, &len, stdin) != -1) {
line[strcspn(line, "\n")] = '\0'; // Remove trailing newline
lineptr[nlines] = line;
line = NULL;
nlines++;
}
free(line); // Free the memory allocated by getline
return nlines;
}
void writelines(char *lineptr[], int nlines) {
for (int i = 0; i < nlines; i++) {
printf("%s\n", lineptr[i]);
}
}
exercise_5_7_without_alloc.c
#include <stdio.h>
#include <string.h>
#define MAXLINES 1000
#define MAXLEN 1000
int readline(char lineptr[][MAXLEN], int maxlines);
void writelines(char lineptr[][MAXLEN], int nlines);
int main() {
char lines[MAXLINES][MAXLEN];
int nlines;
nlines = readline(lines, MAXLINES);
if (nlines >= 0) {
writelines(lines, nlines);
return 0;
} else {
printf("Error: input too big to handle\n");
return 1;
}
}
int readline(char lineptr[][MAXLEN], int maxlines) {
int nlines = 0;
char line[MAXLEN];
while (nlines < maxlines && fgets(line, MAXLEN, stdin) != NULL) {
line[strcspn(line, "\n")] = '\0'; // Remove trailing newline
strcpy(lineptr[nlines], line);
nlines++;
}
return nlines;
}
void writelines(char lineptr[][MAXLEN], int nlines) {
for (int i = 0; i < nlines; i++) {
printf("%s\n", lineptr[i]);
}
}
脚本生成测试文件:
#!/bin/bash
NUM_LINES=1000000 # 设置要生成的行数
OUTPUT_FILE=input_data.txt
# 生成随机文本行并写入文件
for ((i = 1; i <= NUM_LINES; i++)); do
echo "Line $i: $(openssl rand -base64 32)" >> $OUTPUT_FILE
done
echo "Generated $NUM_LINES random lines to $OUTPUT_FILE"
清除内存和Cash准备实验环境:
fgets
和 getline
都是用于从文件流中读取一行文本的函数,但它们有一些区别,主要是在以下几个方面:
-
接口和用法:
fgets
是标准C库中的函数,用于从指定的文件流中读取一行文本。它的原型为:char *fgets(char *str, int n, FILE *stream);
,其中str
是一个指向字符数组的指针,n
是要读取的最大字符数(包括换行符和 null 终止符),stream
是要读取的文件流。getline
是 POSIX 标准中定义的函数,用于从文件流中动态分配内存并读取一行文本。它的原型为:ssize_t getline(char **lineptr, size_t *n, FILE *stream);
,其中lineptr
是一个指向指针的指针,用于存储读取的文本内容,n
是指向lineptr
所指向的缓冲区大小的指针,stream
是要读取的文件流。
-
内存分配:
fgets
需要预先分配足够大小的缓冲区来存储读取的文本行。因此,你需要提前知道要读取的最大行长度,并分配相应大小的缓冲区。getline
会根据需要动态分配内存来存储读取的文本行,因此不需要提前知道行的最大长度。它会根据需要自动调整分配的内存大小,确保足够存储整行文本。
-
行尾处理:
fgets
会将换行符\n
保留在读取的字符串中,并将其视为行的结束标志。getline
会自动去除行尾的换行符,并将其从读取的字符串中移除。
-
错误处理:
fgets
读取失败时返回NULL
,并且可以通过检查feof
和ferror
函数来确定是文件结束还是发生了错误。getline
读取失败时返回-1
,并且会设置errno
来指示发生了什么错误