【问题标题】:Copying a pointer of a struct in C在C中复制结构的指针
【发布时间】:2019-04-26 03:03:49
【问题描述】:

我有以下代码sn-p:

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 行导致分段错误,我做错了什么?

【问题讨论】:

  • 您分配和重新分配pointers,但从不为struct client 自己分配存储空间。您可以通过声明 struct client *clients; 而不是指针和结构来简化。您还必须为每个char* ip_address; 进一步分配或使ip_address 成为固定大小的缓冲区。
  • 它可能与此无关,但是为什么 memcopy 行中的“客户”周围没有任何括号,大小为... memcpy(clients[n_clients-1], client, sizeof client); 不应该是: memcpy(clients[n_clients-1], client, sizeof( client));
  • @user3801839 sizeof 不是正常功能。它是一元运算符。括号是可选的。
  • 我知道是的。只是不要以为我见过没有括号的内容
  • @DavidC.Rankin 我用clients* 替换了clients**,现在我得到了错误:realloc(): invalid pointer,这似乎与memcpy 再次有关。

标签: c pointers segmentation-fault


【解决方案1】:

从注释继续,没有必要分配指针到struct client,然后为每个struct分配。简单地分配/重新分配 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 分配存储空间,您将不得不单独复制该成员(深拷贝)。使用具有自动存储持续时间的简单成员分配,您可以将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;
            }

【讨论】:

  • 我实现了这些更改,但仍然出现分段错误。当我使用gdb 进行跟踪时,它会吐出__memmove_avx_unaligned_erms at ....,所以我认为memcpy 仍然失败。
  • 我已经修复了上面的错误,我忘了最初分配内存给clients。但是,在 now 运行时,如果我添加一个 IP 为 1.2.3.4 的客户端,阵列中的所有客户端都将成为同一个客户端。
  • 使用struct client client = {client_ip_address, client_port, timestamp}; 不会分配client.ip_address。您必须分配存储空间和副本。否则,您存储的所有clients 最终将指向最后填充的ip_address
  • 很好,这是唯一的症结所在。它应该根据需要运行和分配/重新分配,直到计算机上的物理内存限制:)
  • 是的,但是如果我的数组只包含同一客户端的多个副本,这对我没有用——我怎样才能让它复制值,而不是内存地址?
【解决方案2】:

首先我们需要处理错误。

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 不存储指针,它存储整个结构。你需要传入&amp;client


问题是你试图复制一个结构,指向一个结构的指针应该去的地方。

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

memcpy(clients[n_clients-1], &amp;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;

【讨论】:

  • 在第二个示例中,最后一行导致编译错误:incompatible types when assigning to type ‘struct client’ from type ‘struct client *’
  • @Mr.King 您将clientsstruct client **(指向结构的指针数组)更改为struct client *(结构数组)。 Here's the full code.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-19
  • 2021-07-14
  • 1970-01-01
相关资源
最近更新 更多