C语言中如何让start指针始终指向动态分配内存的首个Song结构体?
让我来帮你排查并修复问题,你的代码里有几个关键的指针类型不匹配和逻辑错误,直接导致了start指针读取到垃圾值。我们一步步来解决:
核心问题:函数参数类型不兼容
你在main里调用addSong(&ptr, &nrOfSongs)和showList(&start, nrOfSongs, &ptr)时,传递的是指针的地址(双重指针),但函数声明和定义里却用了单指针参数。这种类型不兼容会让编译器做隐式转换,最终导致程序访问错误的内存地址,出现垃圾值。
步骤1:修正函数声明和参数
首先修改FuncDek.h里的函数声明,匹配实际传递的参数类型:
#ifndef FUNCDEK #define FUNCDEK #include <stdio.h> #include <stdlib.h> #include <string.h> // 新增,用于字符串处理函数 typedef struct { char title[30]; char artist[30]; unsigned int year; } Song; int menu(); void addSong(Song **ptr, int *nrOfSongs); // 改为双重指针 void showList(Song *start, int nrOfSongs); // 移除多余的ptr参数 #endif
步骤2:修复addSong函数的指针操作与赋值逻辑
原来的addSong里,ptr是值传递,修改的只是局部副本,不会影响main里的ptr指针。改成双重指针后,我们需要解引用操作实际的指针;另外手动循环复制字符串的方式容易引入垃圾值,改用字符串处理函数更安全:
#include "FuncDek.h" #pragma warning(disable: 4996) int menu() { fflush(stdin); int choice = 0; printf("Menyval på låtlista\n"); printf("-------------------------\n"); printf("1. Lägg till låt \n"); printf("2. Visa låtlista \n"); printf("3. Blanda låtlistan \n"); printf("4. Spara till fil \n"); printf("0. Avsluta program \n"); scanf("%d", &choice); getchar(); return choice; } void addSong(Song **ptr, int *nrOfSongs) { system("cls"); Song temp; // 清除scanf后残留的换行符,避免fgets直接读取空行 int c; while ((c = getchar()) != '\n' && c != EOF); printf("Ange låtens namn:\n"); fgets(temp.title, sizeof(temp.title), stdin); // 去掉fgets读入的换行符,避免输出时出现多余空行 temp.title[strcspn(temp.title, "\n")] = '\0'; printf("Ange artistens namn:\n"); fgets(temp.artist, sizeof(temp.artist), stdin); temp.artist[strcspn(temp.artist, "\n")] = '\0'; printf("Ange året låtens släpptes: \n"); scanf("%d", &temp.year); // 获取当前要写入的歌曲位置 Song *current = *ptr; // 安全复制字符串和数据 strcpy(current->title, temp.title); strcpy(current->artist, temp.artist); current->year = temp.year; // 移动main里的ptr指针到下一个空位置(注意括号优先级) (*ptr)++; (*nrOfSongs)++; }
步骤3:修复showList函数的遍历逻辑
原来的循环只执行1次,而且参数传递错误。现在start已经是内存块的首地址,直接遍历所有已添加的歌曲即可:
void showList(Song *start, int nrOfSongs) { system("cls"); printf("Song list\n"); printf("-----------------------------\n"); Song *current = start; for (int i = 0; i < nrOfSongs; i++) { printf("Låt: %s \n", current->title); printf("Artist: %s \n", current->artist); printf("År: %d \n", current->year); printf("-----------------------------\n"); current++; } // 空列表提示 if (nrOfSongs == 0) { printf("Inga låtar tillagda än.\n"); } system("pause"); }
步骤4:修正main里的函数调用
更新main里的addSong和showList调用,匹配新的参数:
#include "FuncDek.h" #include <locale.h> #include <crtdbg.h> int main() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); setlocale(LC_ALL, "swedish"); // 分配5个Song的内存 Song *ptr = (Song *)malloc(sizeof(Song) * 5); // start指向内存块的首地址,永远不变 Song *start = &(ptr[0]); int menuChoice = 0; int nrOfSongs = 0; do { system("cls"); menuChoice = menu(); switch (menuChoice) { case 1: // 检查是否超过内存容量(可选,避免越界) if (nrOfSongs >=5) { printf("Låtlista är full, kan inte lägga till fler låtar!\n"); system("pause"); break; } addSong(&ptr, &nrOfSongs); break; case 2: showList(start, nrOfSongs); break; default: printf("\nFelaktigt val, försök igen\n"); system("pause"); break; } } while (menuChoice != 0); // 释放内存,必须用首地址start free(start); system("pause"); return 0; }
关键说明:start指针的正确性
你初始化Song *start = &(ptr[0]);的方式是完全正确的——ptr一开始指向malloc分配的内存首地址,ptr[0]就是第一个Song结构体,取地址后start会一直指向这个首地址,不会随ptr的移动而改变,完美用于遍历整个列表。
内容的提问来源于stack exchange,提问作者anderssinho
相关产品推荐
相关产品推荐

