簡體   English   中英

為什么當我在學期變量中輸入正確的值時,我的程序會崩潰? C

[英]why when i put a proper value in semester variable my program crashes? C

struct student{
    char *number;
    char *name;
    int semester;
    float *grades;
};

typedef struct student RECORD;

void read_record(RECORD *p);
void print_record(RECORD x);
void init_record(RECORD *p);
void free_record(RECORD x);

main()
{
    int N;
    printf("Please enter the number of students that you want to insert: ");
    scanf("%d",&N);

    RECORD array[N];
    int i;

    for (i=0; i<N; i++)
        init_record(&array[i]);

此時,當我從鍵盤輸入學期值時,我的程序崩潰了

for (i=0; i<N; i++)
{
    printf("Number %d student: \n",i+1);
    read_record(&array[i]);     
}


for (i=0; i<N; i++)
print_record(array[i]);


    for (i=0; i<N; i++)
    {
        free_record(array[i]);
    }   
}

我試圖刪除任何不必要的代碼,但我留下了最重要的部分,因此任何錯誤的部分都更容易被看到

void read_record(RECORD *p)
{
        int i;

    printf("Please give the name: ");
    scanf("%s", p->name);

    printf("Please give AM number: ");
    scanf("%s", p->number);

    printf("Please give semester: ");
    scanf("%d",p->semester);

    printf("Please give grades: ");
    for(i=0;i<5;i++)
    scanf("%f", &(p->grades[i]));
}

在這段代碼之后,我有init_record函數,其中包括結構數組的 malloc

void init_record(RECORD *p)
{

    p->name = malloc(sizeof(char)*40);
    if (!p->name)
    {
        printf("Error!");
        exit(0);
    }

    p->number = malloc(sizeof(char)*6);
    if (!p->number)
    {
        printf("Error!");
        exit(0);
    }

    p->grades = malloc(sizeof(float)*5);
    if (!p->grades)
    {
        printf("Error!");
        exit(0);
    }
}

假設您已經為結構成員分配了內存

printf("Please give semester: ");
scanf("%d",p->semester);

是問題所在,因為p->semester是一個int %d需要一個指向int的指針,您將一個未初始化的整數值作為指向scanf的指針傳遞。

正確的調用是

scanf("%d", &p->semester);

通常一次使用malloc是因為需要存在於函數范圍之外的對象,例如鏈表、樹等。但是當您需要一個在編譯器時維度未知的數組時也是如此。

您的init_record在我看來毫無意義,因為您知道數組的大小,而且它們甚至很小,您可以輕松地將結構更改為

struct student {
    char number[6];
    char name[40];
    int semester;
    float grades[5];
};

然后你就不需要做malloc調用了。

此外,您需要像這樣執行scanf

國際 c;

scanf("%39s", p->name);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer

scanf("%5s", p->number);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer

...

如果用戶輸入的名稱/號碼對於緩沖區來說太大,則可以防止緩沖區溢出。


編輯

OP 在評論中詢問

最后一兩件事:我想替換scanf函數與gets的名字的情況下,因為我想一個名字和姓氏保存為一個整體(包括空格)。 但是當我替換 scanf 時,我收到了這個錯誤:在不是結構或聯合的東西中請求成員“名稱”。 我在這里搜索了類似的問題,但沒有找到任何解決方案。 你能推薦一個嗎?

不要使用gets ,這是一個危險的函數,因為它沒有考慮數組的大小,如果輸入的文本大於緩沖區大小,它會溢出並造成很大的損壞。 錯誤消息是由於語法錯誤造成的,因為您沒有顯示代碼,我無法說出您做錯了什么。 但是,如果您接受我的建議,就不會出現此錯誤。

一般來說,我建議不要使用scanf來讀取用戶的信息,因為scanf不是為了這樣做而設計的。 特別是當你想讀取有空格的字符串時,最好用fgets讀取整行,然后使用其他函數解析該行,如strchrstrstrstrtoksscanfstrtol等。使用哪個函數取決於你正在嘗試從用戶那里讀取。 在這種情況下,您正在讀取字符串, fgets會給出更好的結果。 所以我會把你的整個閱讀過程改成這樣:

int read_record(RECORD *p)
{
    char line[1024];

    printf("Please give the name: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read name\n");
        return 0; // error
    }

    line[strcspn(line, "\n")] = 0; // removing newline
    strncpy(p->name, line, sizeof p->name);
    p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string

    printf("Please give AM number: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read AM number\n");
        return 0;
    }

    line[strcspn(line, "\n")] = 0; // removing newline
    strncpy(p->number, line, sizeof p->name);
    p->name[sizeof(p->number) - 1] = 0; // making sure to get a valid string

    // this is ok, this can stay like this
    printf("Please give semester: ");
    scanf("%d", &p->semester);

    printf("Please give grades: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read grades\n");
        return 0;
    }

    if(sscanf(line, "%f %f %f %f %f",
                p->grades, p->grades + 1, p->grades + 2, p->grades + 3,
                p->grades + 4) != 5)
    {
        fprintf(stderr, "Could not read 5 grades\n");
        return 0;
    }

    return 1; // success
}

我知道您以前擁有的代碼要多得多,但此代碼更健壯,它可以處理用戶輸入錯誤格式的情況,您的代碼可以對此做出反應,打印錯誤消息,重試用戶輸入,等等。 當屏幕或文件上打印的數據很奇怪時,錯誤就會顯現出來。

請注意我是如何復制字符串的:

strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string

在這里,我假設您已經更改了結構以保存數組,就像我在答案的第一部分中所說的那樣。 如果您沒有更改它並且您仍然通過使用硬編碼固定大小的malloc使用舊方法,則您也必須在此處使用硬編碼固定大小:

strncpy(p->name, line, 40);
p->name[39] = 0; // making sure to get a valid string

並且您明白為什么我更喜歡結構具有數組時的原因,因為使用sizeof我可以獲得大小而不管維度如何。

這里要注意的另一件事是我使用了strncpy而不是strcpy strcpy遇到與gets相同的問題,它沒有考慮目標緩沖區的大小,如果源字符串大於目標緩沖區,它將溢出緩沖區。

strncpy工作方式與strcpy類似,只是您傳遞了目標緩沖區可用的字節數。 如果源大於該數字,則strncpy不會在目標中寫入字節,從而防止緩沖區溢出。 當然,如果'\\0'終止字節不在復制的字節中,則不會寫入目標緩沖區。 p->name[39] = 0; 只需確保字符串以'\\0'結尾,無論源有多長。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM