为什么即使我没有返回对象,我的结构也会被清零?

时间:2017-06-26 15:27:29

标签: c

我一直在涉及一些c代码,并初始化了像这样的猫结构

typedef struct 
{
   int age;
   char *name;
   char *favoriteQuote;
} Cat;

我创建了两个函数,一个用于初始化cat对象,另一个用于清零看起来像这样的内存

Cat initialize_cat_object(void)
{
   Cat my_cat;
   my_cat.age = 3;
   my_cat.favorite_quote = "A day without laughter is a day wasted";
   my_cat.name = "Chester";
   return my_cat;
} 

Cat destroy_cat_object(void)
{
  Cat my_cat;
  memset(&my_cat, 0, sizeof(my_cat));
  //--forgot to return 'my_cat' here--
}

我的主要功能如此

void main(void)
{
    Cat my_cat;
    my_cat = initialize_cat_object();
    printf("Creating cat\n")
    printf("Name: %s\nFavoriteQuote: %s\nAge: %d\n", my_cat.name,
            my_cat.favorite_quote, my_cat.age);

   my_cat = destroy_cat_obect();

   printf("CAT DESTRUCTION\n");
   printf("Name: %s\nFavoriteQuote: %s\nAge: %d\n", my_cat.name,
           my_cat.favorite_quote, my_cat.age);

}

该计划的产出是预期的产出 Program output

直到我回到源代码,我注意到我忘记返回内存被清零的Cat对象,但程序仍显示预期的输出,但是如果我尝试省略return语句'initialize_cat_object'函数,数据输出已损坏

enter image description here

我唯一能想到的是'destroy_cat_object'返回归零的内存,但这怎么可能呢?

2 个答案:

答案 0 :(得分:3)

destroy_cat_object没有return声明。 C11 6.9.1p12说:

  

如果到达了终止函数的},并且调用者使用了函数调用的值,则行为是未定义的。

然而,对于具有返回类型的函数,但是在结束括号之前没有<{em} 具有return语句,这是完全正常的C标准。调用这样的函数也完全可以。

但是,如果函数没有以显式返回值的return语句终止,那么使用函数调用的返回值是不正确的。

如果没有收到相关消息,您可能希望在编译器设置中启用一些额外的诊断。

答案 1 :(得分:2)

将注册放在一边。您的数据可以存储在C中的三个位置:

  1. 只读数据并存储在二进制文件中的常量数据;
  2. 堆叠数据;
  3. 存储在动态内存中的数据通过内存分配函数检索。
  4. 在你的情况下,我们谈论的是堆栈。 Stack是一个LIFO队列元素,只要它们没有弹出就是有效且可访问的。所以如果你有这样的功能:

    typedef struct {
      int a_val;
      float b_val;
      char c_val;
    } a_t;
    
    a_t* func(void) {
      a_t a = {1, 1., 'a'};
      return &a;
    }
    

    “a”将驻留在堆栈中,直到func返回,因此在func返回后,它的指针变为无效并指向堆栈空间中的某个位置。在大多数系统上,堆栈不会归零,因此在其他一些数据覆盖它之前,可能会通过该指针获取一些数据,这可能会产生误导。

    那你该怎么办?像这样:

    void initialize_cat(Cat*);
    void clear_cat(Cat*);
    
    int main() {
      Cat my_cat;
      initialize_cat(&my_cat);
      // do kitty stuff
      clear_cat(&my_cat); // cat's private data must not be compromised
    }
    

    当函数返回一个结构时,这实际上是通过调用者的合作实现的(我在这里说的是SysV x64 ABI,对于其他情况可能是错误的)。基本上,调用者在堆栈上分配足够的空间来存储返回的结构,并将指针作为隐式的第一个参数传递给它。 callee正在使用此指针稍后写入数据。

    所以这两个案例:

    Cat callee(void) {
       Cat my_cat = { .age = 5 };
       return cat;
    }
    
    void caller(void) {
       Cat my_cat = callee();
    }
    

    void callee(Cat *my_cat) {
       my_cat->age = 5;
       return cat;
    }
    
    void caller(void) {
       Cat my_cat;
       callee(&my_cat);
    }
    

    几乎一样。