当前位置:C++技术网 > 资讯 > 程序执行异常中断,编译没错,执行一半走不下去跳出了。

程序执行异常中断,编译没错,执行一半走不下去跳出了。

更新时间:2016-12-12 15:57:06浏览次数:1+次

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct student
{
	char name[30];
    int age;
    int num;
};
//filename数组存放文件名
char filename[25];
FILE *fp;
//主要函数原型声明
void creat();
void output();
void append();

main()
{
	int m;char k;
	printf("\n请输入你要打开的文件名:");
	gets(filename);
	if((fp=fopen(filename,"r"))==NULL)
		{
		printf("\n当前没有此文件,现在是否执行创建(Y/N)?:");
		scanf("%c",&k);getchar();//用于接收输入后的回车键,下边的delete函数同理
		while(k!=''''Y''''&&k!=''''N''''&&k!=''''y''''&&k!=''''n'''')
			{printf("\n输入有误,请再次输入一个值:");scanf("%c",&k);getchar();}
		if(k==''''Y''''||k==''''y'''') {creat();}//当选择了Y或y时执行创建函数
			else if(k==''''N''''||k==''''n'''')
					{printf("\n由于你选择了退出,现在程序即将关闭!\n");}
		}
	else	//当文件存在时把通讯录信息输出,并且提示操作
		{
			printf("\n你要打开的文件%s已存在,现在可直接对其操作\n",filename);
			printf("\n文件%s现已有的信息:\n",filename);output();
			printf("\n请根据提示选择1或2对文件进行操作\n");
		}
	while(1)
	{
		printf("\n添加,请按1");
		printf("\n输出,请按2");
		printf("请输入数字1或2:");
		scanf("%d",&m);getchar();
		while(m<0||m>5)
			{printf("\n抱歉,您输入的值不是1或2的数字,请再次输入:");scanf("%d",&m);getchar();}
		if(m>=0&&m<=5)
		{
			switch(m)
			{
			case 1: append();break;
			case 2: output();break;
			}
			printf("\n操作完毕,请再次选择!\n");
		}
	}
}

/*建立通信录函数creat():功能:建立通信录文件,并连续输入记录,以0结束输入*/
void creat()
{
	struct student one;
    if((fp=fopen(filename,"w"))==NULL)
    {
		printf("\n不能建立文件!");
		return;
    }
	printf("\n成功创建%s文件,请按提示进行操作*^_^*\n",filename);
    fprintf(fp,"%-30s%-20d%-25d%\n","姓名","学号","年龄");
    printf("\n请输入每个成员的姓名、学号、年龄(当成员名是0时结束输入):\n");
    scanf("%s",one.name);
    while(strcmp(one.name,"0"))
		//当输入的名字不为"0"的时候,执行循环,当名字为"0"时,退出循环
    {
		scanf("%d%d",one.num,one.age);
		fprintf(fp,"%%-30s%-20d%-25d%\n\n",one.name,one.num,one.age);
			//把输入的信息按指定格式写入文件当中
		scanf("%s",one.name);
    }
    fclose(fp);
}

/*输出函数output();通信录中所有通信记录,每行输出一条记录*/
void output()
{
	struct student one;
    if((fp=fopen(filename,"r"))==NULL)
    {
		printf("\n不能打开文件!");
		return;
    }
    printf("\n\n%40s\n","< 文 件 >");
    while(!feof(fp))//当文件未结束时,逐条读取文件信息并输出
    {
		fscanf(fp,"%s%d%d\n",one.name,one.num,one.age);
		printf("%-30s%-20d%-25d%",one.name,one.num,one.age);
    }
    fclose(fp);
}

/*通讯录添加函数*/
void append()
{
	struct student one;
    if((fp=fopen(filename,"a"))==NULL)
    {
		printf("\n不能文件!");
		return;
    }
	printf("\n请输入添加的姓名:");
	scanf("%s",one.name);
	printf("请输入学号");
	scanf("%d",one.num);
	printf("请输入年龄");
	scanf("%d",one.age);
    fprintf(fp,"%-30s%-20d%-25d%\n",one.name,one.num,one.age);
	printf("\n你添加的信息:\n");
	printf("%-30s%-20d%-25d%\n","姓名","学号","年龄");
	printf("%-30s%-20d%-25d%\n",one.name,one.num,one.age);
    fclose(fp);
}

C++技术网会员解答:

处理之后的效果图:

运行的崩溃图:


    看出来问题了吗?问题出在最基本的语法上。然而编译时却检查不出来,为什么呢?我们来分析一下。

    出问题的是scanf和fscanf,函数的后面几个参数都是变量的地址,是将输入的值赋值给变量。在处理名字的时候,因为one.name本来就是字符数组,所以one.name就代表了地址。而变量one.num和one.age则是两个变量,不折不扣的变量哦。所以,语法错误了。应该传递变量的地址,使用&操作符取地址。如下所示:

&one.num,&one.age
     这样一改,程序就如第一张图一样,正常运行了。当然错误都是在所难免的,栽在基本错误上也都是常有的事。关键是,这些错误要能够识别出来。如果基础不扎实,很容易近在咫尺却远在天涯!你就是这样,我相信你在程序崩溃之后,怎么也看不出来这个错误。小样,还是自己嫩了点,没有火眼金睛呀。

    那么问题解决了,我们再进一步分析一下,既然都摔了一跤,那么就要摔的值得,不要起身就跑了,那样还会摔跤的。解决问题要抓住本质,如何抓,仔细研究一番。

    为什么编译没有错而运行有错呢?很多人认为,编译通过了,程序就不会有问题了。那就错的太离谱了。亲,这是C/C++的世界,变化莫测,随心所欲。编译器乃一介凡人,对于众多不可预测的变数,也只是叹命运的无奈。

    我们这个问题编译不崩溃的原因在于,scanf和fscanf后面的参数需要的是地址,地址又是什么?地址实际也是一个整数而已。那么变量的值不就是整数吗?那么这样不就隐含了将整型变量的值转为地址值嘛。这样的转换行不行呢?行的。否则编译就报错了。整数到整数的转换为什么不行呢?是吧。所以编译通过了。

    那么地址又是什么呢?地址可不是随便一个数值哦。地址的值代表了一块内存的位置。因为函数会通过地址去操作内存,如果地址错误,也就要崩溃了。虽然整型数值转为了地址,然而数值不对,在运行的时候去操作内存就会出问题。我们这两个变量初始的时候并没有赋值,变量存储的是随机的垃圾值。所以,这个值有可能对应到一个可以用的内存,也可能不对应一个可用的内存或者说对应一个被别人占用了的内存。不管是哪种,都不是幸运的。你以为对应到一个可用的内存会是幸运的,恰恰相反,如果果真对应到了可用的内存,你还发现不了错误,结果有时崩溃有时不崩溃,我想你会崩溃的。对应的是不可用内存或者被占用的内存,直接崩溃,这样还可以很快发现错误,及早解决。