从局部作用域返回初始化结构在 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
的(现已消失的)堆栈,这是当然有问题。
在我们的代码库中,我遇到了一些我无法理解其工作原理的 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
的(现已消失的)堆栈,这是当然有问题。