为什么代码的某一部分会输出Segmentation Fault?

时间:2017-02-06 01:03:42

标签: c segmentation-fault

我正在制作用户必须输入输入的控制台应用程序。但是,当代码到达用户输入文件名的部分,然后使用strcat将其添加到文件路径中时,它会输出Segmentation Fault .. 这是完整的代码:

int main(int argc, char *argv[])
{   
    char theFilePath[512];
    char theIP[20];
    char theFile[100];
    char password[1];
    char username[10];

printf("Username: ");
scanf("%s" , &username);

printf("Enter password: ");
scanf("%s", &password);

printf("Enter IP: ");
scanf("%d" , &theIP);

printf("Please specify the file: ");
scanf("%s" , &theFile);

strcat(theFilePath, "./passfiles/");
strcat(theFilePath, theFile);
strcat(theFilePath,".pf");
sprintf(theFilePath,"%s",theFilePath);


if (!(file_exist (theFilePath)))
{
    printf("The file cannot be found in the path %s", theFilePath);
    exit(EXIT_FAILURE);
} else 
{
    printf("The file exists!");
}
}

为什么会这样做?

提前致谢!

3 个答案:

答案 0 :(得分:1)

你(至少)有几个问题。

首先,您通常不应该使用无界%s scanf,因为它不能防止缓冲区溢出(非常类似于gets,在C99中已弃用并从C11中删除。

当你的缓冲区很小,例如char password[1]时,这是非常重要的,这意味着,使用null终止符,所有密码都必须为零个字符长。现在我不是世界着名的安全研究员,但我有理由相信那个方案中有一个安全漏洞某处: - )

当需要用户输入时,有更安全的选项,例如可以找到的功能here,一个可以防止缓冲区溢出的功能,并提供有关尝试输入过多数据的用户的信息(和从中恢复)

第二个是依赖未初始化的记忆。 theFilePath变量未初始化,因此可能包含任意数据,但您使用strcat期望它包含以num结尾的字符串。这是一个糟糕的假设,可以通过简单地将第一个strcat变成strcpy来解决,或者因为它始终被设置为相同的初始值,所以这样做变量声明本身的一部分:

char theFilePath[512] = "./passfiles/";

而且,顺便说一下,假设你使用的是现代的C编译器,你最好在你需要的地方声明变量,而不是在函数顶部的所有变量,这意味着这个声明应该转到当前(错误)strcat当前所在的位置。将您的声明和使用本地化有助于提高可读性。

您可能还想考虑声明的用处:

sprintf(theFilePath,"%s",theFilePath);

即使它有效,它实际上也是一种无操作,因为它只是将一些数据复制到与当前存在的完全相同的位置。在任何情况下,它的保证可以正常工作,因为标准在C11 7.21.6.6./2中明确说明:

  

如果在重叠的对象之间进行复制,则行为未定义。

答案 1 :(得分:0)

字符数组

char theFilePath[512];

未被字符串初始化。因此,您可能不会使用连接字符串的标准C函数strcat。结果该程序具有未定义的行为。

您可以将其初始化为例如

char theFilePath[512] = "";

或喜欢

char theFilePath[512] = { 0 };

或喜欢

char theFilePath[512];
theFilePath[0] = '\0';

考虑到scanf功能不安全。最好使用函数fgets。 你还得写

printf("Username: ");
scanf("%s" , username);
             ^^^^^^^

而不是

printf("Username: ");
scanf("%s" , &username);
             ^^^^^^^^^

数组被隐式转换(极少数例外)指向表达式中第一个元素的指针。

答案 2 :(得分:0)

虽然上述答案都是有用和正确的,但我想提供一个略有不同的观点。

如果安全使用,

scanf 是安全的。例如,

char username[10];
scanf("%10s" , username);

是安全的。不幸的是,无法将sizeof username传递给scanf。

对于文件名,Posix将limits.h中的PATH_MAX定义为文件名的最大有效长度。如果您的系统没有提供该定义,呃,我建议切换到那个。在紧张的情况下,MAX_PATH在Windows上可用。

我还建议查看 getopt (3)的手册页。虽然学习如何使用scanf很有用,但最好只接受用户提供的信息的命令行选项,这些信息仅在启动时才需要。

最后,看看你能做些什么来提高你的警告水平。例如,我最近的clang版本标记了您的错误使用,

$ cc -std=c11     addr.c   -o addr
addr.c:9:16: warning: format specifies type 'char *' but the argument 
has type 'char (*)[10]' [-Wformat]
  scanf("%s" , &username);
         ~~    ^~~~~~~~~
1 warning generated.

此类警告甚至比SO上的问题更快地提供反馈。 ;-)