【问题标题】:Runs in Ubuntu, SegFault in Debian (C, strtok)在 Ubuntu 中运行,在 Debian 中运行 SegFault (C, strtok)
【发布时间】:2014-10-13 19:31:00
【问题描述】:

我的程序已经完全完成,并且完全可以在 Ubuntu 12.04 中正常运行。但是,当我尝试在 Debian VM 上编译它时,它给了我一个段错误。给出段错误的代码是:

printf("Going to location: /bin/%s\n", input2[0]);

Input2 是一个 char*,因为我需要使用 strtok 函数(我相信需要 char* 作为参数)。我在下面发布了我的代码,以及 Eclipse 调试的变量窗口以显示数组中的值。

这是帮助解决我的问题的变量窗口:

我最初认为错误出在 array2 本身,因为未使用的值以 0x0 而不是 \0 结尾。后来我发现 0x0 和 \0 只是表达空终止符的不同方式,所以这不是问题。我也试过了

printf("Going to location: /bin/%s\n", *input2[0]); 

使用取消引用,因为 input2 应该是指向字符串的指针,但这也不起作用。

我在这里很困惑;问题是由于 Ubuntu 和 Debian 之间的差异造成的,还是实际上是因为我使用阵列的方式造成的?我倾向于后者,但我很困惑哪里出了问题。

这是我的代码,直到出现段错误。剩下的就是 fork() 和 execvp(),效果很好:

int flag = 0;
int i = 0;
int status;
char *s; //for strchr, strtok
char input[15] = "";
char *input2[5];
//char input2[5];

//Prompt
printf("Please enter prompt:\n");

//Reads in input
fgets(input, 14, stdin);

//Remove \n
int len = strlen(input); //Warning: Implicit declaration of strlen and Incompatible implicit declaration of built-in function strlen
if (len > 0 && input[len-1] == '\n')
    input[len-1] = ' ';

//Check for & via strchr
s = strchr (input, '&'); //Warning: Implicit declaration of strchr and Incompatible implicit declaration of built-in function strchr
if (s != NULL) { //If there is a &
    printf("'&' detected. Program not waiting.\n");
    //printf ("'&' Found at %s\n", s);
    flag = 1;
}


//Now for strtok
input2[i] = strtok(input, " "); //Warning: Implicit declaration of function strtok and Assignment makes pointer from integer without cast

while(input2[i] != NULL)
{
    input2[++i] = strtok( NULL, " "); //Assignment makes pointer from integer without cast
}

if (flag == 1) {
    i = i - 1; //Removes & from total number of arguments
}

//Sets null terminator for unused slots. (Is this step necessary? Does the C compiler know when to stop?)
int c = i;
while (c < 5) {
    input2[c] = '\0';
    c++;
}

printf("Going to location: /bin/%s\n", input2[0]); //SegFault

编辑:我已将警告作为 cmets 添加到我的代码中。它们中的大多数是隐式声明警告,还有一些是“Assignment make pointer from integer without cast”。我现在会检查这些。

【问题讨论】:

  • 这个问题肯定与你的程序有关。这从根本上不是由于 Ubuntu 和 Debian 之间的差异。如果你的程序有段错误,那么这是一个非常安全的假设,它是你的错误。
  • fgets(input, 100, stdin) 放入仅声明为 char input[15] 的缓冲区中,从一开始就写满了灾难的秘诀。
  • 15 和 100 的差异是我的错!实际代码为 15;我之前正在改变一些事情。 “ls”和“pwd”将是唯一的输入。我一直在尝试其他各种方法,但仍未找到解决方案。
  • 你试过通过 valgrind 运行吗?能够使用 valgrind 是一项非常有价值的技能,将来可能会派上用场。
  • 关于您的文本:“我后来发现 0x0 和 \0 只是表达空终止符的不同方式,所以这不是问题。”。它们根本不一样。 0 是一个空指针。指向任何非法解除引用的指针。而指向值 0 ('\0') 的非零指针是一个空字符串。您忽略的那些警告是您的程序中需要修复的错误。

标签: c linux segmentation-fault debian strtok


【解决方案1】:

您使数组input 足够大以容纳 14 个字符和一个终止符,但您告诉 fgets() 它足够大以容纳 99。如果程序读取超过 14 个字符,则您有未定义的行为。

您将输入标记为不可预测数量的标记,但最多为 5 个保留空间。如果输入包含更多,则您的行为未定义。

您假设如果“&”字符出现在输入中,那么它位于末尾,作为单独的标记。

如果输入是空行,则strtok() 可能会为第一个标记返回 NULL,这可能会导致指定行出现段错误。

如果 input2[0] 的值如问题中所述,那么printf() 没有理由出现段错误。我得出结论,要么变量包含不同的东西,要么段错误发生在其他地方。

【讨论】:

  • 例如,它可能会在该行之后不久出现段错误。假设它做了涉及strlen(input2[1]) 的事情,因为正如@indiv 所观察到的,您已将其他input2[] 元素设置为NULL 指针。 (你的数据也显示了这一点。)
  • 感谢您的回复!我最初在 fgets 中有 15 个(在粘贴之前进行了一些更改),但现在将其更改为 14 个。输入总是“ls”或“pwd”,结尾有或没有&,所以我把数组做得很小。我也很困惑,因为 Eclipse 中的调试窗口显示它运行良好。不过,我在 Eclipse 中确实有一些警告。我现在就去看看。
【解决方案2】:

我发现了错误!

我按照你们中的一些人的建议查看了我的警告,在查看 Stack Overflow 上的隐式声明警告帖子时,我发现我错过了:

#include <string.h>

标题。这解决了一切!我现在没有错误。

我还是有点困惑。 string.h 给了我什么没有让我的程序更早地工作?我注意到 string.h 包含 size_t,它是 strlen 和 strchr 的返回类型。是这样吗?它还包含一个 NULL“宏”,其中包含一个空指针常量的值。这有什么特别之处,使它与之前的 NULL 区别开来?

最后,在什么情况下程序需要包含 string.h?我们需要包含的唯一时间是我们想要使用与字符串相关的函数,例如 strchr、strcat、strcpy 等吗?一般情况下,我们只想用一个字符串来存储一个词组,就不需要包含string.h,对吗?

再次感谢大家的帮助!

【讨论】:

  • 这肯定没有解决所有问题,但我可以相信你停止遇到段错误(如果包含标题,这确实不是由printf()调用引起的)停止)。我猜想在您的程序工作的机器上,指针的大小与 int 相同,但在程序发生段错误的机器上,它们的大小不同。如果前者运行 32 位内核而后者运行 64 位内核,则很容易出现这种情况。如果您不提供函数原型(通过标头),那么编译器可能会错误地猜测参数类型。
  • 永远不要假设忽略编译器警告是安全的。为了您的利益,编译器会发出它们。偶尔他们真的没问题,但你应该确定你知道为什么在你解雇他们之前他们没问题。
  • 更直接地回答您的问题:(1)标题提供的最重要的东西是函数原型; (2) NULL 是宏,不是内置语言,除非出现严重错误,否则 string.h 中的定义等同于系统头文件中的任何其他定义; (3) 您需要在使用字符串函数的源文件中包含 string.h。功能文档(除其他外)应该告诉您。特别是,man 3 strcpy 等命令应显示要包含的头文件。
  • string.h“修复”(如果你可以这样称呼它)是因为另外两个警告也被忽略了,一个表示“函数strtok隐式返回@987654326 @" 然后"将int 分配给char* 类型的左值。我怀疑您的Debian VM 平台是64 位的,带有32 位本机int,因此未能包含string.h 意味着没有strtok 的原型,因此它被视为int strtok()。结果是每个 64 位指针的一半被丢弃,并带有假定的int 结果,除非您尝试取消引用一个,否则您不会知道。
猜你喜欢
  • 2012-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多