在 C 中复制结构的指针



我有以下代码片段:

struct client {
char* ip_address;
int port;
struct timeval last_seen;
};
/* setup socket */
struct client** clients = malloc(0);
unsigned int n_clients = 0;
for (uint32_t iter = 0; ; iter++) {
/* some socket code, that populates client_ip_address, client_prt and timestamp */
n_clients++;
struct client client = {client_ip_address, client_port, timestamp};
clients = realloc(clients, n_clients * sizeof(struct client*));
memcpy(clients[n_clients-1], client, sizeof client);
}

基本上,我正在尝试跟踪阵列clients内连接到我的套接字的所有客户端的 IP、端口和时间戳。但是,memcpy行会导致分段错误,我做错了什么?

从注释继续,没有必要分配指向struct client指针,然后为每个结构分配。简单地分配/重新分配结构数组会更有意义。您还必须确保您的:

/* some socket code populates ip_address, port and timestamp */

实际上为char *ip_address;分配存储,因为它只是一个指针,并且在使用之前必须指向有效的存储。

您的分配/重新分配方案也略有失序。在尝试使用阵列中的其他存储之前,您需要检查是否需要重新分配。此外,您始终realloc到临时指针,以避免丢失指向数据的指针是(以及何时)realloc失败返回NULL。如果您使用原始指针重新分配,例如clients = realloc (clients, ...)realloc返回NULL,会覆盖指针地址,NULL丢失指针并创建内存泄漏。

打乱顺序并实现临时指针的使用,您可以执行类似于以下操作的操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NSTRUCT 8   /* initial number of struct to allocate */
struct client {
char *ip_address;   /* must be allocated separately */
int port;
struct timeval last_seen;
};
int main (void) {
size_t  n_clients = 0,          /* number of clients filled */
n_alloced = NSTRUCT;    /* number of clients allocated */
struct client *clients = malloc (n_alloced * sizeof *clients);
if (!clients) { /* validate every allocation */
perror ("malloc-clients");
return 1;
}
for (uint32_t iter = 0; ; iter++) {
if (n_clients == n_alloced) {   /* check if realloc required */
/* always realloc with a temporary pointer, or risk data loss */
void *tmp = realloc (clients, 2 * n_alloced * sizeof *clients);
if (!tmp) { /* validate reallocation */
perror ("realloc-clients");
break;  /* don't exit, original clients still valid */
}
clients = tmp;  /* assign reallocated block to clients */
n_alloced *= 2; /* update allocated number of struct */
}
struct client client = {client_ip_address, client_port, timestamp};
/* some socket code populates ip_address, port and timestamp */
if (/* client filled correctly */) {
memcpy (&clients[n_clients], &client, sizeof client);
n_clients++;
}
}
}

(注意:client.ip_address的存储必须是已分配类型,此外,由于client只是一个结构,因此clients[n_clients] = client;就足够了 - 并且client必须完全填充,没有其他成员或子成员需要其他已分配成员的深层副本

不要忘记在不再需要时free()分配的内存。

编辑 - 某些套接字代码未分配client.ip_address

由于struct client包含指向char作为.ip_address成员的指针,因此如果您的"some socket code"没有为.ip_address分配存储空间,则必须单独复制该成员(深层复制)。使用具有自动存储持续时间的成员的简单分配,您可以分别分配和复制client.ip_addressclients[n_clients].ip_address,如下所示:

/* some socket code populates ip_address, port but doesn't
* allocate storage for client.ip_address -- you must copy.
*/
if (/* client filled correctly */) {
/* assignment is sufficient for non-allocated members */
clients[n_clients] = client;  
size_t len = strlen (client.ip_address);
clients[n_clients].ip_address = malloc (len + 1);
if (!clients[n_clients].ip_address) {
perror ("malloc-clients[n_clients].ip_address");
break;
}
memcpy (clients[n_clients].ip_address, client.ip_address,
len + 1);
n_clients++;
}

(这也意味着在释放数组之前,您必须分别free()每个.ip_address成员)

如果编译器提供strdup(),则可以将.ip_address的副本简化为:

clients[n_clients].ip_address = strdup (client.ip_address);
/* strdup allocates -- so you must validate */  
if (!clients[n_clients].ip_address) {
perror ("strdup-clients[n_clients].ip_address");
break;
}

首先我们需要处理错误。

cc -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic -g `pkg-config --cflags glib-2.0 --cflags json-glib-1.0 --cflags json-c`   -c -o test.o test.c
test.c:23:34: error: passing 'struct client' to parameter of incompatible type
'const void *'
memcpy(clients[n_clients-1], client, sizeof client);
^~~~~~

memcpy需要指针。client不存储指针,而是存储整个结构。您需要传入&client.


问题是您正在尝试复制指向结构的指针应该所在的结构。

struct client** clients是一组struct client*。它,它是一个指针数组。clients = realloc(clients, n_clients * sizeof(struct client*));正在为clients中的n_clients指针分配空间。没关系。

memcpy(clients[n_clients-1], &client, sizeof client);试图将整个结构复制到一个只应该接受指针的空间中。它将 16 个字节推入 8 个字节的空间(假设为 64 位)。

client正在使用自动堆栈内存,一旦您离开块,该内存将被覆盖。您需要为其分配堆内存,将其复制到该内存,并将指向堆内存的指针存储在clients中。

n_clients++;
// Put the struct in stack memory
struct client client = {client_ip_address, client_port};
// Allocate space for the pointer in the list.
clients = realloc(clients, n_clients * sizeof(struct client*));
// Allocate space for the struct in heap memory.
struct client *tmp = malloc(sizeof(struct client));
// Copy the struct from stack to heap memory
memcpy(tmp, &client, sizeof client);
// Store the pointer to heap memory
clients[n_clients-1] = tmp;

但是,首先跳过堆栈内存并client分配为堆内存更容易。

n_clients++;
// Allocate heap memory for the struct.
struct client* client = malloc(sizeof(struct client));
// Initialize the struct.
client->ip_address = client_ip_address;
client->port = client_port;
// Allocate space for the pointer.
clients = realloc(clients, n_clients * sizeof(struct client*));
// Store the pointer.
clients[n_clients-1] = client;

相关内容

  • 没有找到相关文章

最新更新