【问题标题】:Is it safe to return a struct with pointer members that point to internal struct arrays?返回带有指向内部结构数组的指针成员的结构是否安全?
【发布时间】:2019-08-08 08:15:02
【问题描述】:

如果结构体中有指向结构体其他成员的指针,如果我们从函数返回结构体,是否可以保证这些指针仍指向结构体的内部成员?

以这个小尺寸优化的缓冲区结构为例:

#include <stdint.h>
#include <stdio.h>

struct buffer {
    uint8_t *begin;
    uint8_t *end;
    uint8_t sso[20];
};

struct buffer test() {
    struct buffer buffer;
    buffer.begin = buffer.sso;
    buffer.end = buffer.begin + 20;

    return buffer;
}

int main(void) {
  struct buffer buffer = test();

  return buffer.begin == buffer.sso ? 0 : 1;
}

当我在本地编译这段代码并运行它时,它返回 0 所以buffer.begin 仍然指向buffer.sso 的开头。但是,这是否保证始终是大小写,还是我取决于特定于编译器的 RVO 行为?

【问题讨论】:

  • 我的测试返回 1。它要求两个 structs 在内存中的相同位置。如果您添加struct buffer buffer2 = buffer;,您会期望地址发生神奇的变化吗?

标签: c


【解决方案1】:

没有。当函数返回时,test() 函数内部的 buffer 后面的内存将停止存在。函数返回后,指向此bufferbuffer 内部任何成员的所有指针均无效。

以下是一个有效的替代方案:

#include <stdint.h>
#include <stdio.h>

struct buffer {
    uint8_t *begin;
    uint8_t *end;
    uint8_t sso[20];
};

void buffer_init(struct buffer *buffer) {
    buffer->begin = buffer->sso;
    buffer->end = buffer->begin + 20;
}

int main(void) {
    struct buffer buffer;
    buffer_init(&buffer);

    return buffer.begin == buffer.sso ? 0 : 1;
}

【讨论】:

  • 内存不会停止存在。对象的生命周期结束。
【解决方案2】:

它不起作用,因为分配局部变量buffer 的内存超出范围,但指针仍将指向该区域。在 main() 中分配的 buffer 将具有不同的地址。该结构的内容是从函数中复制的,因此您必须更新指针。

一个更好的版本是不使用指针,而是使用数组索引:

struct buffer {
    size_t  begin;
    size_t  end;
    uint8_t sso[20];
};

struct buffer test() {
    struct buffer buffer;
    buffer.begin = 0;
    buffer.end = 20;
    return buffer;
}

这个你可以安全地从函数中返回,一切仍然有效。

【讨论】:

  • 标识符有范围。内存没有范围,因此不能超出范围。本地对象的生命周期在其关联块的执行结束时结束(不仅仅是在执行超出其范围时,如在调用子例程时)。这很重要,因为即使在直接或间接调用的子例程的完全不同范围内执行时,指向此类对象的指针也是有效的。
猜你喜欢
  • 1970-01-01
  • 2014-12-30
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多