1

当从定义文件访问不透明结构内部的结构时,我遇到了一个奇怪的分段错误。我最近刚刚了解了不透明指针,但我的猜测是,我在分配内部结构时做错了。但是,在第一次调用该结构时,它似乎可以工作,并且在同一方法中无法访问它。

所以我在里面定义了我的不透明结构指针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我内部分配servtype 的内部结构指针struct sockaddr_un

#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
$1 = (struct sockaddr_un *) 0x66203d2068746170
(gdb) x self->serv
0x66203d2068746170: Cannot access memory at address 0x66203d2068746170

我的问题是,为什么self->serv在同一方法中使用它后无法访问?

4

3 回答 3

4

您没有分配足够的内存:

self->serv = malloc(sizeof(struct sockaddr_un *));

您为指向 a 的指针分配了足够的空间,struct sockaddr_un不是 a 的实例。指针大小小于结构大小,因此当您尝试写入结构时,您写入的内存超出了分配内存的末尾,从而触发了未定义的行为

分配空间的正确方法是:

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期待 a short int *,而不是 a 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));

这本身是无效的,因为您再次为指针分配空间,而不是结构的实例。它需要是:

client tmp = malloc(sizeof(*tmp));

但是即使你修复了这个问题,你也会传递这个指针,connection_connect它需要一个指向后者结构的指针,而不是前者。

所有将创建实例struct connection并修改其成员的函数都应驻留在真正定义所在的 connection.c 中。这就是不透明结构应该如何工作的方式。

于 2021-02-14T17:06:52.817 回答
1

编译你的代码我发现了这个:

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);

程序没有错误

于 2021-02-14T21:14:50.303 回答
-1

self->serv->sun_path在将数据复制到它之前不要分配。在那之后一定会出问题。

除非sun_path是一个数组char

于 2021-02-14T17:40:01.417 回答