使用 strtok 时出现段错误

Seg fault when using strtok

我正在尝试从如下所示的输入中读取每个数字:

179    2358 5197    867 
5541    172 4294    1397
2637    136 3222    591 

... 逐行获取每一行的 min/max,然后使用 strtok 获取行中的每个数字,但我在第二个 while 循环中遇到段错误。

代码:

#include "header.h"
#include <string.h>

int main(void) {
  int8_t* line = malloc(sizeof(int8_t) * 75);
  int8_t* temp;

  int32_t minim, maxim;
  int32_t current;

  stdin = fopen("stdin", "r");
  while (fgets(line, 75 * sizeof(int8_t), stdin)) {
    printf("%s", line);
    temp = strtok(line," \n\t");
    maxim = minim = atoi(temp);

    while (temp != NULL) {
      current = atoi(temp);
      temp = strtok(NULL, " \n\t");

      if (current < minim)
        minim = current;
      if (current > maxim)
        maxim = current;

    } 

    printf("\nMin and max: %d %d\n", minim, maxim);
  }

  printf("\n");
  return 0;
}

header:

#ifndef HEADER_H
# define HEADER_H

# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>

  typedef                   char  int8_t;
  typedef   signed     short int int16_t;
  typedef   signed           int int32_t;
  typedef   signed long long int int64_t;

  typedef unsigned          char  uint8_t;
  typedef unsigned     short int uint16_t;
  typedef unsigned           int uint32_t;
  typedef unsigned long long int uint64_t;

#endif

我只是不明白它可能出了什么问题。谢谢

stdin = fopen("stdin", "r");

这不是你想的那样。这会尝试在当前目录中打开一个名为“stdin”的文件。它不会 打开标准输入流。

你很可能没有这个名字的文件,所以这个函数可能会失败并且 returns NULL。因为你没有检查 return 值,所以你将 NULL 指针传递给 fgets。这会调用未定义的行为,在本例中表现为崩溃。

标准输入流在您的程序启动时已经打开。所以删除 fopen 调用,它应该可以工作。

好的,所以...

  1. 你的 <header.h> 破坏了我的编译器,因为它重新定义了标识符 int8_t 等。由标准保留。 (编译器的抱怨不太正确,因为相关的 header 没有被包括在内,但你就是这样。)如果你定义你自己的类型,使用你自己的标识符。或者为 "real" intX_t 类型包含 <stdint.h>(是的,我知道某个 "big" 编译器没有那个 header)。

  2. 最简单的当然是对字符串使用char *而不是int8_t *。 (char 的 signed-ness 是 implementation-defined,无论如何,一个好的编译器会大声抱怨使用 char * 的许多隐式转换 not涉及。)

  3. 您使用 exact-width 类型 int32_t 作为 minimmaxim,但忽略了使用正确的 printf() 宽度与之对应的说明符(PRId32,在 <inttypes.h> 中指定)。由于我看不到对 exact-width 类型的任何特定需求,我将它们替换为普通的 int (仅 %d 就足够了)。

  4. 完成后,删除提及 sizeof( char ),因为根据定义 1.

  5. 检查 return 函数值。 fopen() 可能会失败。确保它没有。

  6. 不要 "reuse" stdin 文件描述符。到处都是混乱。您的 stdin 是来自终端还是文件或管道取决于您调用可执行文件的方式。如果打开文件,请使用 FILE * handle_name,而不是 stdin。此外,stdin 一个打开的文件描述符(即您程序的标准输入),因此您必须使用 freopen(),而不是 fopen(),让它从不同的来源正确接收。最简单的事情就是实际使用 stdin as-is(这也给了你最大的灵活性 使用 你的程序)。

  7. 释放你获得的资源。 free()记忆。 fclose() 个文件。许多操作系统保护您免受这种疏忽并在您之后进行清理。有些人没有。

一旦你解决了那些你在评论中打喷嚏的问题...

#include <stdlib.h>                           // <-- added
#include <stdio.h>                            // <-- added
#include <string.h>

int main() {
  char* line = malloc(75);                    // <-- turned to char *
  char* temp;                                 // <-- turned to char *

  int minim, maxim;                           // <-- turned to int
  int current;                                // <-- turned to int

  while (fgets(line, 75, stdin)) {            // <-- using stdin directly
    printf("%s", line);
    temp = strtok(line," \n\t");
    maxim = minim = atoi(temp);

    while (temp != NULL) {
      current = atoi(temp);
      temp = strtok(NULL, " \n\t");

      if (current < minim)
        minim = current;
      if (current > maxim)
        maxim = current;

    }

    printf("\nMin and max: %d %d\n", minim, maxim);
  }

  printf("\n");
  free( line );                                // <-- releasing memory
  return 0;
}

...您的程序有效:

./testme.exe < file.dat                      # feeding file.dat to stdin
179    2358 5197    867

Min and max: 179 5197
5541    172 4294    1397

Min and max: 172 5541
2637    136 3222    591

Min and max: 136 3222

这里的教训:干净地编写代码。启用最严格的编译器警告,并留意它们。

至于像您一样用 fopen() 的结果覆盖 stdin,标准规定(强调我的):

229) [...] [stderr, stdin, or stdout] need not be modifiable lvalues to which the value returned by the fopen function may be assigned.