简体   繁体   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;
}

I'm very new to programming, but I was trying to write a function to extract words from a user inputted string and save them in an array for later use in the program.我对编程很陌生,但我试图编写一个 function 从用户输入的字符串中提取单词并将它们保存在一个数组中以供以后在程序中使用。 I added the 2 printf lines of code to see if it was working properly.我添加了 2 行代码 printf 以查看它是否正常工作。 I always get a segmentation fault error after the second iteration of the while loop.在 while 循环的第二次迭代后,我总是遇到分段错误错误。 Also, somehow this problem didn't present itself when I compiled the same code on the CS50 ide (Cloud9), but it happens in any other case.此外,不知何故,当我在 CS50 ide (Cloud9) 上编译相同的代码时,这个问题并没有出现,但在任何其他情况下都会发生。

Few issues which can be resolved to prevent segmenatation fault :可以解决的几个问题以防止segmenatation fault

  1. No string.h header in the source code for strtok function strtok function 的源代码中没有string.h header
 #include <stdio.h> #include <unistd.h>
  1. Macros are generally declared in the top of the source code and not inside any function宏通常在源代码的顶部声明,而不是在任何 function 中

#define MAX_STR_SZ 256

  1. The char string array is of length 50 but the fgets is allowing 256 and can lead to bufferoverflow. char string数组的长度为50 ,但fgets允许为256 ,并且可能导致缓冲区溢出。
 char string[50]; printf("\nPlease input a string of text.\n\n"); fgets(string, MAX_STR_SZ, stdin);
  1. The value of the variable n_of_words is 0 .变量n_of_words的值为0 So, the declaration所以,声明

char * words[n_of_words];

Will not create an array of the desired length.不会创建所需长度的数组。

  1. The root cause of your question lies here :您的问题的根本原因在于
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]);
}

You are accessing a memory location which was never declared,您正在访问一个从未声明过的 memory 位置,

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

Every C program gets for free a list of the command line parameters, in general declared as int main(int argc, char* argv[]);每个 C 程序都会免费获得一个命令行参数列表,通常声明为int main(int argc, char* argv[]); or int main(int argc, char** argv);int main(int argc, char** argv);

This is precisely what you are trying to replicate with int n_of_words and char* words[n_of_words];这正是您试图用int n_of_wordschar* words[n_of_words];复制的内容。

But you are doing it the wrong way.但是你做错了。

A first note on this 3 lines from your code:您的代码中关于这 3 行的第一个注释:

#define MAX_STR_SZ 256

char string[50];

fgets(string, MAX_STR_SZ, stdin);

You are setting 256 as the limit for fgets() to read, but you have only 50 chars in string.您将 256 设置为fgets()读取的限制,但字符串中只有 50 个字符。 Many times it will work in this case, since you are reading from the keyboard and many of us would not key more than a few words in, but you have a problem.很多时候它会在这种情况下工作,因为你是从键盘上阅读的,而我们中的许多人只会输入几个单词,但你有问题。 Change the limits.更改限制。

strtok() is probably not the best one to choose here. strtok()可能不是这里最好的选择。 A single loop using scanf() could read many lines and break all of then in words skipping over the newlines and such, and you may find it easier to code.使用scanf()的单个循环可以读取多行并在跳过换行符等的单词中中断所有行,您可能会发现它更容易编码。

Anyway, back to your code: since you do not know in advance the number of words, you can estimate a limit or allocate memory for the strings one by one, or even in blocks.无论如何,回到您的代码:由于您事先不知道字数,您可以估计一个限制为字符串一个一个分配 memory,甚至以块为单位。 But

you need to allocate memory for the strings you will have a SegFault at the moment you try to write in the words[] array.您需要为在尝试写入words[]数组时将出现 SegFault 的字符串分配 memory。

I changed a minimum of your code so you can see an example, and I fixed the number of strings in a #define similar of what you have written so far.我至少更改了您的代码,以便您可以看到一个示例,并且我修复了#define中的字符串数量,类似于您到目前为止所写的内容。

A simple way to go is declare --- as C does in main() --- words[] as char** and allocate memory for them as soon as you know you have at least one string to record. go 的一种简单方法是声明 --- 正如 C 在main()中所做的那样 --- words[] as char**并分配 memory 以记录它们。

But then you need to note that you will have just the pointers.但是您需要注意,您将只有指针。 They are still pointing to nothing.他们仍然没有指向任何东西。

As soon as you have a string to load you need to allocate memory for it, plus 1 byte for the terminating '\0' , and then copying the string and saving the address in the corresponding pointer in the words[] array.一旦你有一个要加载的字符串,你需要为其分配 memory ,加上 1 个字节用于终止'\0' ,然后复制字符串并将地址保存在words[]数组中的相应指针中。

See the code.查看代码。

#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;
};

As a result:因此:

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

There are a few problems that could lead to a seg fault.有一些问题可能导致段错误。 First, I get warnings compiling your code:首先,我在编译您的代码时收到警告:

../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, " ");

All of this is because you didn't include the proper header for strtok , namely string.h .所有这一切都是因为您没有为strtok包含正确的 header ,即string.h This could potentially cause problems because the default return type is assumed to be int , which may not be large enough to hold a pointer.这可能会导致问题,因为默认返回类型假定为int ,它可能不足以容纳指针。

Second, you are passing an incorrect size to fgets() .其次,您将不正确的大小传递给fgets() The size should be the size of the buffer for holding the result.大小应该是保存结果的缓冲区的大小。 If the buffer is overflowed, undefined behavior results.如果缓冲区溢出,则会导致未定义的行为。

Finally, the words array is declared with a size n_of_words , which is zero at that point.最后, words数组被声明为大小n_of_words ,此时该值为零。 This results in a zero size array.这会产生一个大小为零的数组。 Arrays in C do not automatically grow. C 中的 Arrays 不会自动增长。

Here is your code with these issues fixed:这是您修复了这些问题的代码:

#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