为什么这个C程序会崩溃?
我已经考虑了至少一个小时,但我仍然不知道问题出在哪里为什么这个C程序会崩溃?,c,C,我已经考虑了至少一个小时,但我仍然不知道问题出在哪里 #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("%
#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;
}
#包括
类型定义结构
{
智力水平;
字符*名称;
}基础;
基本结构;
int main(int argc,char*argv[])
{
scanf(“%s”,baseStruct.Name);
scanf(“%d”和baseStruct.Level);
printf(“%s::级别%d\n”,baseStruct.Name,baseStruct.Level);
返回0;
}
发生的情况是,我去输入“Name”字符串,然后当我输入整数时,程序崩溃。发生了什么事
scanf("%s", ...)
这需要一个缓冲区(scanf
需要写入缓冲区),然后给它一个未初始化的指针,可以指向任何地方
考虑执行以下操作之一:
Name
改为字符缓冲区:
typedef struct
{
int Level;
char Name[100];
} Base;
baseStruct.Name = malloc(100); /* do not forget to cleanup with `free()` */
scanf
格式字符串中指定最大字符串长度,以防止溢出:
/* assume 'Name' is a buffer 100 characters long */
scanf("%99s", baseStruct.Name);
Name只是指向字符串的未初始化指针。它没有指向任何有用的东西。您需要将其正确初始化为字符串缓冲区。此外,您可能希望通过格式化(如%100s)限制字符串,以确保不会超出缓冲区。不要为每个人都会犯这样的错误感到难过。char*代表“指向字符的指针”,但字符串本身的内存没有分配 加: baseStruct.Name=malloc(sizeof(char)*100)
(注意我的语法可能有点不正确)您尚未为Base.Name分配任何存储空间。您正在将字符串扫描到不指向任何存储的指针中 为字符串分配一些空间。问题是,您不知道使用scanf复制的字符串有多大。假设您malloc 256字节,然后scanf加载一个300字节的字符串?分配一个足够大的字符串以处理来自scanf的所有可能结果,或者修改scanf以限制字符,如:
baseStruct.Name = malloc(sizeof(char) * 256);
scanf("%256s", baseStruct.Name);
正如其他人指出的,
baseStruct.Name
没有指向有效的内存区域。但是,分配固定大小的缓冲区并不安全。对于学习练习,请使用
typedef struct
{
int Level;
char Name[1];
} Base;
并输入长字符串以检查缓冲区溢出的影响
为安全处理长度不确定的输入,请使用fgets
和sscanf
或strtol
(或strtoul
如果基本电平
不能为负值
以下是一个例子:
#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;
}
#包括
#包括
#包括
#包括
#包括
#定义初始尺寸100
#定义最大尺寸30000
char*myreadline(文件*fin){
字符*缓冲区;
整数偏移=0;
int bufsize=初始尺寸;
缓冲区=malloc(bufsize);
如果(!缓冲区){
返回NULL;
}
而(fgets(缓冲区+偏移量、bufsize、fin)){
最后的大小=strlen(缓冲区)-1;
如果(缓冲区[最后]==(字符)'\n'){
缓冲区[最后]=0;
打破
}
否则{
char*tmp;
偏移量+=bufsize-1;
bufsize*=2;
如果(尺寸>最大尺寸){
打破
}
tmp=realloc(缓冲区,bufsize);
如果(!tmp){
打破
}
否则{
缓冲区=tmp;
}
}
}
返回缓冲区;
}
int myreadint(文件*fin,int*i){
长x;
char*endp;
char*line=myreadline(fin);
如果(!行){
返回0;
}
x=strtol(直线和端点,10);
if((!*endp | | isspace((未签名字符)*endp))
&&(x>=INT_MIN)和(x t)
输入名称:
黑暗而神秘的地牢
输入级别:
3456772
名称:黑暗神秘的地牢
级别:3456772当然,如果用户输入的字符数超过100个,这将严重崩溃。除非您完全确定输入,否则对字符串使用scanf是不安全的。此外,您需要将结果强制转换为char*@EboMike这是C。因此,无需强制转换malloc
@Gabrielsizeof(char)的返回值
总是1
。标准上说sizeof(char)
总是1,所以它不是真的需要。哎哟!您分配了256个字节并指定了%256s
sizeof(char)
总是1,所以它是多余的。或者,您可以使用sizeof(*baseStruct.Name)
如果baseStruct.Name
的类型可能会从char*
更改为wchar\u t*
,等等@Kludge:我认为分配足够大的字符串不是一个现实的选择,因为此字符串的大小可能正好是malloc
可以返回的最大大小,而让程序保持sti我很容易受到缓冲区溢出的影响。你在哪里找到C的STL版本?好吧,起诉我,我没有告诉我这是一个C特定的问题。我不认为仅仅使用sscanf
而不是scanf
会有任何区别。如果使用其中任何一个,你仍然必须在格式说明符中输入最大字符串长度。使用fgets
读取输入允许您使用realloc
调整缓冲区大小以适应任何实际大小的字符串。然后您可以使用sscanf
或strto(ld)
转换数字。显然,您在读取字符串时不会使用sscanf
。从scanf
的角度来看,最大字符串长度是可选的,但为了确保您将获得一个工作程序,它应被视为强制性的。不包括最大长度说明符将创建另一个程序易受缓冲区溢出的影响。@Schedler我不确定你的意思。这正是我在回答中说的,不?内存,内存,内存,内存,…&^%(*#分段错误!
C:\Temp> t
Enter name:
A dark and mysterious dungeon
Enter level:
3456772
Name: A dark and mysterious dungeon
Level: 3456772