C 为什么代码输出的某一部分出现分段错误?
我正在制作这个控制台应用程序,用户必须在其中输入输入。但是,当代码到达用户输入文件名的部分,然后使用strcat将文件名添加到文件路径中时,它会输出分段错误。。 以下是完整的代码:C 为什么代码输出的某一部分出现分段错误?,c,segmentation-fault,C,Segmentation Fault,我正在制作这个控制台应用程序,用户必须在其中输入输入。但是,当代码到达用户输入文件名的部分,然后使用strcat将文件名添加到文件路径中时,它会输出分段错误。。 以下是完整的代码: int main(int argc, char *argv[]) { char theFilePath[512]; char theIP[20]; char theFile[100]; char password[1]; char username[10]; print
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
isscanf
,因为它不能防止缓冲区溢出(非常类似于get
,它在C99中被弃用并从C11中删除)
当缓冲区小得可怜时,这一点尤为重要,例如char password[1]
,这意味着使用空终止符时,所有密码的长度都必须为零个字符。现在,我不是世界著名的安全研究员,但我有理由相信,在该方案的某个地方存在安全漏洞:-)
当需要用户输入时,有更安全的选项,例如可以找到的功能,该功能可以防止缓冲区溢出,并提供有关试图输入过多数据的用户的信息(以及从中恢复)
第二种是依赖未初始化的内存。theFilePath
变量未初始化,因此其中可能包含任意数据,但您使用的strcat
要求它包含以num结尾的字符串。这是一个不好的假设,可以通过简单地将第一个strcat
转换为strcpy
来解决,或者,由于它总是被设置为相同的初始值,因此作为变量声明本身的一部分:
char theFilePath[512] = "./passfiles/";
另外,假设您使用的是现代C编译器,最好在需要变量的地方声明变量,而不是在函数顶部声明所有变量,这意味着此声明应该放在当前(错误的)
strcat
当前所在的位置。本地化您的声明和使用大大有助于可读性
您可能还需要考虑该声明的有用性:
sprintf(theFilePath,"%s",theFilePath);
即使它能工作,它实际上也是不可操作的,因为它所做的只是将一些数据复制到与当前数据完全相同的位置。在任何情况下,它都不能保证工作,因为标准在C117.21.6.6./2中非常明确地规定:
如果复制发生在重叠的对象之间,则行为未定义
字符数组
char theFilePath[512];
未由字符串初始化。因此,您可能无法使用连接字符串的标准C函数strcat。因此,程序具有未定义的行为
例如,您可以对其进行初始化,如下所示
char theFilePath[512] = "";
或者
char theFilePath[512] = { 0 };
char theFilePath[512];
theFilePath[0] = '\0';
或者
char theFilePath[512] = { 0 };
char theFilePath[512];
theFilePath[0] = '\0';
考虑到scanf
功能不安全。最好使用函数fgets
。
你也必须写作
printf("Username: ");
scanf("%s" , username);
^^^^^^^
而不是
printf("Username: ");
scanf("%s" , &username);
^^^^^^^^^
数组被隐式转换(除了极少数例外)为指向表达式中第一个元素的指针。虽然上面的答案都是有用且正确的,但我想提供一个稍微不同的视角
scanf如果安全使用,则是安全的。比如说,
char username[10];
scanf("%10s" , username);
这是安全的。不幸的是,无法将用户名的大小传递给scanf
对于文件名,Posix在limits.h
中定义为文件名的最大有效长度。如果你的系统没有提供这个定义,呃,我建议切换到一个有这个定义的系统。在紧急情况下,Windows上的MAX_路径可用
我还建议查看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的问题更快地提供反馈 标准误差strcat
要求第一个参数是以NUL结尾的字符串。你没有<代码>文件路径
未初始化,可能包含随机垃圾。单字符密码?@davenewt在零字符密码上,允许空终止符。错误可能来自函数“file_exist”int file_exist(char*filename){struct stat*buffer;return(stat(filename,&buffer)==0)}
因此我将第一个strcat
转换为strcpy
并声明theFilePath
变量,如下所示:char theFilePath[512]=”代码>但它仍然输出分段错误。。。