我有以下代码片段:
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_address
到clients[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;