在结构中分配内存时出现不可预测的行为

Posted

技术标签:

【中文标题】在结构中分配内存时出现不可预测的行为【英文标题】:Unpredictable behaviour while allocating memory in struct 【发布时间】:2021-07-31 07:06:11 【问题描述】:

我是 C 的新手,想了解内存分配和指针是如何工作的。但是我为我的代码的一些奇怪行为而苦苦挣扎。请参阅下面的代码和输出。我正在使用 mingw,gcc 版本 4.9.2 (tdm-1),不确定它是错误还是我遗漏了什么?向/从函数发送结构的正确方法是什么?可以简单地将静态分配的数组分配给指针吗?顺便说一句,gcc 没有警告。

#include <stdlib.h>
#include <stdio.h>

typedef struct S 

  int *a;

 s_t;

s_t
create_s () 
  s_t s;
  s.a = malloc ( sizeof ( int ) * 5 );
  for ( int i = 0; i < 5; ++i ) 
    s.a [ i ] = i << 1;
  
  return s;


void
fill_s ( s_t s ) 
  for ( int i = 0; i < 5; ++i ) 
    s.a [ i ] = i;
  


void
kill_s ( s_t s ) 
  free ( s.a );


void
fill1_s_from_const ( s_t s ) 
  int array [ 5 ] =  11, 21, 31, 41, 51 ;
  s.a = array;


s_t
fill2_s_from_const () 
  int array [ 5 ] =  12, 22, 32, 42, 52 ;
  s_t s;
  s.a = array;
  return s;


void
copy_s_from_const ( s_t s ) 
  int array [ 5 ] =  111, 222, 333, 444, 555 ;
  for ( int i = 0; i < 5; ++i ) 
    s.a [ i ] = array [ i ];
  


int
main () 

  s_t s = create_s ();
  printf ( "\ncreate_s\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

  fill_s ( s );
  printf ( "\nfill_s\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

  copy_s_from_const ( s );
  printf ( "\ncopy_s_from_const\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

  kill_s ( s );

  // not working at all (array filled with garbage)
  fill1_s_from_const ( s );
  printf ( "\nfill1_s_from_const\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

  // works partly (array filled correctly but some fields are still full of garbage)
  s = fill2_s_from_const ();
  printf ( "\nfill2_s_from_const\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

  // same as fill1_s_from_const or fill2_s_from_const (imo) but works perfectly fine
  int b [ 5 ] =  11, 22, 33, 44, 55 ;
  s.a = b;
  printf ( "\ninline\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

Output

【问题讨论】:

请详细说明“奇怪的行为”。您的预期输出是什么,实际输出是什么?另外,请将输出直接添加到问题中(作为代码),而不是仅链接到图像。您可能想阅读以下内容:Why not upload images of code/errors when asking a question? 【参考方案1】:

好的,我知道了。

fill_s 有效,因为我按值发送 s,因此 fill_s 中的 s.a 与 main 中的 s.a 具有相同的值(指向相同的内存块),而两个 s'es 都是单独的结构实例。

fill1_s_from_const 的工作方式不同,因为我将值分配给在函数调用时创建的本地 s.a,它与 main 中的 s.a 不同(值是)。如果 s 将作为指针传递,函数将作为 fill2_s_from_const 工作。

fill2_s_from_const 只是将指向本地内存块的指针设置为 s.a。当程序返回主程序时,存储在其中的数据已经被破坏(在我的情况下——部分地,这让我感到困惑)。

对不起这个愚蠢的问题(不能删除它)。

【讨论】:

【参考方案2】:
void
fill1_s_from_const ( s_t s ) 
  int array [ 5 ] =  11, 21, 31, 41, 51 ;
  s.a = array;

结构是按值传递的,所以这个函数不做任何事情。在你调用它之后,调用者的 s.a 仍然是它之前的样子,在你的情况下是一个指向你刚刚释放的内存的指针。

s_t
fill2_s_from_const () 
  int array [ 5 ] =  12, 22, 32, 42, 52 ;
  s_t s;
  s.a = array;
  return s;

通过引用将数组分配给指针,因此调用此函数会导致在s.a 中为调用者提供一个悬空指针。

  // same as fill1_s_from_const or fill2_s_from_const (imo) but works perfectly fine
  int b [ 5 ] =  11, 22, 33, 44, 55 ;
  s.a = b;
  printf ( "\ninline\n" );
  for ( int i = 0; i < 5; ++i ) 
    printf ( "%d\n", s.a [ i ] );
  

之所以有效,是因为与fill2_s_from_const 不同,指针在您在这里使用之前不会悬空。

【讨论】:

以上是关于在结构中分配内存时出现不可预测的行为的主要内容,如果未能解决你的问题,请参考以下文章

在 CUDA 的 __device__ 函数中使用动态分配时出现“未知错误”

如何在C中的堆栈中分配超过所需的内存?

JAVA里String数组在内存分配中分配的空间每个占几个字节?

在 Linux 内核中分配用户空间内存

多种数据结构之间比较

通过指针子函数中分配内存,在主函数中实现赋值