0

晚上好,

我在分配作业时遇到问题。

基本上,我们需要编写一个程序来计算给定stdin. 数据只能通过其进入程序stdin,无论是通过一个echo还是一个< file.txt。数据流永远不会超过 80 个字符(它们可以是数字,也可以不是数字)。

我在程序中使用的函数是read(),strotol()strtok(), “不相关”的代码流程如下:

  1. 用于malloc分配 80 个初始字节的内存。
  2. int通过read()读取的字符数(我相信,最后一个)存储在 中\0
  3. 重新分配内存realloc()以节省尽可能多的内存(我知道在这种情况下这很简单,但是哦……)。

现在是棘手的一点:

  1. 由于数据必须用空格分隔,因此要检查的最大项目数最多为:(n/2)+1,其中n是在上点 nº 2 处读取的字符数。
  2. 创建一个数组,long其最大大小为在点 nº 1 中获得的数字。
  3. 填充numbers[0]结果:strtol(strtok(line, delim), &end, 10)
  4. 添加1counter并进入一个while循环:

    while((numbers[counter] = strtol(strtok(NULL, delim), &end, 10)) != NULL) {
        if(!*end) {
            // Check whether it's 0, 1, negative, prime or extract its factors
        }
        else {
            fprintf(stderr, "\"%s\" is not a non-negative integer", end)
        }
        counter++;
    }
    

现在,这里有一些输入和它们的输出:

输入:echo 1 2 3 4 5 6 7 8 9 10 | ./factors

输出:

1
2    
3
    2    2
5
    2    3
7 
    2    2    2
    3    3
    2    5
Segmentation Fault (core dumped). 

Input./factors < integers.txt 其中 integers 包含一列整数。

输出:

所有整数都被分解得很好,最后打印出:

Segmentation Fault (core dumped). 

输入:echo abc 12 13 14 15 | ./factors

输出:

"abc" is not a non-negative integer
13
    2    7
    3    5
Segmentation Fault (core dumped). 

输入:echo -1 -2 -3 -4 -5 | ./factors

输出:

"-1" is not a non-negative integer
"-2" is not a non-negative integer
"-3" is not a non-negative integer
"-4" is not a non-negative integer
"-5" is not a non-negative integer

输入:echo abc abc abc | ./factors

输出:

"abc" is not a non-negative integer

(并且不继续检查)。

输入:echo 3 4 0 6 7 | ./factors

输出:

3
    2    2

(并且不继续检查)。

据我所知,当它遇到一个0以上的非或基本上在基于健康的数据流integer的末尾的实例时,它会失败。integer

知道我该如何解决这个问题,为什么它会如此明显地随机失败?

我应该让你知道我是 C 的新手...

非常感谢您。

==================================================== ==

EDIT1:根据请求,这里是生成的代码片段numbers[],并从中读取stdin

char *line;
char *end;
char *delim = " \n";
int charsread, counter; 

line = malloc(80);
charsread = read(0, line, 81);
if (charsread == 0) {
    return EX_OK;
}
realloc(line, charsread);
maxelem = (charsread / 2) + 1;
long numbers[maxelem];
numbers[0] = strtol(strtok(line, delim), &end, 10);
if (!*end) {
    zeroone(numbers[0]);
}
else {
    fprintf(stderr, "\"%s\" is not a non-negative integer\n", end);
}
counter = 1;
while [...]
4

3 回答 3

2

尝试使用gdb调试segfault,通过设置适当的环境变量使其在segfaulting时转储核心,或直接在gdb中运行。Segfault 意味着你正在读/写你不应该读/写的一部分内存。随机性意味着您可能正在破坏堆栈或其他东西。我认为“printf”是罪魁祸首,检查他们的论点。您也没有检查数字是否小于数组长度?它可能会超出它吗?

于 2012-06-07T18:04:18.180 回答
1

好的,让我们在现场查看一些内容,看看我们是否可以解决您的问题。


这在您的程序中不是必需的:

realloc(line, charsread);

尝试减少内存占用是正确的,但是如果分配太多,那又如何呢?它是 80 个字节。不要过于复杂,因为你的盘子里已经有足够的了。


这很奇怪:

maxelem = (charsread / 2) + 1;
long numbers[maxelem];

这很好,并且适用于您的情况,但是您可以通过计算数字组来更准确地确定元素的数量。


我建议尝试在 lopp 中进行整个提取


关于实际segmentation fault......我可能错了,因为我没有机会在这里在我的机器上重现它,但我认为发生错误是因为你没有line\0字符终止你的。在 C 语言中,我相信您已经通过您发布的代码片段的外观知道,行通常是我们所说的“NULL 终止”,因此按照惯例我们用值 0 来终止它们,这是 of 的数值NUL字符,也表示为(\0有时有点令人困惑,因为人们会在“NULL 终止”、“NUL”字符和“NULL 指针”之间混淆,它们是不同的东西)。

然后,这会使您的程序中断,因为最终您的程序会尝试读取行尾,strtok但它不知道在哪里停止。它不知道缓冲区的长度以及在此缓冲区中的停止位置。所以它一直在读取,到达一个不允许访问的内存地址,因此出现了一个segmentation fault.

所以你想简单地:

/*
** If you keep your realloc, you need to allocate for the number of read
** characters from stdin, and for an extra char to terminate.
*/
line = realloc(line, charsread + 1); 
/*
** Terminate the string.
*/
line[charsread] = '\0';

更新:啊,你实际上几乎在那里,你有逻辑,但可能只是错过了这一点......你甚至自己写了这个:

通过 read() 将读取的字符数存储在 int 中(我相信是最后一个 \0)。

这部分是正确的。如果您真的得到了一行 80 个字符,那么您的read调用将在最后返回带有 a 的行\0。但大多数情况下,您的读取缓冲区较少,因此您的读取缓冲区仅读取可见字符,您需要自己对字符串进行空终止。


我还会尝试重写您的处理循环,以便将第一次初始调用作为它的一部分 strtok。编写起来并不总是很方便,但通常看到一段代码几乎与循环之前或之后的循环内的内容相同,这让我认为有更好的逻辑方法。

于 2012-06-07T19:14:08.827 回答
0

非常感谢您富有洞察力的回复。

最后我只是重写了整件事,因为它看起来不像我预期的那么干净。由于手册中strtok指定了NULL如果无法提取令牌的返回值,这就是我最终得到的结果:

long number;
item = strtok(line, delim);
while (item != NULL) {
       number = strtol(item, &rest, 10);
       if (*rest == 0) {
           zeroOne(number);
       }
       else {
           fprintf(stderr, "\"%s\": not a non-negative int.\n", item);
       }
       item = strtok(NULL, delim);
}   

NULL似乎是一种更简洁的方法,并且确实考虑了 strtok 在尝试进入while我之前的循环之前返回的返回值这一事实。然后,今天早上我回到这里并阅读了您的回复:)

关于您对 a 的硬编码输入到该行的后续问题\0:这是否意味着我的最后一个strtok实际输出\0令牌并尝试进入循环,或者它是否NULL在我最后一次介绍后立即到达实体时返回 a特点?作为惯例,在使用时read()(或者可能是其他读取功能,例如fgets()我是否应该始终尝试将 a 硬编码\0到 read 行以允许其他功能检查EOL/ EOF

如果其他人在使用这些功能(strtokstrtol)时遇到问题,我建议您查看此站点上发布的这两个问题:

关于strtol的第二个论点:Strtol 第二个论点

关于strtok的输出:无法连接 strtok 的输出变量。strcat 和 strtok

于 2012-06-08T06:34:11.990 回答