我正在运行以下C代码,其中尝试读入缓冲区 在调用者的堆栈上分配,但在错误14(错误地址)时失败。
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
void wrapper(int fd, char **buf)
{
int res = read(fd, *buf, 10);
printf("res: %d, errno: %d\n", res, errno);
printf("Buf: %s\n", *buf);
}
int main()
{
char buffer[10];
memset(buffer, 0, 10);
int fd = open("main.c", O_RDONLY);
wrapper(fd, (char **)&buffer);
return 0;
}
输出
res: -1, errno: 14
Buf: (null)
我一直在寻找解释失败的原因,而将其改为
void wrapper(int fd, char *buf)
...
wrapper(fd, (char *)buffer);
有效,但到目前为止没有结果。
答案 0 :(得分:0)
为什么失败
数组不是指针。 buffer
不是char*
。因此,&buffer
不是char**
,与char**
不兼容,不应转发为char**
。如果将其强制转换为char**
然后取消引用,则行为未定义。
答案 1 :(得分:0)
在分析了你的意图后,当然可以通过read(2)
系统调用创建包含读取字符串的“包装器”,并使用远离wrapper()
函数的缓冲区。您希望传递将从文件表中读取的字符数量,该文件表是由open(2)
系统调用返回的表(文件描述符)的索引。但正如上午说,数组不是指针,你的解决方案无法正常工作。
让我解释一下我对代码的简单修复:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define AMOUNT 20
#define assert_msg(x) for ( ; !(x) ; assert(x) )
void
wrapper(int fd, char **buf, size_t size)
{
ssize_t res;
char *out;
out = calloc(size + 1, sizeof(char));
assert(out != NULL);
res = read(fd, out, size);
assert_msg(res != -1) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
out[size] = '\0';
fprintf(stdout, "Inside function: %s\n", out);
fprintf(stdout, "res: %d, size: %d, errno: (%d: %s)\n", res, size,
errno, strerror(errno));
*buf = out;
}
int
main(int argc, char **argv)
{
int fd;
char *buf;
buf = NULL;
assert(argc == 2);
errno = 0;
fd = open(argv[1], O_RDONLY);
assert_msg(fd != -1) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
wrapper(fd, &buf, AMOUNT);
fprintf(stdout, "Outside function: %s\n", buf);
free(buf);
return (EXIT_SUCCESS);
}
wrapper()
实现中,我为out
缓冲区分配内存,其大小我传递的值为size
变量。我知道与AMOUNT
值相同的值定义为宏,但在任何其他解决方案中都很容易更改。read(2)
系统调用从open(2)
系统调用main()
函数返回的文件描述符中读取给定数量的字符,并将其传递给wrapper()
。out
缓冲区的开头,我希望*buf
指示该地址。它是size + 1
char
元素的缓冲区,在堆上分配,而不是在本地堆栈上分配。因此程序在执行期间不能“重用”该地址。声明为int a;
,struct type name;
或char tab[10];
的变量的每个地址在函数结束后自动“释放”,并且您无权访问它。需要说明的是,您可以访问(例如,将地址中的打印数据保存到指示器中)但您无法确定是否会丢失保存在那里的数据。手动分配的空间仍然存在于堆上,直到调用free(3)
函数。所以,如果我们做的事情如下:
void
wrapper(int fd, char **buf, const size_t size)
{
ssize_t res;
char out[size];
(...)
*buf = out;
}
在程序执行过程中,您可能会丢失保存在本地堆栈中的数据。
此外,在我的解决方案中,我还定义了自己的宏assert_msg(x)
,它能够运行assert(3)
函数并显示错误的文本消息。但它只是一个功能,但是由于我们能够看到对应于errno
数字的字符串。
当然,我的程序需要更好的处理错误,但它必须只提出这个想法。
此外,您还应在使用open(2)
系统调用作为第三个参数时指定文件权限。它看起来类似于第二个参数,因为它是一个按位'或'分隔的值列表。示例标记:S_IRUSR
,S_IRGRP
,S_IWOTH
等
在该参数中,您还可以编写描述权限的正确值,例如0755
。