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

Unpredictable behaviour while allocating memory in struct

提问人:Случайная Котя 提问时间:5/10/2021 更新时间:5/10/2021 访问量:43

问:

我是 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 ] );
  }
}

输出

阵列 c gcc struct malloc

评论

1赞 Andreas Wenzel 5/10/2021
请详细说明“奇怪的行为”。您的预期产出是多少,实际产出是多少?此外,请将输出直接添加到问题中(作为代码),而不是仅链接到图像。您可能想阅读以下内容: 为什么在提问时不上传代码/错误的图像?

答:

1赞 Joseph Sible-Reinstate Monica 5/10/2021 #1
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

0赞 Случайная Котя 5/10/2021 #2

好的,我明白了。

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。当程序返回到主程序时,存储在其中的数据已经被销毁(在我的情况下 - 部分地,这让我感到困惑)。

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