Почему происходит сбой этой программы на C? - PullRequest
2 голосов
/ 07 октября 2010

Я размышлял об этом в течение по крайней мере часа и все еще не могу выяснить, в чем проблема.

#include <stdio.h>

typedef struct
{
    int Level;
    char* Name;
} Base;

Base baseStruct;

int main(int argc, char *argv[])
{
    scanf("%s", baseStruct.Name);
    scanf("%d", &baseStruct.Level);
    printf("%s :: Level %d\n", baseStruct.Name, baseStruct.Level);
    return 0;
}

Что происходит, я иду и ввожу строку «Имя»,затем, когда я набираю и ввожу целое число, программа вылетает.Что происходит?

Ответы [ 5 ]

12 голосов
/ 07 октября 2010
scanf("%s", ...)

Это предполагает, что буфер (scanf должен записать в это), и вы даете ему неинициализированный указатель, который может указывать куда угодно.следующее:

  1. Вместо * сделать Name буфер символов:

    typedef struct
    {
        int Level;
        char Name[100];
    } Base;
    
  2. Инициализировать его из кучи:

    baseStruct.Name = malloc(100); /* do not forget to cleanup with `free()` */
    

Вы также должны указать максимальную длину строки в формате scanf для предотвращения переполнения:

/* assume 'Name' is a buffer 100 characters long */
scanf("%99s", baseStruct.Name);
1 голос
/ 07 октября 2010

Не расстраивайся, все делают эту ошибку.Char * обозначает «указатель на символ», но память для самой строки не выделяется.

Add:

baseStruct.Name = malloc (sizeof (char) * 100);

(обратите внимание, мой синтаксис может быть немного выключен)

1 голос
/ 07 октября 2010

Имя - это просто неинициализированный указатель на строку. Это не указывает на что-нибудь полезное. Вам нужно будет правильно инициализировать его в строковом буфере. Кроме того, вы можете ограничить строку с помощью форматирования (например,% 100), чтобы убедиться, что вы не переполняете свой буфер.

0 голосов
/ 07 октября 2010

Как уже отмечали другие, baseStruct.Name не указывает на допустимую область памяти.Однако выделение буфера фиксированного размера не является более безопасным.Для учебного упражнения используйте

typedef struct
{
    int Level;
    char Name[1];
} Base;

и введите длинные строки для проверки влияния переполнения буфера.

Для безопасной обработки ввода неопределенной длины используйте fgets и sscanf илиstrtol (или strtoul, если Base.Level не может быть отрицательным.

Вот пример:

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

#define INITIAL_BUFSIZE 100
#define MAX_BUFSIZE 30000

char *myreadline(FILE *fin) {
    char *buffer;
    int offset = 0;
    int bufsize = INITIAL_BUFSIZE;
    buffer = malloc(bufsize);

    if ( !buffer ) {
        return NULL;
    }

    while ( fgets(buffer + offset, bufsize, fin) ) {
        size_t last = strlen(buffer) - 1;
        if ( buffer[last] == (char) '\n' ) {
            buffer[last] = 0;
            break;
        }
        else {
            char *tmp;
            offset += bufsize - 1;
            bufsize *= 2;
            if ( bufsize > MAX_BUFSIZE ) {
                break;
            }
            tmp = realloc(buffer, bufsize);
            if ( !tmp ) {
                break;
            }
            else {
                buffer = tmp;
            }
        }
    }
    return buffer;
}

int myreadint(FILE *fin, int *i) {
    long x;
    char *endp;
    char *line = myreadline(fin);

    if ( !line ) {
        return 0;
    }

    x = strtol(line, &endp, 10);

    if ( (!*endp || isspace((unsigned char) *endp) )
            && (x >= INT_MIN) && (x <= INT_MAX ) ) {
        *i = (int) x;
        free(line);
        return 1;
    }

    return 0;
}

typedef struct base_struct {
    int Level;
    char* Name;
} Base;

int main(int argc, char *argv[]) {
    Base bs;
    int i;

    puts("Enter name:");
    bs.Name = myreadline(stdin);
    if ( !bs.Name ) {
        fputs("Cannot read Name", stderr);
        return EXIT_FAILURE;
    }

    puts("Enter level:");
    if ( myreadint(stdin, &i) ) {
        bs.Level = i;
        printf("Name: %s\nLevel: %d\n", bs.Name, bs.Level);
        free(bs.Name);
    }
    else {
        fputs("Cannot read Level", stderr);
        return EXIT_FAILURE;
    }


    return 0;
}

Выход:

C:\Temp> t
Enter name:
A dark and mysterious dungeon
Enter level:
3456772
Name: A dark and mysterious dungeon
Level: 3456772
0 голосов
/ 07 октября 2010

Вы не выделили хранилища для Base.Name.Вы сканируете строку в указатель, который не указывает ни на какое хранилище.

Выделите некоторое место для строки.Проблема в том, что вы не знаете, насколько большая строка будет скопирована при использовании scanf.Предположим, вы malloc 256 байтов, а затем scanf загружает 300-байтовую строку?Либо выделите строку, достаточно большую для обработки всех возможных результатов от scanf, или измените ваш scanf, чтобы ограничить количество символов, например:

baseStruct.Name = malloc(sizeof(char) * 256);
scanf("%256s", baseStruct.Name);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...