C中嵌套结构,指针寻址问题。

问题描述 投票:0回答:1

我有一个编程练习,我要做两个结构。Star 和... Cluster. Star 有四个参数--名称,第1个数字,第2个数字,第3个数字。Cluster 包含n颗星星,它们在文件 星星.数据 以及星号所在的文件名信息。然后写一个加载函数,它将文件作为参数,并返回一个指向一个文件名的指针。Cluster 从文件中读取的结构。我的代码是这样的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME 100

struct Star {
    char name[MAX_NAME];
    double recta;
    double decl;
    double mag;
};

struct Cluster {
    struct Star *ptr;
    char *file;
};

struct Cluster *load(char *plik);

int main() {
    load("stars.dat");
    return 0;
}

struct Cluster *load(char *plik) {
    char line[MAX_NAME];
    int i = 0;
    struct Star *star_ptr;

    FILE *file = fopen(plik, "r");
    struct Cluster *cluster_ptr;
    if (!plik) {
        perror(plik);
        exit(-1);
    } else {
        while (fgets(line, MAX_NAME, file) != NULL) {
            star_ptr = malloc(sizeof(struct Star));
            sscanf(line, "%s %lf %lf %lf", star_ptr->name, &star_ptr->recta, &star_ptr->decl, &star_ptr->mag);

            printf("%s \n", star_ptr->name);
            star_ptr++;
            i++;
        }
        cluster_ptr = (struct Cluster *)malloc(sizeof(struct Star) * i);
        cluster_ptr->ptr = star_ptr;
        cluster_ptr->file = plik;
        // printf("%s \n", cluster_ptr->star_ptr[0].name);
        printf("%s \n", star_ptr[0].name);
    }
    fclose(file);
    return cluster_ptr;
}

我的问题是,这样做是否正确?我怎样才能检查返回的指针是否正确(我不知道怎样才能检查出返回的指针是否正确 printf 例如列表中第1颗星的名称)。) 如果有人能给我一个提示,我将感激不尽。

c struct malloc
1个回答
1
投票

你的处理 struct Cluster 成员变量 struct Star *ptr 没有意义。

如果你想 struct Cluster 能够 "容纳 "多个 struct Star例如,你可以做以下工作之一。

  1. 使 struct Cluster 包含一个指向动态分配的 链表 数据类型的 struct Star.
  2. 使 struct Cluster 包含一个数组,或者有一个成员指针指向一个动态分配的类型为 struct Star. 为了跟踪这个数组中元素的数量,你还需要一个单独的成员变量。
  3. 使 struct Cluster 包含一个数组,或者有一个成员指针指向一个动态分配的类型为 struct Star*,其中每个元素都是指向自己动态分配的 struct Star. 为了跟踪这个数组中元素的数量,你可以用一个单独的变量来指定元素的数量,或者你可以用一个特殊的值来标记数组的结尾,例如一个 NULL 指针。

你的代码没有做这两个选项。取而代之的是,你做了下面的事情。

对于在文件中找到的每一颗星星 你都会动态地分配足够的内存给一个单独的 struct Star. 但是没有记住这个内存的地址。struct Star在下一个循环的迭代中,你用下一个内存地址覆盖指向这个内存地址的指针。struct Star,这样你就不再知道第一个的地址。struct Star. 这是一个 内存泄漏. 当循环的最后一次迭代完成后,你可以让集群的 ptr 成员点 struct Star 因此,它实际上只指向一颗星。它不可能指向更多的星星,因为正如我已经指出的,你没有记住它们的内存位置。

因此,为了解决你的问题,我建议你决定使用上面列出的选项中的哪一个,并据此编写你的代码。如果你事先不知道星团中的恒星数量,我建议你先尝试选项#1(链接的列表)。

另外,下面一行包含一个错误。

if (!plik)

你应该把它改成下面这行。

if (!file)

或者改成这样,我个人认为这样更易读。

if ( file == NULL )

EDIT: 因为你在评论区说你想要一个链接列表的实现例子,所以我对你的代码做了相应的修改,并发布在下面。

#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;

这样集群结构就会存储它自己的字符串副本。这样做的好处是,你也可以将寿命有限的字符串(比如本地的char数组)传递给函数,而不会使指针成为一个 悬针.

© www.soinside.com 2019 - 2024. All rights reserved.