我正在制作用户必须输入输入的控制台应用程序。但是,当代码到达用户输入文件名的部分,然后使用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!");
}
}
为什么会这样做?
提前致谢!
答案 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上的问题更快地提供反馈。 ;-)