新手C问题:使用可变长度字符串的可变长度数组?



我可能有一个简单的C程序员在那里!

我试图创建一个简单的C函数,该函数将执行系统命令in并将过程输出写入字符串缓冲区out(应初始化为长度n的字符串数组)。输出需要按以下方式格式化:

写入标准输出的每一行都应该初始化为字符串。每个字符串的长度都是可变的。输出应该是由每个字符串组成的数组。没有办法知道有多少字符串会被写入,所以这个数组在技术上也是可变长度的(但出于我的目的,我只是在函数之外创建了一个固定长度的数组,并将其长度作为参数传递,而不是去创建一个我必须手动分配内存的数组)。

这是我现在的数据:

#define MAX_LINE_LENGTH 512                                    
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
const char terminator = '';
if ((file = popen(in, "r")) == NULL) {
return 1;
}

for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
if (pclose(file)) {
return 2;
}
return 0;
}

命名
#define N 128 
int main(void)
{
const char* buffer[N];
const char cmd[] = "<some system command resulting in multi-line output>";
const int code = exec(cmd, buffer, N);
exit(code);
}

我相信上面的代码导致的错误是一个segfault,但我没有足够的经验来弄清楚为什么或如何修复。

我几乎肯定这是我的逻辑:

for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}

我认为这是:

  1. 获取out的可变引用(即head指针)
  2. 保存当前标准输出行到buffer(通过fgets)
  3. 附加一个空终止符到buffer(因为我不认为fgets这样做?)
  4. 用步骤3中的值覆盖head指针处的数据
  5. 移动head指针strlen(buffer)字节(即bufferchar秒的数量)
  6. 继续直到fgets返回NULLhead指针已经移动到out数组的边界之外

我错在哪里?任何帮助感激,谢谢!

编辑# 1

根据Barmar的建议,我编辑了我的代码:
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
if ((file = popen(in, "r")) == NULL) return 1;
for (size_t i = 0; i < n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; i += 1) out[i] = buffer;
if (pclose(file)) return 2;
return 0;
}
#define N 128 
int main(void)
{
const char* buffer[N];
const char cmd[] = "<system command to run>";
const int code = exec(cmd, buffer, N);
for (int i = 0; i < N; i += 1) printf("%s", buffer[i]);
exit(code);
}

虽然我写的东西有很多冗余,现在已经修复了,但这仍然会在运行时导致分段错误。

专注于编辑过的代码,这个作业

out[i] = buffer;

有问题。

在这个表达式中,buffer被隐式地转换为指向其第一个元素的指针(&buffer[0],参见:衰变)。不分配额外内存,也不复制字符串。

每次迭代重写buffer。循环结束后,out的每个有效元素将指向相同的内存位置,该位置将包含最后一行读取的内容。

bufferexec函数的本地数组。它的生命周期在函数返回时结束,因此main中的数组包含悬空指针。使用这些值是未定义行为。

另外

for (int i = 0; i < N; i += 1)

总是循环到可存储的最大行数,当可能读取的行数少于这个数时。


刚性解决方案使用数组的数组来存储读取的行。这里是一个粗略的例子(有关使用多维数组作为函数参数的更多信息,请参阅:这个答案)。

#include <stdio.h>
#include <stdlib.h>
#define MAX_LINES 128
#define MAX_LINE_LENGTH 512
int exec(const char *cmd, char lines[MAX_LINES][MAX_LINE_LENGTH], size_t *lc)
{
FILE *stream = popen(cmd, "r");
*lc = 0;
if (!stream)
return 1;
while (*lc < MAX_LINES) {
if (!fgets(lines[*lc], MAX_LINE_LENGTH, stream))
break;
(*lc)++;
}
return pclose(stream) ? 2 : 0;
}
int main(void)
{
char lines[MAX_LINES][MAX_LINE_LENGTH];
size_t n;
int code = exec("ls -al", lines, &n);
for (size_t i = 0; i < n; i++)
printf("%s", lines[i]);
return code;
}
使用动态内存是另一种选择。下面是一个使用strdup(3)的基本示例,缺少健壮的错误处理。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **exec(const char *cmd, size_t *length)
{
FILE *stream = popen(cmd, "r");
if (!stream)
return NULL;
char **lines = NULL;
char buffer[4096];
*length = 0;
while (fgets(buffer, sizeof buffer, stream)) {
char **reline = realloc(lines, sizeof *lines * (*length + 1));
if (!reline)
break;
lines = reline;
if (!(lines[*length] = strdup(buffer)))
break;
(*length)++;
}
pclose(stream);
return lines;
}
int main(void)
{
size_t n = 0;
char **lines = exec("ls -al", &n);
for (size_t i = 0; i < n; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}

相关内容

  • 没有找到相关文章

最新更新