我有这个C作业,我在这个特定点上有点挣扎。我有一些 C 语言背景,但指针和动态内存管理仍然让我非常难以理解。
作业要求我们编写一个程序来模拟UNIX中"uniq"命令/过滤器的行为。
但是我遇到的问题是 C 库函数 getline 或 getdelim(我们需要根据实现规范使用这些函数)。
根据规范,用户输入可能包含任意数量的行,并且每行可能具有任意长度(在编译时未知)。
问题是,while循环的以下行 while (cap = getdelim(stream.linesArray, size, '', stdin))
当我像那样离开它时,以某种方式编译和"工作"。我的意思是,当我执行程序时,我每行输入任意长度的任意数量的行,程序不会崩溃 - 但它会一直循环,除非我停止程序执行(这些行是否正确存储在" char **linesArray; "是我不确定的不同故事。
我希望能够做类似的事情 while ((cap = getdelim(stream.linesArray, size, '', stdin)) && (cap != -1))
因此,当 getdelim 不读取某行的任何字符(除了 EOF 或 )时 - 也就是用户第一次输入空行时 - 程序将停止从 stdin 中获取更多行。 (然后打印 getdelim 存储在 stream.linesArray 中的行)。
问题是,当我执行程序时,如果我进行了上面提到的更改,程序会给我"分段错误",坦率地说,我不知道为什么以及如何解决这个问题(我已经尝试做一些事情很多次无济于事)。
供参考:
https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html
https://en.cppreference.com/w/c/experimental/dynamic/getline
http://man7.org/linux/man-pages/man3/getline.3.html
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_SIZE 20
typedef unsigned long long int ull_int;
typedef struct uniqStream
{
char **linesArray;
ull_int lineIndex;
} uniq;
int main()
{
uniq stream = { malloc(DEFAULT_SIZE * sizeof(char)), 0 };
ull_int cap, i = 0;
size_t *size = 0;
while ((cap = getdelim(stream.linesArray, size, 'n', stdin))) //&& (cap != -1))
{
stream.lineIndex = i;
//if (cap == -1) { break; }
//print("%s", stream.linesArray[i]);
++i;
if (i == sizeof(stream.linesArray))
{
stream.linesArray = realloc(stream.linesArray, (2 * sizeof(stream.linesArray)));
}
}
ull_int j;
for (j = 0; j < i; ++j)
{
printf("%sn", stream.linesArray[j]);
}
free(stream.linesArray);
return 0;
}
好的,所以意图很明确 - 使用getdelim
将行存储在数组中。getline
本身使用动态分配。手册对此非常清楚:
getline() 从 stream 中读取整行,存储 包含 *lineptr 文本的缓冲区。缓冲区是 以 null 结尾,并包含换行符(如果找到)。
getline()
"将缓冲区的地址存储到 *lineptr 中"。因此,lineptr
必须是指向char *
变量的有效指针(读取两次)。
*lineptr 和 *n 将更新 以分别反映缓冲区地址和分配的大小。
此外,n
需要是指向size_t
变量的有效(!)指针,以便函数可以更新它。
另请注意,lineptr
缓冲区:
即使 getline() 失败,用户程序也应该释放此缓冲区。
那我们该怎么办呢?我们需要一个指向字符串数组的指针数组。因为我不喜欢成为三星程序员,所以我使用结构。我稍微修改了一下您的代码,添加了一些检查。请原谅,我不喜欢 typedefs,所以我不使用它们。将uniq
重命名为struct lines_s
:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct line_s {
char *line;
size_t len;
};
struct lines_s {
struct line_s *lines;
size_t cnt;
};
int main() {
struct lines_s lines = { NULL, 0 };
// loop breaks on error of feof(stdin)
while (1) {
char *line = NULL;
size_t size = 0;
// we pass a pointer to a `char*` variable
// and a pointer to `size_t` variable
// `getdelim` will update the variables inside it
// the initial values are NULL and 0
ssize_t ret = getdelim(&line, &size, 'n', stdin);
if (ret < 0) {
// check for EOF
if (feof(stdin)) {
// EOF found - break
break;
}
fprintf(stderr, "getdelim error %zd!n", ret);
abort();
}
// new line was read - add it to out container "lines"
// always handle realloc separately
void *ptr = realloc(lines.lines, sizeof(*lines.lines) * (lines.cnt + 1));
if (ptr == NULL) {
// note that lines.lines is still a valid pointer here
fprintf(stderr, "Out of memoryn");
abort();
}
lines.lines = ptr;
lines.lines[lines.cnt].line = line;
lines.lines[lines.cnt].len = size;
lines.cnt += 1;
// break if the line is "stop"
if (strcmp("stopn", lines.lines[lines.cnt - 1].line) == 0) {
break;
}
}
// iterate over lines
for (size_t i = 0; i < lines.cnt; ++i) {
// note that the line has a newline in it
// so no additional is needed in this printf
printf("line %zu is %s", i, lines.lines[i].line);
}
// getdelim returns dynamically allocated strings
// we need to free them
for (size_t i = 0; i < lines.cnt; ++i) {
free(lines.lines[i].line);
}
free(lines.lines);
}
对于此类输入:
line1 line1
line2 line2
stop
将输出:
line 0 is line1 line1
line 1 is line2 line2
line 2 is stop
在 onlinegdb 上测试。
笔记:
if (i == sizeof(stream.linesArray))
sizeof
不会神奇地存储数组的大小。sizeof(stream.linesArray)
只是sizeof(char**)
只是一个指针的大小。它通常是 4 或 8 个字节,具体取决于 32 位还是 64 位架构。uniq stream = { malloc(DEFAULT_SIZE * sizeof(char)),
-stream.linesArray
是一个char**
变量。所以如果你想有一个指向char
的指针数组,你应该为指针分配内存malloc(DEFAULT_SIZE * sizeof(char*))
。typedef unsigned long long int ull_int;
如果类型表示数组大小或大小(变量),则size_t
类型。ssize_t
有时在 posix api 中用于返回大小和错误状态。使用这些变量,无需键入unsigned long long
。ull_int cap
cap = getdelim
- 帽子没有签名,它永远不会被cap != 1
.