我正在制作这个控制台应用程序,用户必须在其中输入输入。然而,当代码到达用户输入文件名的部分,然后使用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!");
}
}
有什么想法吗?
提前感谢!
您(至少)有几个问题。
第一个事实是,通常不应该使用无边界的%s
是scanf
,因为它不能防止缓冲区溢出(与gets
非常相似,后者在C99中被弃用,并从C11中删除)。
当缓冲区小得可怜时,这一点就显得尤为重要,比如char password[1]
,这意味着使用null终止符,所有密码的长度都必须为零个字符。现在我不是世界著名的安全研究员,但我有理由相信这个方案中有一个安全漏洞:-)
当需要用户输入时,还有更安全的选项,比如可以在这里找到的功能,它可以防止缓冲区溢出,并提供有关试图输入过多数据的用户的信息(并从中恢复)。
第二种是依赖未初始化的内存。theFilePath
变量未初始化,因此其中可能包含任意数据,但使用strcat
时需要包含以num结尾的字符串。这是一个不好的假设,可以通过简单地将第一个strcat
设置为strcpy
来修复,或者,由于它总是被设置为相同的初始值,因此将其作为变量声明本身的一部分:
char theFilePath[512] = "./passfiles/";
顺便说一句,假设你使用的是现代C编译器,你最好在需要的地方声明变量,而不是全部在函数的顶部,这意味着这个声明应该放在当前(错误的)strcat
所在的位置。本地化声明和使用大大有助于可读性。
您可能还需要考虑以下语句的有用性:
sprintf(theFilePath,"%s",theFilePath);
即使有效,也实际上是一个无操作,因为它所做的只是将一些数据复制到与当前存在的位置完全相同的位置。在任何情况下,都不能保证工作,因为标准在C11 7.21.6.6./2
:中有明确规定
如果复制发生在重叠的对象之间,则行为未定义。
字符数组
char theFilePath[512];
未由字符串初始化。因此,您可能不使用连接字符串的标准C函数strcat
。因此,程序具有未定义的行为。
例如,您可以像一样初始化它
char theFilePath[512] = "";
或类似
char theFilePath[512] = { 0 };
或类似
char theFilePath[512];
theFilePath[0] = ' ';
考虑到scanf
功能不安全。最好使用函数fgets
。你还必须写
printf("Username: ");
scanf("%s" , username);
^^^^^^^
而不是
printf("Username: ");
scanf("%s" , &username);
^^^^^^^^^
数组被隐式转换为指向表达式中第一个元素的指针(极少数例外)。
虽然上面的答案都是有用和正确的,但我想提供一个稍微不同的视角。
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上的问题。;-)