您对struct Cluster 成员变量struct Star *ptr 的处理没有意义。
如果您希望 struct Cluster 能够“包含”多个 struct Star,那么您可以例如执行以下操作之一:
- 使
struct Cluster 包含一个指针,该指针指向数据类型为struct Star 的动态分配的linked list 的第一个元素。
- 使
struct Cluster 包含一个数组或具有一个指向动态分配的struct Star 类型元素数组的成员指针。为了跟踪该数组中的元素数量,您还需要一个单独的成员变量。
- 使
struct Cluster 包含一个数组或具有一个指向动态分配的struct Star* 类型元素数组的成员指针,其中每个元素都是指向其自己的动态分配struct Star 的指针。为了跟踪此数组中的元素数量,您可以使用一个单独的变量来指定元素的数量,或者您可以使用特殊值标记数组的末尾,例如 NULL 指针。
您的代码不执行这些选项。相反,您可以执行以下操作:
对于您在文件中找到的每一颗星,您都会为单个struct Star 动态分配足够的内存。但不是记住这个struct Star的内存地址,而是在下一次循环迭代中,用下一个struct Star的内存地址覆盖指向这个内存地址的指针,这样你就不再知道这个内存地址的地址了第一个struct Star。这是memory leak。在循环的最后一次迭代完成后,您将集群的ptr 成员指向最后分配的struct Star,这样它实际上只指向一颗星。它不能指向更多的星星,因为正如我已经指出的那样,你不记得它们的记忆位置。
因此,为了解决您的问题,我建议您决定要使用上面列出的哪些选项并相应地编写代码。如果您事先不知道星团中的恒星数量,我建议您先尝试选项#1(链表)。
此外,以下行包含一个错误:
if (!plik)
您应该将其更改为以下内容:
if (!file)
或者可能是这个,我个人认为这更易读:
if ( file == NULL )
编辑:因为您在 cmets 部分中声明您想要一个链接列表实现的示例,所以我已经相应地修改了您的代码并将其发布在下面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
//Do not change the following line without also changing MAX_NAME_MINUS_ONE_STRING
#define MAX_NAME 100
#define MAX_NAME_MINUS_ONE_STRING "99"
//the line above must have the value MAX_NAME - 1 and be enclosed in quotation
//marks, otherwise you risk a buffer overflow in the sscanf function call!
struct StarNode
{
//this points to the next node of the linked list
struct StarNode *next;
//I changed this struct member to a pointer to a dynamically
//allocated string, because having a char array of 100 bytes
//was a waste of memory
char *name;
double recta;
double decl;
double mag;
};
struct Cluster
{
//points to the head of the linked list of stars
struct StarNode *p_head;
//points to its own dynamically allocated copy of the filename
char *filename;
};
struct Cluster* load_cluster( const char* filename );
void cleanup_cluster( struct Cluster *p_cluster );
int main()
{
struct Cluster *cluster_ptr;
cluster_ptr = load_cluster( "stars.dat" );
cleanup_cluster( cluster_ptr );
return 0;
}
struct Cluster* load_cluster( const char *filename )
{
char line[MAX_NAME];
//pp_next will always point to the address of the struct StarNode * where
//the address of the next node should be written
struct StarNode *p_newstar, **pp_next;
struct Cluster *p_cluster;
FILE *file = fopen( filename, "r" );
assert( file != NULL );
p_cluster = (struct Cluster*)malloc( sizeof( struct Cluster ) );
assert( p_cluster != NULL );
pp_next = &p_cluster->p_head;
while ( fgets( line, MAX_NAME, file ) != NULL )
{
char starname[MAX_NAME];
int i;
p_newstar = (struct StarNode *)malloc( sizeof( struct StarNode ) );
assert( p_newstar != NULL );
//I changed the %s to %99s (assuming MAX_NAME == 100) to prevent buffer overflow.
//The value must be one less in order to have space for the null terminator.
//Also, I now check the return value of sscanf.
i = sscanf( line, "%" MAX_NAME_MINUS_ONE_STRING "s %lf %lf %lf", starname, &p_newstar->recta, &p_newstar->decl, &p_newstar->mag );
assert( i == 4 );
printf( "Adding %s\n", starname );
//allocate memory for star's own copy of starname and copy it
p_newstar->name = (char*)malloc( strlen( starname ) + 1 /*for null terminator character*/ );
assert( p_newstar->name != NULL );
strcpy( p_newstar->name, starname );
//link the new star node to the linked list
*pp_next = p_newstar;
//update pp_next to the address of the pointer where the address of the next node should be written to
pp_next = &p_newstar->next;
}
//the last element of the linked list must have a NULL pointer
*pp_next = NULL;
//allocate sufficient memory for filename and copy the string
p_cluster->filename = (char*)malloc( strlen( filename ) + 1 /*for null terminator character*/ );
assert( p_cluster->filename != NULL );
strcpy( p_cluster->filename, filename );
fclose( file );
return p_cluster;
}
void cleanup_cluster( struct Cluster *p_cluster )
{
struct StarNode *p;
p = p_cluster->p_head;
//cleanup every star node individually
while ( p != NULL )
{
struct StarNode *temp;
printf( "Deleting %s\n", p->name );
free( p->name );
temp = p;
p = p->next;
//free must be called last, because the contents of the current node become invalid
//once free is called, which means that also the pointer to the next node would
//become invalid
free( temp );
}
free( p_cluster->filename );
free( p_cluster );
}
在上面的代码中,我还创建了一个函数cleanup_cluster,以释放集群分配的内存。
请注意我也改了行
cluster_ptr->file = plik;
以便集群结构存储它自己的字符串副本。这样做的好处是,您还可以将生命周期有限的字符串(例如本地字符数组)传递给函数,而不会将指针变为dangling pointer。