这是我第一次遇到C中的Segmentation fault 11,我似乎无法绕过实际出错的地方。
我想要做的是将一些int值写入一个struct加上来自子进程的命令行(char *)的文件名,然后将该struct写入管道以从中读取父进程。当它只有整数并且我取出使用字符串的代码时工作正常,但是一旦我添加了字符串并尝试在父进程中打印出文件名,我就会得到分段错误11程序运行。
我已经查看过来自各地的各种帖子,但是注意到这个问题的常见问题是有人试图将字符串分配给char数组并进行打印,但我确保在这里只使用char * 。这是锁定的代码
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
以下是我运行程序时所读取的内容:
$ ./a testfile
Parent process... should be waiting on child...
In child process! pid = 21993
it worked? testfile
Done with child process!
words = 1
lines = 2
bytes = 3
Segmentation fault: 11
此处还有完整的代码 编辑:我使用sizeof for string换掉了代码并使用了strlen
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
我非常感谢您对此有任何见解!
答案 0 :(得分:4)
你的主要问题是你对C字符串没有正确的理解。你做不到sizeof(char_pointer)
。这只会给你指针大小(32位系统中的4)而不是它指向的字符串的大小。使用strlen
获取字符串的长度。
第二个相关问题是您正在编写指针地址v.file
,而不是通过管道写入完整的字符串内容。这是不正确的,因为每个进程都有一个单独的地址空间,因此一个进程中的指针在另一个进程中无效。
有几种方法可以解决您的问题。我会给你最简单的(但不是最好的)。
首先在结构中声明file
作为char数组而不是char指针。这基本上为您提供了固定大小的缓冲区。
#define MAX_FILENAME_LEN 64
typedef struct total {
int words, lines, bytes;
char file[MAX_FILENAME_LEN];
} Vals;
然后删除malloc调用。您不再需要它,因为file
已经是您可以复制到的缓冲区。
最后,确保在字符串复制期间不要溢出缓冲区:
if (strlen(fname) >= MAX_FILENAME_LEN) {
error_exit("File name too long");
}
strcpy(v.file, fname);
您也不需要+1
中的write
,因为sizeof
会为您提供完整的缓冲区大小。
我将把它作为练习留给你使用动态内存作为结构中的文件名。它并不难,但需要你稍微改变你的读写逻辑,因为你需要单独读/写文件名(因为在这种情况下写整个结构只会写指针而不是内容)
答案 1 :(得分:1)
这里有一些问题。首先,您不是free()
使用malloc()
分配的空间。
其次,您应该在计算中使用strlen()
代替sizeof()
。这在代码中出现两次。
第三,声明char fname = "Not set";
不安全,因为它实际上是const char*
只读内存(文本段),后来指向通过malloc()
分配的内容。不要这样做。
更正代码清单
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define MAX_BUF_LEN (1024)
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char file[MAX_BUF_LEN];
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char fname[MAX_BUF_LEN] = "Not set";
if(argc > 1) {
//fname = malloc(sizeof(argv[1]) + 1);
//fname = argv[1];
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1) {
error_exit("pipe creation");
}
if((pid = fork()) == -1) {
error_exit("the fork forked up!");
} else if(pid == 0) {
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
//v.file = malloc(strlen(fname) + 1);
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
printf("Done with child process!\n");
close(pd[1]);
return 0; //return from child
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
if((read(pd[0], &pv, 2048)) == -1) {
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
示例运行
Parent process... should be waiting on child...
In child process! pid = 7410
it worked? HelloWorld
Done with child process!
words = 1
lines = 2
bytes = 3
file = HelloWorld
答案 2 :(得分:0)
此代码有几个问题,由于某些原因未提及。因此,这是我的看法。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
嗯,gcc -Wall -Wextra告诉我:
警告:隐式声明函数'wait'
你是如何编译的?你看到这个错误而忽略了吗?如果是这样,一周没有糖果。
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
奇怪的命名。 &#39;总&#39 ;? &#39;瓦尔斯&#39;?
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
相当无用的评论。
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
如果&gt;测试argc == 2并抛出侮辱2。
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
不正确的。 strlen返回长度而没有终止空字符。考虑使用strdup(非标准)。缺少NULL检查。
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
您通常会提早关闭。
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
此代码不起作用,但您可能想要使用&#39; char文件[BIGNUM];&#39;在其他评论中提到,所以让我们偷一个应该有用的样本:
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
不正确的。让我们假设这会增加结构的大小 - 然后&#39; + 1&#39;在这里找到导致结构后读取1个字节。但是由于填充,不能保证所有struct元素的大小总和整个结构的大小。如果使用&#39; char文件[BIGNUM];&#39;只是sizeof(v)。如果使用char *文件,你必须确保文件始终是最后的,为简单起见,只需将offsetof用于文件指针。
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
不正确的。应该使用_Exit(2)代替。
}
else
{
printf("Parent process... should be waiting on child...\n");
}
使用else子句,只打印一些内容并在下面执行执行是什么?
//wait for child
while((pid = wait(NULL)) > 0);
不正确的。等待可以由于信号而返回。
close(pd[1]);
等待之前应该关闭。
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
pv没有2048个字节,所以这可能只是偶然发生。
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
考虑使用perror或err-family函数(不可移植)。
最后,我建议找一些不太糟糕的风格(来自linux或KNF)。