使用strtok的C程序分段


#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.nn");
fgets(string, MAX_STR_SZ, stdin);
char * words[n_of_words]; 
// extracting the first word
words[n_of_words] = strtok(string, " ");
printf("n%i  %sn", 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  %sn", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}

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

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

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

#define MAX_STR_SZ 256

  1. char string阵列的长度为50,但fgets允许256,并可能导致缓冲区溢出
char string[50];
printf("nPlease input a string of text.nn");
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  %sn", n_of_words, words[n_of_words]);
}

您正在访问一个从未声明的内存位置,

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()的单个循环可以读取许多行,然后用跳过换行符之类的单词打断所有行,您可能会发现编写代码更容易。

无论如何,回到你的代码:由于你事先不知道单词的数量,你可以估计一个限制为字符串逐个甚至分块分配内存。但是

您需要为将具有SegFault的字符串分配内存当您尝试在words[]数组中写入时。

我更改了您的代码的最小值,以便您可以看到一个示例,并且我固定了#define中的字符串数量,与您迄今为止所写的内容类似。

一个简单的方法是像C在main()中所做的那样,将words[]声明为char**,并在知道至少有一个字符串要记录时立即为它们分配内存。

但是,你需要注意的是,你将只有指针。他们仍然一无所获。

一旦有字符串要加载,就需要为其分配内存,再加上终止''的1个字节,然后复制该字符串并将地址保存在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  %sn", 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:nn", n_of_words);
for (int i = 0; i < n_of_words; i += 1)
{
printf("%i  %sn", 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

有一些问题可能会导致seg故障。首先,我在编译你的代码时收到警告:

../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(即string.h(包含正确的标头。这可能会导致潜在的问题,因为默认返回类型被假定为int,它可能不够大,无法容纳指针。

其次,您向fgets()传递了一个不正确的大小。大小应该是用于保存结果的缓冲区的大小。如果缓冲区溢出,则会产生未定义的行为。

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

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

#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.nn");
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  %sn", 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  %sn", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}

最新更新