Копирование указателя структуры в C - PullRequest
0 голосов
/ 26 апреля 2019

У меня есть следующий фрагмент кода:

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

По сути, я пытаюсь отслеживать IP, порт и временную метку всех клиентов, которые подключаются к моему сокету внутри массиваclients.Однако строка memcpy приводит к ошибке сегментации, что я делаю не так?

Ответы [ 2 ]

1 голос
/ 26 апреля 2019

Продолжая от комментария, нет необходимости выделять указатель на 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;
            }
0 голосов
/ 26 апреля 2019

Сначала нам нужно разобраться с ошибкой.

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*)); выделяет место для n_clients указателей в 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;
...