【问题标题】:C/C++ malloc a block of memory and then use different portion for different, arbitrary typesC/C++ malloc 一块内存,然后为不同的任意类型使用不同的部分
【发布时间】:2015-04-10 01:17:21
【问题描述】:

我当然没有理由这样做。假设我很无聊,想探索这是否可能。 :)

假设我想实现一个链表的节点。我当然可以

struct Node {
  int val; 
  Node *prev, *next; 
}

但是假设我很无聊,我想做一些事情,比如为一个节点分配一块内存,然后任意决定第一部分是指向前一个节点的指针,中间部分是值,最后一部分是指向下一个节点的指针。是否可行?

我尝试了类似的方法,但它无法运行。

*second = first

特别失败。

void** createNewNode(int val) {
    void** p = (void**)malloc(sizeof(int) + 2*sizeof(void*));
    *(p+1) = &val; 
}

void connectTwoNodes(void** first, void** second) {
    *(first+2) = second; 
    cout << "first assigned!" << endl;
    *second = first;
    cout << "second assigned!" << endl;
}

int main() {

    void** p1 = createNewNode(1);
    void** p2 = createNewNode(2);
    cout << "both created!" << endl;
    connectTwoNodes(p1, p2);

    return 0;
}

【问题讨论】:

  • 这很棘手,因为您不能确定所有地址都将针对数据类型正确对齐。
  • 你不能通过 void 指针间接。您必须将它们转换为适当的类型
  • 您可以使用memcpy 从一个指针复制到另一个指针,而不是使用赋值。
  • struct Node { std::string val; Node *prev, *next; }; 现在,一旦您实际使用 Node..
  • "...那么任意决定第一部分是指向前一个节点的指针,中间部分是值,最后部分是指向下一个节点的指针"。换句话说,您希望将其视为具有元素 prev、val 和 next 的新结构,按此顺序。那么你可以定义新的结构 rearranged_node,将原始 *node 转换为 *rearranged_node,然后复制成员(使用临时,因为你正在原地覆盖)。

标签: c++ c malloc


【解决方案1】:

嗯,一般的想法是可能的,但你的代码有很多错误:

  • p+1first + 2 是非法的,因为你不能对 void * 进行点运算。在进行算术运算之前,您必须将指针转换为指向完整类型。
  • *second 是非法的,因为 void 是不完整的类型。
  • sizeof(void *) 可能与 sizeof(Node *) 不同
  • 如果你曾经打算写 struct Node *ptr = p1; ,由于结构填充,这实际上是行不通的。同样,尝试通过转换地址来访问Node * 指针可能会由于对齐而失败。您必须 memcpy 将指针移出存储空间。

一般来说,您可以在malloc'd 空间中读取和写入任何您喜欢的内容,但要遵守对齐和严格的别名规则。换而言之,后者表示,如果您将某些内容写入内存,则无法将其作为不同的类型读出(除非该不同的类型是字符类型)。但是你可以覆盖它。

我无法弄清楚您在connectTwoNodes 中要做什么。但是在 C 中做类似事情的代码可能是:

void *createNewNode(int val) 
{
    char *cp = malloc(sizeof(int) + 2 * sizeof(void *));
    void *null_node = NULL;

    memcpy(cp, &val, sizeof val);
    memcpy(cp + sizeof val, &null_node, sizeof null_node);
    memcpy(cp + sizeof val + sizeof null_node, &null_node, sizeof null_node);
    return cp;
}

void connectTwoNodes(void* first, void* second) 
{
    char *c1 = first, *c2 = second;

    // first->next = second
    memcpy(c1 + sizeof(int) + sizeof(void*), &second, sizeof second);

    // second->prev = first
    memcpy(c2 + sizeof(int), &first, sizeof first);
}

int main()
{
    void* p1 = createNewNode(1);
    void* p2 = createNewNode(2);
    connectTwoNodes(p1, p2);  
}

tl;dr:不要这样做

【讨论】:

  • 您想使用placement new 让C++ 为您构建您的对象。在普通结构的情况下,这不是绝对必要的,但它是 C++ 的方法,也是用适当的类来做你描述的事情的唯一方法。 isocpp.org/wiki/faq/dtors#placement-new
  • 谢谢。还有一个问题,为什么你在 connectTwoNodes() 中使用 char* 类型?
  • @CodeNoob 这样+就可以用在上面了
  • @Ben 我的答案是针对 C。无论如何,这种技术只能在 C++ 中用于可复制的类型;放置新并不能解决这个问题。
【解决方案2】:

补充其他人所说的,您也不能假设结构的大小与其字段大小的总和相同。

【讨论】:

    猜你喜欢
    • 2015-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 2016-02-13
    • 1970-01-01
    相关资源
    最近更新 更多