您的代码存在许多问题,但让我们先暂时搁置一下,谈谈一般的内存管理。
分配内存的一个常见(也是我喜欢的)习惯用法类似于
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 类型不断变化的轻微维护头痛,例如从 int 到 long 或 float 到 double。
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;
};
您必须为name 和number 分配空间,与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] 中的 start 和 destination 元素分配空间。其次,您没有在代码中的任何地方定义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++,你应该使用
new 和
delete 而不是
malloc 和
free。
2.
sizeof是运算符,不是函数;如果操作数是
int 或
float * 之类的类型名称,则只需使用括号。