繁体   English   中英

C 程序段错误与 strtok

[英]C program segfaulting with strtok

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int n_of_words = 0;
    #define MAX_STR_SZ 256

    // asking for user input
    char string[50];
    printf("\nPlease input a string of text.\n\n");
    fgets(string, MAX_STR_SZ, stdin);

    char * words[n_of_words]; 

    // extracting the first word
    words[n_of_words] = strtok(string, " ");
    printf("\n%i  %s\n", n_of_words, words[n_of_words]);

    // looping through the string to extract all other words
    while( words[n_of_words] != NULL )
    {
        n_of_words ++;
        words[n_of_words] = strtok(NULL, " ");
        printf("\n%i  %s\n", n_of_words, words[n_of_words]);
    }
    sleep(10);
    return 0;
}

我对编程很陌生,但我试图编写一个 function 从用户输入的字符串中提取单词并将它们保存在一个数组中以供以后在程序中使用。 我添加了 2 行代码 printf 以查看它是否正常工作。 在 while 循环的第二次迭代后,我总是遇到分段错误错误。 此外,不知何故,当我在 CS50 ide (Cloud9) 上编译相同的代码时,这个问题并没有出现,但在任何其他情况下都会发生。

可以解决的几个问题以防止segmenatation fault

  1. strtok function 的源代码中没有string.h header
 #include <stdio.h> #include <unistd.h>
  1. 宏通常在源代码的顶部声明,而不是在任何 function 中

#define MAX_STR_SZ 256

  1. char string数组的长度为50 ,但fgets允许为256 ,并且可能导致缓冲区溢出。
 char string[50]; printf("\nPlease input a string of text.\n\n"); fgets(string, MAX_STR_SZ, stdin);
  1. 变量n_of_words的值为0 所以,声明

char * words[n_of_words];

不会创建所需长度的数组。

  1. 您的问题的根本原因在于
while( words[n_of_words] != NULL )
{
     n_of_words ++;
     words[n_of_words] = strtok(NULL, " ");
     printf("\n%i  %s\n", n_of_words, words[n_of_words]);
}

您正在访问一个从未声明过的 memory 位置,

 n_of_words ++; words[n_of_words] = strtok(NULL, " "); //words[1] or any index was never declared.

每个 C 程序都会免费获得一个命令行参数列表,通常声明为int main(int argc, char* argv[]); int main(int argc, char** argv);

这正是您试图用int n_of_wordschar* words[n_of_words];复制的内容。

但是你做错了。

您的代码中关于这 3 行的第一个注释:

#define MAX_STR_SZ 256

char string[50];

fgets(string, MAX_STR_SZ, stdin);

您将 256 设置为fgets()读取的限制,但字符串中只有 50 个字符。 很多时候它会在这种情况下工作,因为你是从键盘上阅读的,而我们中的许多人只会输入几个单词,但你有问题。 更改限制。

strtok()可能不是这里最好的选择。 使用scanf()的单个循环可以读取多行并在跳过换行符等的单词中中断所有行,您可能会发现它更容易编码。

无论如何,回到您的代码:由于您事先不知道字数,您可以估计一个限制为字符串一个一个分配 memory,甚至以块为单位。

您需要为在尝试写入words[]数组时将出现 SegFault 的字符串分配 memory。

我至少更改了您的代码,以便您可以看到一个示例,并且我修复了#define中的字符串数量,类似于您到目前为止所写的内容。

go 的一种简单方法是声明 --- 正如 C 在main()中所做的那样 --- words[] as char**并分配 memory 以记录它们。

但是您需要注意,您将只有指针。 他们仍然没有指向任何东西。

一旦你有一个要加载的字符串,你需要为其分配 memory ,加上 1 个字节用于终止'\0' ,然后复制字符串并将地址保存在words[]数组中的相应指针中。

查看代码。

#define MAX_STR_SZ 256
#define MAX_N_OF_STRINGS 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// https://stackoverflow.com/questions/63343800/
// c-program-segfaulting-with-strtok

int main(int argc, char** argv)
{
    int n_of_words = 0;
    int max_n_of_words = MAX_N_OF_STRINGS;
    char** words;

    // asking for user input
    char string[MAX_STR_SZ];
    printf("\nPlease input a string of text: ");
    fgets(string, MAX_STR_SZ, stdin);
    string[strlen(string) - 1] = 0; // drops the final '\n'
    printf("full string was '%s'\n", string);
    if (strlen(string) == 0) return -1; // no input

    // we have at least one byte

    // before anything build words[]
    words = (char**)malloc(max_n_of_words * sizeof(char*));
    // now words[] points to an array of pointers to char

    // extracting the first word
    char* a_word = strtok(string, " ");

    // looping through the string to extract all other words
    do
    {
        printf("\n%i  %s\n", 1+n_of_words, a_word);
        words[n_of_words] = malloc(1 + sizeof(a_word));
        strcpy(words[n_of_words], a_word);
        n_of_words++;
        if (n_of_words >= MAX_N_OF_STRINGS) break;
        a_word = strtok(NULL, " ");
    }   while (a_word != NULL);

    printf("\n%d words at the end of the loop:\n\n", n_of_words);
    for (int i = 0; i < n_of_words; i += 1)
    {
        printf("%i  %s\n", 1 + n_of_words, words[i]);
        free(words[i]); // deletes words[i]
    };  // for()
    free(words); // deletes the array
    return 0;
};

因此:

Please input a string of text: we have at least one byte
full string was 'we have at least one byte'

1  we

2  have

3  at

4  least

5  one

6  byte

6 words at the end of the loop:

1  we
2  have
3  at
4  least
5  one
6  byte

有一些问题可能导致段错误。 首先,我在编译您的代码时收到警告:

../main.c: In function 'main':
../main.c:17:25: warning: implicit declaration of function 'strtok' [-Wimplicit-function-declaration]
     words[n_of_words] = strtok(string, " ");
                         ^~~~~~
../main.c:17:23: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     words[n_of_words] = strtok(string, " ");
                       ^
../main.c:24:27: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
         words[n_of_words] = strtok(NULL, " ");

所有这一切都是因为您没有为strtok包含正确的 header ,即string.h 这可能会导致问题,因为默认返回类型假定为int ,它可能不足以容纳指针。

其次,您将不正确的大小传递给fgets() 大小应该是保存结果的缓冲区的大小。 如果缓冲区溢出,则会导致未定义的行为。

最后, words数组被声明为大小n_of_words ,此时该值为零。 这会产生一个大小为零的数组。 C 中的 Arrays 不会自动增长。

这是您修复了这些问题的代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    int n_of_words = 0;
    #define MAX_STR_SZ 256

    // asking for user input
    char string[MAX_STR_SZ];  // <--- Use macro to define buffer size
    printf("\nPlease input a string of text.\n\n");
    fgets(string, sizeof string, stdin);

    char * words[MAX_STR_SZ]; // <--- Should never be more words than characters in the buffer

    // extracting the first word
    words[n_of_words] = strtok(string, " ");
    printf("\n%i  %s\n", n_of_words, words[n_of_words]);

    // looping through the string to extract all other words
    while( words[n_of_words] != NULL )
    {
        n_of_words ++;
        words[n_of_words] = strtok(NULL, " ");
        printf("\n%i  %s\n", n_of_words, words[n_of_words]);
    }
    sleep(10);
    return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM