【问题标题】:I'm using array of structures inside a structure and my program behaves wierdly我在结构中使用结构数组,我的程序表现得很奇怪
【发布时间】:2022-01-17 00:12:04
【问题描述】:

我正在尝试在 C 程序中创建一个小的播放列表管理器。我设法创建了这段代码。代码的重点是在结构listOfSongs 内部创建结构track_ 的数组,并用循环填充它。当歌曲数小于 6 时,我会遇到分段失败。但是如果歌曲数

struct track_t
{ 
    char *nazev;
    char *autor;
    char *album;
    int orderInAlbum;
};

struct listOfSongs
{     
    struct track_t **track;
}; 


int main(int argc, char *argv[])
{
    (void) argc;
    (void) argv;
    printf("Write count of songs:\n");
    int countOfSongs;
    scanf("%d", &countOfSongs);
    struct listOfSongs *sez;
    sez = malloc(sizeof(* sez));
    sez->track = malloc(sizeof(struct track_t *) * countOfSongs);
    for(int i = 0; i<countOfSongs; i++)
    {
        sez->track[i] = malloc(sizeof(struct track_t));
    }
    for(int i = 0; i<countOfSongs; i++)
    {
        printf("%d\n", i);
        scanf("%s", sez->track[i]->nazev);
        printf("%s\n", sez->track[i]->nazev);
    }
    return 0;
}

【问题讨论】:

  • 无关:您可以使用void 使main 不带任何参数,就像任何其他函数一样。
  • 提示:你在哪里为struct track_t中的字符数组分配空间?
  • 也不相关:您不需要将sez 设为指针或动态分配该结构。您也不需要 track 成员是指针数组,而是结构对象数组 (struct track_t *track;)。您拥有的指针越少,它们中的任何一个出错或引起问题的可能性就越小。
  • -fsanitize=address 擅长调试内存问题。
  • 建议考虑 struct 的数组,而不是struct成员数组。在您的代码中,char *nazev;(和其他成员)在使用前需要内存。

标签: c struct malloc dynamic-memory-allocation undefined-behavior


【解决方案1】:

"...试图创建小播放列表...,...代码的重点是在结构 listOfSongs 中创建结构轨道数组,并用循环填充它..."_

当您尝试访问程序不拥有的内存区域时会导致分段错误。因为char *nazev;(和其他成员)是指针,所以下面的表达式将调用未定义的行为:

scanf("%s", sez->track[i]->nazev); //attempting to scan value into memory not owned by process. 

使用前需要内存。结构中的类似成员也是如此。

简化结构的一个建议是将指针成员替换为具有合理长度的char 数组,以容纳每个成员用于的数据。

但是,由于您声明的意图是创建播放列表,另一个建议可能是使用更适合包含 播放列表 的列表结构。可以做到这一点的 C 构造通常称为linked list

以下是链接列表形式的示例,它允许您的程序将播放列表中每个新项目的信息输入新的节点。每个新节点都通过指向下一个节点和前一个节点的指针连接,从而允许用户向前和向后遍历列表。

链表方法示例:

typedef struct track_s { //this struct contains the type information
    char nazev[80];      //intended to be stored in the list
    char autor[80];      //it can easily be exande to have more members
    char album[80];
    int orderInAlbum;
}track_s;

track_s sample_input[4] = {{"song 1 title", "name of author 1", "name of album 1", 1},
                           {"song 2 title", "name of author 2", "name of album 2", 2},
                           {"song 3 title", "name of author 3", "name of album 3", 3},
                           {"song 4 title", "name of author 4", "name of album 4", 4}
                        };

typedef struct track {     
    track_s track; //payload containing information to be added to list
    struct track *prev;//pointers to next and previous nodes
    struct track *next;
}list_tracts_s; 

//prototypes
void Insert(list_tracts_s** head, track_s *new); //insert
void deleteRecord(list_tracts_s** head_ref, list_tracts_s* del); //free

int main(int argc, char *argv[])
{
    list_tracts_s *head = NULL;
    //populate list of nodes to contain sample input
    Insert(&head, &sample_input[0]);//insert one record into one node
    Insert(&head, &sample_input[1]);
    Insert(&head, &sample_input[2]);
    Insert(&head, &sample_input[3]);
    //use list in program
    //free when done (call once for each Insert
    deleteRecord(&head, head);
    deleteRecord(&head, head);
    deleteRecord(&head, head);
    deleteRecord(&head, head);
    
    return 0;
}

void Insert(list_tracts_s** head, track_s *new)
{
    /* 1. allocate node */
    list_tracts_s *new_node = malloc(sizeof(*new_node));
    if(new_node)
    {        
        /* 2. assign input data  */
        strcpy(new_node->track.nazev , new->nazev);
        strcpy(new_node->track.autor , new->autor);
        strcpy(new_node->track.album , new->album);
        new_node->track.orderInAlbum = new->orderInAlbum;
    
    
        /* 3. Make next of new node as head and previous as NULL */
        new_node->next = (*head);
        new_node->prev = NULL;
     
        /* 4. change prev of head node to new node */
        if ((*head) != NULL)
            (*head)->prev = new_node;
     
        /* 5. move the head to point to the new node */
        (*head) = new_node;
    }
}

void deleteRecord(list_tracts_s** head_ref, list_tracts_s* del)
{
    /* base case */
    if (*head_ref == NULL || del == NULL)
        return;
 
    /* If node to be deleted is head node */
    if (*head_ref == del)
        *head_ref = del->next;
 
    /* Change next only if node to be deleted is NOT the last node */
    if (del->next != NULL)
        del->next->prev = del->prev;
 
    /* Change prev only if node to be deleted is NOT the first node */
    if (del->prev != NULL)
        del->prev->next = del->next;
 
    /* Finally, free the memory occupied by del*/
    free(del);
    return;
}

【讨论】:

    【解决方案2】:

    结构的元素(一个数据成员除外)

    struct track_t
    { 
        char *nazev;
        char *autor;
        char *album;
        int orderInAlbum;
    };
    

    具有指针类型char *。它们没有被初始化并且具有不确定的值。

    因此这个 for 循环

    for(int i = 0; i<countOfSongs; i++)
    {
        printf("%d\n", i);
        scanf("%s", sez->track[i]->nazev);
        printf("%s\n", sez->track[i]->nazev);
    }
    

    调用未定义的行为。

    您需要重新声明将字符数组声明为数据成员的结构,或者您需要动态分配字符数组地址,其地址将分配给指针。

    请注意,将 main 的参数封装为 void 类型以避免有关未使用变量的警告

    int main(int argc, char *argv[])
    {
        (void) argc;
        (void) argv;
        //...
    

    你可以像 main 这样声明函数

    int main( void )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-17
      • 2015-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多