简体   繁体   中英

Why does this C program crash?

I've mulled over this for at least an hour and I still can't find out what the problem is.

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

What happens is, I go and enter the "Name" string, then when I type and enter the integer the program crashes. What is going on?

scanf("%s", ...)

This expects a buffer ( scanf needs to write into it) and you give it an uninitialized pointer, that could point anywhere.

Consider doing one of the following:

  1. Make Name a character buffer instead:

     typedef struct { int Level; char Name[100]; } Base; 
  2. Initialize it from the heap:

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

You should also specify max string length in scanf format string to prevent overflow:

/* assume 'Name' is a buffer 100 characters long */
scanf("%99s", baseStruct.Name);

Name is just an uninitialized pointer to a string. It doesn't point to anything useful. You'll need to initialize it properly to a string buffer. Also, you may want to limit the string through the formatting (like %100s) to make sure you don't overrun your buffer.

Don't feel bad everyone makes that mistake. The char * stands for a "pointer to a character" but the memory for the string itself is not allocated.

Add:

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

(note my syntax may be off a little)

You haven't allocated any storage for Base.Name. You're scanning a string into aa pointer that doesn't point to any storage.

Allocate some space for the string. The problem is that you don't know how big a string will be copied in using scanf. Suppose you malloc 256 bytes and then scanf loads in a 300 byte string? Either allocate a string sufficiently large to handle all possible results from scanf, or modify your scanf to limit the characters, like:

baseStruct.Name = malloc(sizeof(char) * 256);
scanf("%256s", baseStruct.Name);

As others have pointed out, baseStruct.Name does not point to a valid memory region. However, allocating a fixed sized buffer is no safer. For a learning exercise, use

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

and enter long strings to examine effects of buffer overflows.

For safe handling of input of indeterminate length, use fgets and sscanf or strtol (or strtoul if Base.Level cannot be negative.

Here is an example:

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

Output:

C:\Temp> t
Enter name:
A dark and mysterious dungeon
Enter level:
3456772
Name: A dark and mysterious dungeon
Level: 3456772

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM