简体   繁体   中英

Copying a pointer of a struct in C

I have the following code snippet:

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

Basically, I'm trying to keep track of the IP, port and timestamp of all of the clients that connect to my socket, inside of the array clients . However, the memcpy line results in a segmentation fault, what am I doing wrong?

Continuing from the comment, it is not necessary to allocate pointer to struct client and then allocate for each struct . It would make more sense to simply allocate/reallocate an array of struct . You also must ensure your:

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

actually allocates storage for char *ip_address; as it is simply a pointer and must point to valid storage before it is used.

Your allocate/realloc scheme is also slightly out of order. You wan to check whether reallocation is needed before you attempt to use additional storage in your array. Additionally, you always realloc to a temporary pointer to avoid loss of the pointer to your data is (and when) realloc fails returning NULL . If you realloc with the original pointer, eg clients = realloc (clients, ...) and realloc returns NULL you overwrite your pointer address with NULL losing your pointer and creating a memory-leak.

Shuffling the order around and implementing the use of a temporary pointer, you could do something similar to:

#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++;
        }
    }
}

( note: storage for client.ip_address must be of allocated type, further, since client is simply a struct, clients[n_clients] = client; is sufficient - and client must be fully filled with no other members or sub-members requiring a deep-copy of additional allocated members.)

Don't forget to free() the memory you allocate when it is no longer needed.

Edit - some socket code doesn't allocate for client.ip_address

Since your struct client contains a pointer to char as the .ip_address member, if your "some socket code" does not allocate storage for .ip_address , you will have to copy that member separately (a deep-copy). Using a simple assignment of members with automatic-storage duration , you can allocate and copy client.ip_address to clients[n_clients].ip_address separately as follows:

        /* 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++;
        }

(that also means you will have to free() each .ip_address member separately before you free the array)

If your compiler provides strdup() , you can simplify the copy of .ip_address as:

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

First we need to deal with the error.

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 takes pointers. client does not store a pointer, it stores the whole struct. You need to pass in &client .


The problem is you're trying to copy a struct where a pointer to a struct should go.

struct client** clients is an array of struct client* . That its, it's an array of pointers. clients = realloc(clients, n_clients * sizeof(struct client*)); is allocating space for n_clients pointers in clients . That's fine.

memcpy(clients[n_clients-1], &client, sizeof client); is trying to copy the whole struct into a space that's only supposed to take a pointer. It's shoving 16 bytes into 8 bytes of space (assuming 64 bits).

client is using automatic stack memory which will be overwritten as soon as you leave the block. You need to allocate heap memory for it, copy it to that memory, and store the pointer to the heap memory in 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;

But it's easier to skip the stack memory and allocate client as heap memory in the first place.

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM