C 错误记录(二): 指针伪赋值

25 Sep 2014

相关日记: C 错误记录(一): 记一次指针类型转换的 bug

我修改以前一段使用 select(2) 的服务端代码时, 遇到一个 segment fault. 定位到问题后, 发现是指针赋值原因.

原来的代码:

struct xx_fd_set {
	fd_set rfds, wfds;
	fd_set _rfds, _wfds;
};

static int xx_fd_set_init(struct xx_fd_set **fd_set)
{
	*fd_set = malloc(sizeof(struct xx_fd_set));
	if (*fd_set == NULL)
		return -1;

	FD_ZERO(&(*fd_set)->rfds);
	FD_ZERO(&(*fd_set)->wfds);
	return 0;
}

加入 epoll(2) 后, 将以上函数分离到单独的文件, 不再将传入参数限定为 struct xx_fd_set 类型, 修改接口为:

struct xx_server_ctx {
	int sock_fd;
	struct xx_fd_set *xx_allfd_set;
	// ...
};

static int xx_fd_set_init(struct xx_server_ctx *server)
{
	struct xx_fd_set *fd_set = server->xx_allfd_set;
	fd_set = malloc(sizeof(struct xx_fd_set));
	if (fd_set == NULL)
		return -1;

	FD_ZERO(&(fd_set)->rfds);
	FD_ZERO(&(fd_set)->wfds);
	return 0;
}

运行时发生 segment fault, 定位到以下代码:

	struct xx_fd_set *fd_set = server->xx_allfd_set;
	fd_set = malloc(sizeof(struct xx_fd_set));

可以看到, malloc(3) 申请的地址并没有被 server->xx_allfd_set 所指向到, 而是存放在一个栈变量 fd_set 上. 等 xx_fd_set_init() 返回后, 这片申请到的内存没有被任何变量所指向, server->xx_allfd_set 实际上没有被赋值, 当访问server->xx_allfd_set 所指向的内存时, 就发生了 segment fault.

把上面的代码修改如下:

struct xx_server_ctx {
	int sock_fd;
	struct xx_fd_set *xx_allfd_set;
	// ...
};

static int xx_fd_set_init(struct xx_server_ctx *server)
{
	struct xx_fd_set **fd_set = &server->xx_allfd_set;
	*fd_set = malloc(sizeof(struct xx_fd_set));
	if (*fd_set == NULL)
		return -1;

	FD_ZERO(&(*fd_set)->rfds);
	FD_ZERO(&(*fd_set)->wfds);
	return 0;
}

这样调用 xx_fd_set_init() 后可以正常运行. 但是, 二级指针 fd_set 看起来有些晦涩. 想了想是我脑子瓦特勒, 这样写岂不简单明了:


static int xx_fd_set_init(struct xx_server_ctx *server)
{
	struct xx_fd_set *fd_set;
	fd_set = malloc(sizeof(struct xx_fd_set));
	if (fd_set == NULL)
		return -1;
	server->xx_allfd_set = fd_set;
	FD_ZERO(&(fd_set)->rfds);
	FD_ZERO(&(fd_set)->wfds);
	return 0;
}