【问题标题】:Arrays of structures, dynamic allocation结构数组,动态分配
【发布时间】:2014-03-11 01:04:46
【问题描述】:

我想知道我的动态分配是否正确完成,因为我目前遇到这些错误:

我想要一个结构数组,每个结构包含四个元素。

struct train
{
    char* start;
    char* destination;
    int start_time;
    int arrival_time;
};

int i = 0, j, file, time_depature, time_arrival, rows = 0;
char temp_station[20], temp_destination[20];
FILE* times;
struct train *train_array;

        do
        {
            /*Take in four elements from the file and measure how many rows there are in the file from dynamic allocation*/
            file = fscanf(times, "%s %s %d %d", temp_station, temp_destination, &time_depature, &time_arrival);
            rows++;

        } while (file != EOF);

    /*Array of structs using dynamic allocation*/
    train_array = (struct train*)malloc(sizeof(struct train_array)*rows);

    do
    {
        /*Take in four elements from the file*/
        file = fscanf(times, "%s %s %d %d", temp_station, temp_destination, &time_depature, &time_arrival);
        /*makes sure does not go to the end of file*/
        if (file != EOF)
        {
            /*allocates the element in the array*/
            train_array[i] = (struct train*)malloc(sizeof(struct person_array));
            /*copies the string into the array*/
            strcpy(train_array[i].start, temp_station);
            i++;
        }
    } while (file != EOF);

    /*closes the file*/
    fclose(times);

【问题讨论】:

  • 在您的malloc 中,您将sizeof(struct train_array)rows 相乘。而之前的 5 行,你设置了rows = 0。由于零乘以任何东西仍然为零,因此您 malloc()'ing 零字节。我想,这可不好。
  • person_array 类型是什么?
  • 对不起,我没有在这里包含它,但 rows 是文件中行数的度量。它遍历文件并增加行数。
  • @rfernandes struct train *train_array;

标签: c arrays data-structures struct malloc


【解决方案1】:

您的代码存在许多问题,但让我们先暂时搁置一下,谈谈一般的内存管理。

分配内存的一个常见(也是我喜欢的)习惯用法类似于

T *p = malloc( sizeof *p * N ); 

T *p = NULL;
...
p = malloc( sizeof *p * N );

这将分配足够的空间来容纳 T 类型的 N 个元素。请注意,malloc 的结果没有强制转换;从 1989 年标准开始,不再需要强制转换1,并且在 C89 编译器下,如果您在范围内没有 malloc 的声明(即您忘记包括stdlib.h)。 C99 摆脱了隐含的 int 声明,所以这不再是一个大问题,但是很多编译器默认使用 1989 标准。

请注意,我还使用sizeof *p 来获取每个元素的大小; 表达式 *p 的类型为T,因此sizeof *p 等价于sizeof (T)2。这不仅减少了视觉上的混乱,还为您节省了 *p 类型不断变化的轻微维护头痛,例如从 intlongfloatdouble

C 不是你的妈妈,不会在你之后收拾东西;您有责任在完成分配后释放您分配的任何内存。

请注意,您可以使用 realloc 库函数调整动态分配的缓冲区大小。这是一个典型的用法:

#define INITIAL_SIZE ... // some initial value
...
size_t arraysize = INITIAL_SIZE;
size_t counter = 0;
T *array = malloc( sizeof *array * arraysize );
...
while ( we_need_to_store_more_data )
{
  if ( counter == arraysize )
  {
    /**
     * We've run out of room to store new things, so
     * we need to extend the array; one common
     * strategy is to double the size of the array
     * each time.
     */
    T *tmp = realloc( sizeof *array * (2 * arraysize) );
    if ( tmp ) 
    {
      array = tmp;
      arraysize *= 2;
    }
  }
  array[counter++] = new_value;
}

请注意,对于像这样的类型

struct foo
{
  char *name;
  char *address;
  int value;
};

您必须为namenumber 分配空间,与struct foo 本身的实例分开:

struct foo *farr = malloc( sizeof *farr * N );
...
farr[i].name    = malloc( sizeof *farr[i].name * name_length );
farr[i].address = malloc( sizeof *farr[i].address * address_length);

当您进入像这样的多个分配和释放步骤时,您必须非常小心;将这些操作抽象为它们自己的函数是一个非常好的主意,例如:

if ( !add_data( &foo[i], newName, newAddress, newValue ))
  // report error

看起来像

int add_data( struct foo *elem, const char *name, const char *addr, int val )
{
  assert( elem != NULL );
  elem->name = malloc( strlen( name ) + 1 );
  elem->address = malloc( strlen( addr ) + 1 );

  if ( !elem->name || !elem->address ) // Make sure memory allocation
  {                                    // succeeded before trying to
    free( elem->name );                // use name or address; malloc returns
    free( elem->address );             // NULL on failure, and free(NULL)
    return 0;                          // is a no-op.  We want to undo any
  }                                    // partial allocation before returning
                                       // an error.
  strcpy( elem->name, name );
  strcpy( elem->address, addr );
  elem->value = val;

  return 1;
}

现在让我们看看你代码中的具体问题:

train_array = (struct train*)malloc(sizeof(struct train_array)*rows);
                                           ^^^^^^^^^^^^^^^^^^^

类型的名称struct train;您尝试分配的对象的名称是train_array。你已经跨越了两者,这是一个问题。按照我上面的建议,将其重写为

train_array = malloc(sizeof *train_array * rows);

这一行

train_array[i] = (struct train*)malloc(sizeof(struct person_array));

有两个问题。你已经分配了train_array[i];您需要为每个 train_array[i] 中的 startdestination 元素分配空间。其次,您没有在代码中的任何地方定义person_array,这就是编译器抱怨不存在类型的原因。

你不需要循环文件两次;正如我上面演示的那样,您可以随时扩展您的数组。以下是如何重构代码的示例:

int done = 0;
...
size_t rows = 0;
size_t totalRows = 16;  // initial array size
train_array = malloc( sizeof *train_array * totalRows ); // initial allocation

while ( scanf( "%s %s %d %d", temp_station, 
                              temp_destination, 
                              &time_depature, 
                              &time_arrival) == 4 )
{
  if ( rows == totalRows )
  {
    struct train *tmp = realloc( sizeof *train_array * ( 2 * totalRows ));
    if ( tmp )
    {
      train_array = tmp;
      totalRows *= 2;
    }
    else
    {
      fprintf( stderr, "could not extend train_array past %zu elements\n", totalRows);
      break;
    }
  }
  train_array[rows].start_time = time_departure;
  train_array[rows].arrival_time = time_arrival;
  train_array[rows].start = malloc( strlen( temp_station ) + 1 );
  if ( train_array[rows].start )
    strcpy( train_array[rows].start, temp_station );

  train_array[rows].end = malloc( strlen( temp_destination ) + 1 );
  if ( train_array[rows].end )
    strcpy( train_array[rows].destination, temp_destination );
}

请注意,当您使用完此内存后,您将按如下方式释放它:

for ( size_t i = 0; i < totalRows; i++ )
{
  free( train_array[i].start );
  free( train_array[i].end );
}
free( train_array );


1。在 C 中,void * 类型的值可以分配给任何其他指针类型而无需强制转换。这在 C++ 中不是正确的;在 C++ 编译器下,需要强制转换,但如果你正在编写 C++,你应该使用 newdelete 而不是 mallocfree
2.sizeof是运算符,不是函数;如果操作数是 intfloat * 之类的类型名称,则只需使用括号。

【讨论】:

    【解决方案2】:
    1. 您应该在第二个 do {} while(); 之前调用 rewind(times); 以将文件位置设置为其开头。

    2. 这个

      train_array = (struct train*)malloc(sizeof(struct train_array)*rows);
      

      错了,因为你没有struct train_array,改成

      train_array = (struct train*)malloc(sizeof(*train_array)*rows);
      

      train_array = (struct train*)malloc(sizeof(struct train)*rows);
      
    3. 这个

      train_array[i] = (struct train*)malloc(sizeof(struct person_array));
      

      错了,因为train_array[i]struct train,你为什么要给它分配struct train *呢?此行应删除。

    【讨论】:

      【解决方案3】:

      第一个错误看起来是因为您在分配之前取消引用指向结构类型的指针。此外,您在该行上分配了一个“person_array”,但是将其类型转换为一个“train_array”指针,我认为这不是您想要做的。

      也许这就是你的本意?

      train_array[i].start = (char*)malloc(sizeof(struct person_array));
      

      第二个错误看起来你没有声明 person_array 结构。您发布的代码没有定义,所以如果您没有在其他地方定义它,那可能就是问题所在。

      同样,我不认为您的第一个 malloc 正在做您希望它做的事情。也许试试这个:

      train_array = (struct train*)malloc(sizeof(struct train)*rows);
      

      "train_array" 是一个指针,所以它只会是 size_t 个字节。我认为“struct train”是您的数据类型,它具有您要分配的字节数。

      【讨论】:

        猜你喜欢
        • 2021-10-30
        • 2017-03-30
        • 2019-07-19
        • 2020-03-06
        • 2012-03-25
        • 2011-12-13
        相关资源
        最近更新 更多