【问题标题】:Storing void pointers in a struct在结构中存储 void 指针
【发布时间】:2015-12-14 10:13:09
【问题描述】:

我在尝试初始化指向 0mq 上下文和套接字的指针结构时遇到分段错误。 main 方法中注释掉的代码有效,但它只使用局部变量。我想初始化它们并在结构中传递它们,但我的 google foo 让我无法正确执行此操作。

#include "zhelpers.h"
#include <stdio.h>
#include <stdlib.h>
#include <zmq.h>

struct publisher{
    void *handle;
    void *context;
};

void init_publisher(struct publisher *p);
void destroy_publisher(struct publisher *p);
void publish(struct publisher *p,char *msg);

void init_publisher(struct publisher *p)
{
    p = (struct publisher *)malloc(sizeof(struct publisher));
    p->context = malloc(sizeof(void *));
    p->handle = malloc(sizeof(void *));
    void *context = zmq_ctx_new();
    void *handle = zmq_socket(context,ZMQ_PUB);
    zmq_bind(handle, "tcp://*:5556");
    zmq_bind(handle, "ipc://feed.ipc");
    p->context = context;
    p->handle = handle;
}

void destroy_publisher(struct publisher *p)
{
    zmq_close(p->handle);
    zmq_ctx_destroy(p->context);
    free(p->handle);
    free(p->context);
    free(p);
}

void publish(struct publisher *p,char *msg)
{
    s_send(p->handle, msg);
}

int main(void)
{
/**
    void *context = zmq_ctx_new();
    void *publisher = zmq_socket(context, ZMQ_PUB);
    int rc = zmq_bind(publisher, "tcp://*:5556");
    assert(rc == 0);
    rc = zmq_bind(publisher, "ipc://weather.ipc");
    assert(rc == 0);
    printf("Started Weather Server...\n");

    srandom((unsigned) time (NULL));
    int zipcode, temperature, relhumidity;
    zipcode = randof(100000);
    temperature = randof (215) - 80;
    relhumidity = randof (50) + 10;

    char update[20];
    sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
    s_send(publisher, update);
    zmq_close(publisher);
    zmq_ctx_destroy(context);
*/

    struct publisher *p;
    init_publisher(p);
    printf("Setup pub\n");

    srandom((unsigned) time (NULL));
    int zipcode, temperature, relhumidity;
    zipcode = randof(100000);
    temperature = randof (215) - 80;
    relhumidity = randof (50) + 10;
    char update[20];
    sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
    publish(p,update);
    printf("Published Message\n");

    destroy_publisher(p);
    printf("Destroyed publisher\n");
    return 0;
}

【问题讨论】:

  • 避免使用 void 指针——它有一定的含义
  • p = (struct publisher *)malloc(sizeof(struct publisher)); ---> p = malloc(sizeof(struct publisher)); 一个问题是您没有检查malloc() 是否没有返回NULL。另一个问题是你不知道代码到底在哪里崩溃,使用调试器。
  • @iharob 它在 zeromq 代码中崩溃。但是在这两种情况下,实际初始化 void 指针的调用是相同的。所以在我看来,我没有正确设置结构,因为如果我在 main 方法(注释掉的代码)中完成所有工作,它不会引发任何错误。
  • 我建议只从您的 init_publisher 方法返回一个“struct publisher *”。如果你真的不想这样做,在这种情况下,你必须传递一个指针到指针(struct publish **p)。

标签: c pointers struct zeromq


【解决方案1】:

这段代码中似乎没有任何东西会导致它崩溃。 (假设您知道所有 zmq_... 的工作原理。)

如果您能准确地告诉我们错误发生的位置会有所帮助,但我的猜测是错误发生在此代码之外

您看,您将struct publisher *p 传递给您的init_publisher() 函数,但随后您在该方法内为p 分配内存(这使得传递p 毫无意义)然后您没有返回@ 987654326@。结果,调用init_publisher() 的代码可能期望p 被初始化,但事实并非如此。 p 指向的内存只是在您的 init_publisher() 函数中本地分配和泄漏。

所以,不要传递p,而是让函数声明它并返回它。

或者,如果调用者已经分配了p,则不要从init_publisher() 中重新分配它。


还请注意,p-&gt;context = malloc(sizeof(void *)); 语句是不必要的,它们会泄漏少量内存,因为您继续覆盖这些结构成员。

【讨论】:

  • 不知道为什么这被否决了,它是准确的。回到 0。
  • @DanielSloof 好吧,我写过“这段代码似乎没有任何问题”,这在技术上是不正确的,我将其更改为“这段代码中没有任何内容会导致它崩溃”。
  • 我没有投反对票,我想这是由于“完全毫无意义的陈述”部分。我切换到从 init_publisher 方法返回指针并且它有效。我想我很困惑,因为我假设如果你将一个指向某个东西的指针传递给一个方法并执行一个 malloc,那么它实际上会在它返回时保留内存。
  • 马特,它会保留内存。但是p按值 传递的,所以调用者永远不会看到这个内存。如果p 在调用前的调用者上下文中为NULL,则p保留 NULL 在函数返回后调用者的上下文中。
  • 啊,按值部分是我缺少的部分。当您习惯于通过参考语言传递时会发生这种情况:) 感谢您的帮助。
【解决方案2】:

问题是传递的指针和你malloc()ed的指针不一样。传递的指针包含与原始指针相同的地址,可能是无效地址,但它们自身的指针地址不同,因为在 中,您只能按值传递变量,因此指针被复制。

这意味着当您在函数内部重新分配p 时,函数外部的p 不会改变。如果它被分配在外部并且您只是使用该函数访问它的成员,那将是不同的。

您也不需要malloc() 要使用的每个指针,问题是它必须在取消引用之前指向一个有效地址。当您想请求新的未初始化内存时,请使用malloc(),否则您只需将指针指向一个有效地址,以便定义解引用它,使用没有malloc()ing 的指针的一个示例是

int *pointer;
int value;
value = 4;
pointer = &value; // Now `pointer' points to `value's` address
*pointer = 3;
printf("%d\n", value);

编写函数的一种方法是

int
init_publisher(struct publisher **pp)
{
    struct publisher *p;
    *pp = malloc(sizeof(struct publisher));
    if (*pp == NULL)
        return -1;
    p = *pp;
    p->context = zmq_ctx_new();
    p->handle = zmq_socket(context,ZMQ_PUB);
    if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
    {
        zmq_bind(p->handle, "tcp://*:5556");
        zmq_bind(p->handle, "ipc://feed.ipc");
    }
    return 0;
}

然后你可以像这样使用它

struct publisher *p;
if (init_publisher(&p) != 0)
    do_something_there_was_an_error();
/* Continue using `p' */

请注意,该函数返回一个指示分配是否成功的值。通常malloc() 不会失败,但这并不意味着您应该忽略可能的失败。

当我说如果你先分配p 时,我的意思是如果你这样做

struct publisher *p;
p = malloc(sizeof(*p));
if (p == NULL)
    return handle_error();
init_publisher(p);

那么init_publisher() 可能是

void
init_publisher(struct publisher *pp)
{
    void *context;
    void *handle;
    p->context = zmq_ctx_new();
    p->handle = zmq_socket(context,ZMQ_PUB);
    if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
    {
        zmq_bind(p->handle, "tcp://*:5556");
        zmq_bind(p->handle, "ipc://feed.ipc");
    }
}

这可能是你想要做的。

【讨论】:

  • 是的,你的最后一个例子就是我的想法。但不能完全到达那里。我评论了迈克的回答,我认为真正的问题是我不明白即使是指针也是按值传递的,如果你改变它,它也不会在方法调用内部更新。我假设它是通过引用传递的,可能是由于大部分时间使用 C# 而不必担心这一点(当然除了值类型)。结构!=类。明白了:)
  • 另外,与c# 不同,c 中没有自动内存处理,没有垃圾收集器,没有类似的东西。因此,请小心调用free(),但也要确保在调用malloc() 时已成功分配内存。
猜你喜欢
  • 1970-01-01
  • 2016-03-21
  • 2014-10-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-03
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
相关资源
最近更新 更多