Ваша обработка переменной-члена struct Cluster
struct Star *ptr
не имеет смысла.
Если вы хотите, чтобы struct Cluster
мог "содержать" несколько struct Star
, то вы могли бы, например, сделать одно из следующих:
- Сделать
struct Cluster
содержать указатель, который указывает на первый элемент динамически выделяемого связанного списка типа данных struct Star
. - Сделать
struct Cluster
содержать массив или иметь указатель на член, который указывает на динамически выделяемый массив элементов типа struct Star
. Чтобы отслеживать количество элементов в этом массиве, вам дополнительно потребуется отдельная переменная-член. - Сделать
struct Cluster
содержать массив или иметь указатель элемента, который указывает на динамически выделяемый массив элементов типа struct Star*
, где каждый элемент является указателем на свой динамически выделяемый struct Star
. Чтобы отслеживать количество элементов в этом массиве, вы можете либо иметь отдельную переменную, которая определяет количество элементов, либо вы можете отметить конец массива специальным значением, например указателем NULL
.
Ваш код не поддерживает ни одну из этих опций. Вместо этого вы делаете следующее:
Для каждой звезды, которую вы найдете в файле, вы динамически выделяете достаточно памяти для одной struct Star
. Но вместо того, чтобы запоминать адрес памяти этого struct Star
, в следующей итерации l oop вы перезаписываете указатель на этот адрес памяти адресом памяти следующего struct Star
, так что вы больше не знаете адрес первого struct Star
. Это утечка памяти . По окончании последней итерации l oop вы затем заставляете ptr
член кластера указывать на последний struct Star
, который был выделен, так что он фактически указывает только на одну звезду. Он не может указывать на большее количество звезд, потому что, как я уже указывал, вы не запомнили их расположение в памяти.
Поэтому, чтобы решить вашу проблему, я рекомендую вам решить, какой из перечисленных вариантов выше, вы хотите использовать и написать свой код соответственно. Если вы не знаете заранее количество звезд в скоплении, я рекомендую вам сначала попробовать вариант № 1 (связанный список).
Кроме того, следующая строка содержит ошибку:
if (!plik)
Вы должны изменить его на следующее:
if (!file)
Или, может быть, на это, которое я лично считаю более читаемым:
if ( file == NULL )
РЕДАКТИРОВАТЬ: поскольку в разделе комментариев вы указали, что вам нужен пример реализации связанного списка, я соответствующим образом изменил ваш код и разместил его ниже:
#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;
, так что структура кластера хранит свою собственную копию строки. Это имеет то преимущество, что вы также можете передавать в функцию строки с ограниченным временем жизни (например, локальные массивы символов) без превращения указателя в висячий указатель .