从局部作用域返回初始化结构在 C11 中意外有效

Returning an initialized structure from local scope unexpectedly works in C11

在我们的代码库中,我遇到了一些我无法理解其工作原理的 C 代码。

非常肯定会实现在 Internet 上找到的某些模式。理想情况下,此代码应模拟 C++ 中的某些面向对象的模式,并用于创建队列。

这里是队列模块声明(.h)的(部分)代码:

struct Queue_t
{
  uint8_t         Queue[MAX_QUEUE_SIZE];
  uint32_t        Head;
  uint32_t        Tail;
  uint16_t        Counter;
  Queue_return_t  (*Push)(struct Queue_t * Queue, uint8_t * NewElement, uint16_t ElementSize);
  Queue_return_t  (*Pop)(struct Queue_t * Queue, Queue_Element_t *RetElement);
  Queue_return_t  (*Flush)(struct Queue_t * Queue);
};

extern const struct QueueClass {
        struct Queue_t (*new)( void );
} Queue_t;

struct Queue_t new( void );

这里是队列模块实现(.c)的(部分)代码:

struct Queue_t new( void )
{
  struct Queue_t NewQueue;

[...]
  NewQueue.Push = &QueueManager_Push;
  NewQueue.Pop = &QueueManager_Pop;
  NewQueue.Flush = &QueueManager_Flush;
  return NewQueue;
}

const struct QueueClass Queue_t={.new=&new};

那么代码中的用法如下:

struct Queue_t Output_Queue;
Output_Queue = Queue_t.new();
[...]
RetQueue =  Output_Queue.Pop(&Output_Queue,Output_Queue_elem);

现在,我们切换到更直接的队列实现。 我仍然无法理解这段代码中发生了什么。

如标​​题所述,我的问题出在“新”函数中,其中在局部范围内声明了一个 struct Queue_t 然后返回。

作为进一步的信息,在使用此“队列”模块的项目中没有动态内存分配,因此没有堆、空闲或 malloc。

一切正常真的顺利,我希望一旦释放引用对象的堆栈并访问指向结构的指针,代码就会崩溃。

此外,使用的编译器是 IAR,并没有抱怨(此代码用于 uC)。

可能涉及 const 限定符?

关于发生了什么的任何提示?

在函数内声明 return 一个 value 是完全合法的。仅当您 return 将 指针 指向函数内声明的值,然后尝试访问它时才会出现此问题。

您显示的new()函数等同于:

int foo(void) {
    int a = 123;
    return a;
}

这里没有问题,您正在 return 正在 复制 到目的地的值:

int x;
x = foo(); // copy happens here

在此之后使用 x 而不是 访问 foo 的(现已消失的)堆栈,因为 x 是在 foo 并在函数 returned.

时被复制

struct 没有什么不同,struct 仍然被视为单个值,唯一真正的区别在于生成的机器代码,因为复制结构需要更多的努力(您需要复制每个字段)。

同样,只有当您 return 指向本地定义变量的指针并尝试在外部使用它时,问题才会出现:

int *foo(void) {
    int a = 123;
    return &a;     // never do this!
} 

int main(void) {
    int *x;
    x = foo();
    return *x;     // OUCH!
}

现在取消引用 x(用于读取和写入)意味着尝试访问foo的(现已消失的)堆栈,这是当然有问题。