Продолжая от комментария, нет необходимости выделять указатель на struct client
, а затем выделять для каждого struct . Более разумно было бы просто выделить / перераспределить массив из структуры . Вы также должны убедиться, что:
/* 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
содержит указатель на символ в качестве члена .ip_address
, если ваш "some socket code"
не выделяет память для .ip_address
, вам придется скопировать этот элемент отдельно (a глубокая копия). Используя простое назначение членов с длительностью автоматического хранения , вы можете выделить и скопировать 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;
}