无法从常驻 .c 文件访问不透明指针
Opaque pointer not accessable from resident .c file
访问不透明结构内部的结构时,我遇到了奇怪的分段错误。我最近刚刚了解了不透明指针,但我的猜测是,我在内部结构的分配方面做错了。但是,在第一次调用该结构时,它似乎可以工作,并且在同一方法中无法访问它。
所以我在 connection.h
:
中定义了我的不透明结构指针
#ifndef _Connection_h
#deifne _Connection_h
// Connection opaque pointer
typedef struct connection;
// API
_Bool connection_connect(struct connection *self);
#endif // _Connection_h
然后在 connection.c
内部分配类型为 struct sockaddr_un
的内部 serv
结构指针。
#include "../connection.h"
struct connection {
_Bool (*connect)();
char *(*get_name)();
char name[MAX_NAMELEN];
char hostname[MAX_NAMELEN];
uint serv_id;
struct sockaddr_un *serv; // Server socket structure
};
// ** Public Interface **
_Bool connection_connect(struct connection *self) {
printf("Generic connection (opaque pointer)..\n");
strcpy(self->name, SERVERNAME);
// Socket path
char path[108];
strcpy(path, SERVERNAME);
strcat(path, "_socket");
printf("self->name = %s\n", self->name);
// Allocate the serv socket structure
self->serv = malloc(sizeof(*self->serv));
strcpy(self->serv->sun_path, path);
self->serv->sun_family = AF_UNIX;
printf("self->serv->sun_path = %s\n", self->serv->sun_path);
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
// Locate the host
char hostname[MAX_NAMELEN];
gethostname(hostname, MAX_NAMELEN);
strcpy(self->hostname, hostname);
if ((self->serv_id = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
handle_error("Socket");
return 1;
}
然后 connection
接口方法的分配由客户端处理。对于最小的可重现示例,client
类型实现可以像这样处理接口方法分配:
#include "../connection.h"
typedef struct connection {
void (*connect)();
} *client;
void client_connect(client self) {
connection_connect(self);
printf("Client connection..\n");
}
client make_client(client self) {
client tmp = malloc(sizeof(client));
tmp->connect = client_connect;
return tmp;
}
// Main procedure
int main(int argc, char **argv) {
client c = make_client(c);
c->connect(c);
return 0;
}
执行后,分配的结构似乎首先被正确分配,并且它的实例在最后一个之前都可以访问 printf
:
Generic connection (opaque pointer)..
self->name = freebay
self->serv->sun_path = freebay_socket
[1] 210938 segmentation fault (core dumped)
(gdb) p self->serv
= (struct sockaddr_un *) 0x66203d2068746170
(gdb) x self->serv
0x66203d2068746170: Cannot access memory at address 0x66203d2068746170
我的问题是,为什么 self->serv
在同一方法中使用后无法访问?
您没有分配足够的内存:
self->serv = malloc(sizeof(struct sockaddr_un *));
您为 指针 分配了足够的 space 到 struct sockaddr_un
,而不是其中的一个实例。指针大小小于结构大小,因此当您尝试写入结构时,您写入的结构超出了分配内存的末尾,触发 undefined behavior.
分配 space 的正确方法是:
self->serv = malloc(sizeof(struct sockaddr_un));
甚至更好:
self->serv = malloc(sizeof(*self->serv));
因为后者不关心类型是什么
此外,这是不正确的:
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
因为 %hn
期待 short int *
,而不是 short int
。将其更改为 %hd
.
根据您提供的完整示例,我们现在可以看到您的每个 .c 文件中都有两个同名的不兼容结构。
您的主文件将 struct connection
定义为:
typedef struct connection {
void (*connect)();
} *client;
而 connection.c 将其定义为:
struct connection {
_Bool (*connect)();
char *(*get_name)();
char name[MAX_NAMELEN];
char hostname[MAX_NAMELEN];
uint serv_id;
struct sockaddr_un *serv; // Server socket structure
};
您的主文件在 make_client
中创建前者的一个实例:
client tmp = malloc(sizeof(client));
这本身是无效的,因为您再次为指针而不是结构实例分配了 space。它需要是:
client tmp = malloc(sizeof(*tmp));
但即使你修复了它,你也会将这个指针传递给 connection_connect
,它需要一个指向后一个结构的指针,而不是前一个。
所有将创建 struct connection
实例并修改其成员的函数都应驻留在真正定义所在的 connection.c 中。这就是不透明结构应该如何工作。
在将数据复制到其中之前,您不分配 self->serv->sun_path
。之后肯定出问题了。
除非 sun_path
是 char
的数组。
编译你的代码我发现了这个:
connection.c:37:43: warning: format specifies type 'short *' but the argument has type 'sa_family_t' (aka 'unsigned char') [-Wformat]
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
~~~ ^~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
如果你写:
printf("self->serv->sun_family = %cn\n", self->serv->sun_family);
程序没有错误
访问不透明结构内部的结构时,我遇到了奇怪的分段错误。我最近刚刚了解了不透明指针,但我的猜测是,我在内部结构的分配方面做错了。但是,在第一次调用该结构时,它似乎可以工作,并且在同一方法中无法访问它。
所以我在 connection.h
:
#ifndef _Connection_h
#deifne _Connection_h
// Connection opaque pointer
typedef struct connection;
// API
_Bool connection_connect(struct connection *self);
#endif // _Connection_h
然后在 connection.c
内部分配类型为 struct sockaddr_un
的内部 serv
结构指针。
#include "../connection.h"
struct connection {
_Bool (*connect)();
char *(*get_name)();
char name[MAX_NAMELEN];
char hostname[MAX_NAMELEN];
uint serv_id;
struct sockaddr_un *serv; // Server socket structure
};
// ** Public Interface **
_Bool connection_connect(struct connection *self) {
printf("Generic connection (opaque pointer)..\n");
strcpy(self->name, SERVERNAME);
// Socket path
char path[108];
strcpy(path, SERVERNAME);
strcat(path, "_socket");
printf("self->name = %s\n", self->name);
// Allocate the serv socket structure
self->serv = malloc(sizeof(*self->serv));
strcpy(self->serv->sun_path, path);
self->serv->sun_family = AF_UNIX;
printf("self->serv->sun_path = %s\n", self->serv->sun_path);
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
// Locate the host
char hostname[MAX_NAMELEN];
gethostname(hostname, MAX_NAMELEN);
strcpy(self->hostname, hostname);
if ((self->serv_id = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
handle_error("Socket");
return 1;
}
然后 connection
接口方法的分配由客户端处理。对于最小的可重现示例,client
类型实现可以像这样处理接口方法分配:
#include "../connection.h"
typedef struct connection {
void (*connect)();
} *client;
void client_connect(client self) {
connection_connect(self);
printf("Client connection..\n");
}
client make_client(client self) {
client tmp = malloc(sizeof(client));
tmp->connect = client_connect;
return tmp;
}
// Main procedure
int main(int argc, char **argv) {
client c = make_client(c);
c->connect(c);
return 0;
}
执行后,分配的结构似乎首先被正确分配,并且它的实例在最后一个之前都可以访问 printf
:
Generic connection (opaque pointer)..
self->name = freebay
self->serv->sun_path = freebay_socket
[1] 210938 segmentation fault (core dumped)
(gdb) p self->serv
= (struct sockaddr_un *) 0x66203d2068746170
(gdb) x self->serv
0x66203d2068746170: Cannot access memory at address 0x66203d2068746170
我的问题是,为什么 self->serv
在同一方法中使用后无法访问?
您没有分配足够的内存:
self->serv = malloc(sizeof(struct sockaddr_un *));
您为 指针 分配了足够的 space 到 struct sockaddr_un
,而不是其中的一个实例。指针大小小于结构大小,因此当您尝试写入结构时,您写入的结构超出了分配内存的末尾,触发 undefined behavior.
分配 space 的正确方法是:
self->serv = malloc(sizeof(struct sockaddr_un));
甚至更好:
self->serv = malloc(sizeof(*self->serv));
因为后者不关心类型是什么
此外,这是不正确的:
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
因为 %hn
期待 short int *
,而不是 short int
。将其更改为 %hd
.
根据您提供的完整示例,我们现在可以看到您的每个 .c 文件中都有两个同名的不兼容结构。
您的主文件将 struct connection
定义为:
typedef struct connection {
void (*connect)();
} *client;
而 connection.c 将其定义为:
struct connection {
_Bool (*connect)();
char *(*get_name)();
char name[MAX_NAMELEN];
char hostname[MAX_NAMELEN];
uint serv_id;
struct sockaddr_un *serv; // Server socket structure
};
您的主文件在 make_client
中创建前者的一个实例:
client tmp = malloc(sizeof(client));
这本身是无效的,因为您再次为指针而不是结构实例分配了 space。它需要是:
client tmp = malloc(sizeof(*tmp));
但即使你修复了它,你也会将这个指针传递给 connection_connect
,它需要一个指向后一个结构的指针,而不是前一个。
所有将创建 struct connection
实例并修改其成员的函数都应驻留在真正定义所在的 connection.c 中。这就是不透明结构应该如何工作。
在将数据复制到其中之前,您不分配 self->serv->sun_path
。之后肯定出问题了。
除非 sun_path
是 char
的数组。
编译你的代码我发现了这个:
connection.c:37:43: warning: format specifies type 'short *' but the argument has type 'sa_family_t' (aka 'unsigned char') [-Wformat]
printf("self->serv->sun_family = %hn\n", self->serv->sun_family);
~~~ ^~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
如果你写:
printf("self->serv->sun_family = %cn\n", self->serv->sun_family);
程序没有错误