Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么代码输出的某一部分出现分段错误?_C_Segmentation Fault - Fatal编程技术网

C 为什么代码输出的某一部分出现分段错误?

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

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

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
is
scanf
,因为它不能防止缓冲区溢出(非常类似于
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]=”但它仍然输出分段错误。。。