简体   繁体   English

为什么这个C程序会崩溃?

[英]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. 会发生什么,我去输入“Name”字符串,然后当我输入并输入整数时程序崩溃。 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. 这需要一个缓冲( scanf需要写入它),你给它一个未初始化的指针,可能指向任何地方。

Consider doing one of the following: 考虑执行以下操作之一:

  1. Make Name a character buffer instead: Name为字符缓冲区:

     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: 您还应该在scanf格式字符串中指定最大字符串长度以防止溢出:

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

Name is just an uninitialized pointer to a string. Name只是一个指向字符串的未初始化指针。 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. 此外,您可能希望通过格式化限制字符串(如%100s)以确保不会超出缓冲区。

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. char *代表“指向字符的指针”,但不分配字符串本身的内存。

Add: 加:

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

(note my syntax may be off a little) (注意我的语法可能有点偏差)

You haven't allocated any storage for Base.Name. 您尚未为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. 问题是你不知道使用scanf复制一个字符串有多大。 Suppose you malloc 256 bytes and then scanf loads in a 300 byte string? 假设你malloc 256字节然后scanf加载300字节的字符串? Either allocate a string sufficiently large to handle all possible results from scanf, or modify your scanf to limit the characters, like: 要么分配足够大的字符串来处理scanf的所有可能结果,要么修改scanf来限制字符,例如:

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. 正如其他人所指出的那样, baseStruct.Name并不指向有效的内存区域。 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. 为了安全处理不确定长度的输入,请使用fgetssscanfstrtol (如果Base.Level不能为负,则使用strtoul

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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