第一篇:c语言源程序段
1.有三个整数a,b,c,由键盘输入,输出其中最大的数。#include
scanf(“%d%d%d”,&a,&b,&c);if(a>b&&a>c)
printf(“%dn”,a);else
if(b>a&&b>c)
printf(“%dn”,b);
else
printf(“%dn”,c);} 2.编程输入整数a和b,若a2b2大于100,则输出a2b2百位以上的数字,否则输出两数之和。
#include
printf(“%dn”,(d+e));else
printf(“%dn”,f);} 3.有一函数:
x(x1) y2x11(1x10)
3x11(x10)编写一程序,输入x,输出y值。#include
y=x;else
if(x<10&&x>=1)
y=2*x-11;
else
y=3*x-11;
printf(“%dn”,y);} 4.给出一百分制成绩,要求输出成绩等级’A’,’B’,’C’,’D’,’E’。90分以上为’A’,80-89分为’B’,70-79分为’C’,60-69分为’D’,60分以下为’E’ #include
printf(“A”);else
if(x<90&&x>=80)
printf(“B”);
else
if(x<80&&x>=70)
printf(“C”);
else
if(x<70&&x>=60)
printf(“D”);
else
printf(“E”);printf(“n”);} 5.提高题:给一个不多于5位的正整数,要求:①求出它是几位数;②分别打印出每一位数字;③按逆序打印出各位数字,例如原数是321,应输出123。#include
for(i=0;j>1;i++)
j=j/10;}
printf(“%dnn”,i);{
for(k=1;k<=i;k++){
b=a%10;
a=a/10;
printf(“%d”,b);} } }.求解一元二次方程a*x2+b*x+c=0 #include
int main(){ int a,b,c,m;double x1,x2,n;
//解为double类型
printf(“请输入ax2+bx+c=0中的a,b,c: n”);scanf(“%d %d %d”,&a,&b,&c);//输入参数
m=(b*b-4*a*c);if(m<0)
printf(“方程无解”);else
n=sqrt((double)m);
//对m进行强制类型转换为double,因为接为double
x1=(-b-m)/(2*(double)a);
x2=(-b+m)/(2*(double)a);
printf(“x1=%.2lf x2=%.2lfn”,x1,x2);return 0;}.有一个分数数列: 23581321,,,求出这个数列前20项之和 1235813#include
double sum(int n){ int i;double part = 0;
for(i = 1.0;i <= n;i++)
part +=(1.0 / i);return 2 * n-part;}
int main(void){ printf(“%.18fn”, sum(20));return 0;} 将从键盘输入的偶数写成两个素数之和。#include
int a,b,c,d;
scanf(“%d”,&a);
for(b=3;b<=a/2;b+=2)
{
for(c=2;c<=sqrt(b);c++)
if(b%c==0)break;
if(c>sqrt(b))
d=a-b;
else break;
for(c=2;c<=sqrt(d);c++)
if(d%c==0)break;
if(c>sqrt(d))
printf(“%d=%d+%dn”,a,b,d);
} } 1:5位跳水高手参加10米高台跳水决赛,有好事者让5人据实力预测比赛结果.A选手说:B第二,我第三B选手说:我第二,E第四;C选手说:我第一,D第二;D选手说:C最后,我第三;E选手说:我第四,A第一.决赛成绩公布之后,每位选手的预测都只说对了一半,即一对一错.请编程解出比赛的实际名次.
1.#include
#include
} for(i=1;i<=4;i++)
b=a[0];a[0]=a[3];a[3]=b;b=a[1];a[1]=a[2];a[2]=b;for(k=1;k<=4;k++)printf(“%d”,a[k-1]);{ } a[i-1]=(a[i-1]+5)%10;a[4-j]=b%10;b=b/10;}
2、编写程序,对输入两个正整数m和n,求出它们的最大公约数和最小公倍数 #include “stdio.h” #include “math.h” void main(){ int n,m,maxgy,mingb,i,p;printf(“please input n and m:”);scanf(“%d%d”,&n,&m);if(n>m){
p=n;n=m;m=p;/*m和n交换*/ } for(i=n;i>=1;i--)
if(m%i==0&&n%i==0)
break;maxgy=i;printf(“nmaxgy=%d mingb=%dn”,maxgy,m*n/maxgy);
}
2、编写程序,对输入两个正整数m和n,求出它们的最大公约数和最小公倍数
#include “stdio.h” #include “math.h” void main(){ int n,m,maxgy,mingb,t,p;printf(“please input n and m:”);scanf(“%d%d”,&n,&m);if(n>m){
p=n;n=m;m=p;/*m和n交换*/ } p=m*n;while(m%n!=0){
t=m%n;
m=n;
n=t;} maxgy=n;printf(“nmaxgy=%d mingb=%dn”,maxgy,p/maxgy);} #include “stdio.h” #include “math.h” void main(){ int n,m,maxgy,p;int maxgy1(int m,int n);printf(“please input n and m:”);scanf(“%d%d”,&n,&m);if(n>m){
p=n;n=m;m=p;/*m和n交换*/ } p=m*n;
maxgy=maxgy1(m,n);printf(“nmaxgy=%d mingb=%dn”,maxgy,p/maxgy);} int maxgy1(int m,int n){ if(n==0)
return m;else return maxgy1(n,m%n);}
3输入n判断n是否为素数 #include “stdio.h” void main(){ int n,i,flag;flag=1;printf(“please input n:”);scanf(“%d”,&n);for(i=2;i if(n%i==0) { flag=0; break; } if(flag==1) printf(“n%d is ssn”,n);else printf(“n%d is not ssn”,n);} #include “stdio.h”、求100以内的所有素数,并按10个一行进行打印。#include “math.h” void main(){ int n,i,flag,sum;sum=0;for(n=2;n<=100;n++){ flag=1; for(i=2;i<=sqrt(n);i++) if(n%i==0) { flag=0; break; } if(flag==1) { sum++; printf(“%5d”,n); if(sum%10==0) printf(“n”); } } printf(“n”);} 4、用递归求1到100的和 #include return 1;else return n+lj(n-1);} 累加法求1到100的和 #include sum=sum+i;printf(“result=%dn”,sum);} #include printf(“result=%dn”,sum);} #include if(i>100) break; sum=sum+i; i++;} printf(“result=%dn”,sum);} 求20到40以及60到80中所有能被3整除数的和 #include if(i%3==0) sum=sum+i; if(i==40) i=i+19;} //i%3==0&&i>=20&&i<=40||i>=60&&i<=80 printf(“result=%dn”,sum);} A+aa+aaa+….+a…..a #include t=t*10+a; sum=sum+t;} printf(“result=%ldn”,sum);} 1、请从键盘上输入年、月、日,求该年月日是该年的第多少天? #include int year,month,day,sum=0,i;int days(int,int);printf(“please input year month and day:”);scanf(“%d%d%d”,&year,&month,&day);for(i=1;i int day;switch(month){ case 1: case 3: case 5: case 7: case 8: case 10: case 12:day=31;break;case 4: case 6: case 9: case 11:day=30;break;case 2:if(year%4==0&&year%100!=0||year%400==0) } return day; day=29;else day=28;} 2、求3到1000内所有尾数为3的素数之和。#include } int ss(int n){ int flag=1,i;for(i=2;i<=sqrt(n);i++)if(n%i==0){ flag=0;break;int i,sum=0;int ss(int);for(i=3;i<=1000;i++)if(ss(i)==1) if(i%10==3) sum=sum+i;printf(“result=%dn”,sum); } } return flag; 3、从键盘上输入一个整数,将它拆成质因子的乘积。例如 18=2*3*3 #include } if(n%i==0){ } else i++;printf(“%d*”,i);n=n/i;printf(“b n”);} 4、从键盘上输入一串字符,统计字母、数字、空格和其它字符的个数。#include char ch;int c,d,s,o;c=d=s=o=0;while((ch=getchar())!=10){ if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z') c++;else if(ch>='0'&&ch<='9') d++;else if(ch==' ') s++;else o++;} printf(“c=%dnd=%dns=%dno=%dn”,c,d,s,o);} 5、从键盘上输入10个数,求它们的最大值。#include } int n,i,max;scanf(“%d”,&n);max=n;for(i=1;i<10;i++){ scanf(“%d”,&n);if(max max=n;} printf(“max=%dn”,max);3. 一个数恰好等于它的因子之和,这个数就称为“完数”。例如,6的因子为1,2,3而6=1+2+3,因此6是完数。编程找出求1000以内的所有完全数。#include “stdio.h” #include “math.h” void main(){ int n,s,i,k;for(n=1;n<=1000;n++){ s=0; for(i=1;i if(n%i==0) s=s+i; if(n==s) { printf(“%5d its factors is ”,n); for(k=1;k if(n%k==0) printf(“%d,”,k); printf(“b n”); } } printf(“n”);} .打印出杨辉三角形(要求打印出10行如下图)#include “stdio.h” #include “math.h” void main(){ long i,j,x,y,z,k;long jc(int);for(i=0;i<10;i++){ for(j=0;j<=i;j++) { x=jc(i); y=jc(j); z=jc(i-j); printf(“%4d”,x/(y*z)); } printf(“n”);} } long jc(int n){ long x=1,k;if(n==0) return 1;else for(k=1;k<=n;k++) x=x*k;return x;} #include “stdio.h” #include “math.h” void main(){ long i,j,x,y,z,k;for(i=0;i<10;i++){ for(j=0;j<=i;j++) { x=y=z=1; for(k=1;k<=i;k++) x=x*k; for(k=1;k<=j;k++) y=y*k; for(k=1;k<=i-j;k++) z=z*k; printf(“%4d”,x/(y*z)); } printf(“n”);} } 7.用*打印图形 #include “stdio.h” void main(){ int i,j,k,n;printf(“please input n:”);scanf(“%d”,&n);for(i=1;i<=n;i++)/**/ { for(j=1;j<=40-i;j++)/**/ printf(“ ”); for(k=1;k<=2*i-1;k++)/**/ printf(“*”); printf(“n”);} for(i=n-1;i>=1;i--)/**/ { for(j=1;j<=40-i;j++)/**/ printf(“ ”); for(k=1;k<=2*i-1;k++)/**/ } } printf(“*”);printf(“n”); 基于单片机msp430和温度传感器ds18b20的水温度控制系统的c语言源程序(不是测量,要有加热跟制冷) 我这是用STC做的,应该很容易移植到MPS430上的给你参考一下。#include sbit scl=P1^3;sbit sda=P1^4; sbit key1=P1^6;sbit key2=P1^7;sbit key3=P2^0;sbit key4=P2^1; sbit lcrs=P3^7;//数据/命令 sbit lcwr=P3^5;//读/写 sbit lcden=P3^4;//使能 sbit DS=P2^2; /*sbit lcrs=P3^4;//数据/命令 sbit lcwr=P3^7;//读/写 sbit lcden=P3^5;//使能 */ sbit jrk=P2^2;sbit cyk=P2^3;sbit xhk=P2^4;bit flag=0,rsg=0,not=0,he=0,in=0;int acon=0,bcon=0,dcon=0,econ=0, temp=0,y=0,j=0,l=0,cfj=0,ec=0,dc=0,at;uchar code table[]={48,49,50,51,52,53,54,55,56,57};uchar code ta1[]={“Temperature UP”};uchar code ta2[]={“Temperature DN”};uchar code ta3[]={“Inflator Cycle”};uchar code ta4[]={“Inflator Time ”};uchar code ta5[]={“ Heating UP ”};uchar code ta6[]={“ Inflator ”};uchar code table7[]={“Temperature”};uchar table1[]={0,0,0,'.',0};uchar table3[]={“AptitudeAquarium”};uchar table4[]={0,0,0,0,0};uchar n,c=0;void delay(uchar);void wen_kong();void xh();void rso();void weno(); void Init_Com(void){ TMOD = 0x11;PCON = 0x00;TH1=0x61;TL1=0x99;EA=1;ET1=1;TR1=1;} void delay(uchar count)//delay { uint i;while(count){ i=200;while(i>0)i--;count--;} } ////初始化18B20///////// bit init18b20(void){ uint i;bit no;DS=0;i=103;while(i>0)i--;DS=1;i=4;while(i>0)i--;no=DS;if(no==0){ DS=1;i=100;while(i>0)i--;no=DS;if(no==1)not=0;else not=1;} else not=1;return(not);} bit tmpread()bit(void)//读一位 { uint i;bit dat;DS=0;i++;DS=1;i++;i++;dat=DS;i=8;while(i>0)i--;return(dat);} uchar tmpread()(void)//读一个字节 { uchar i,j,dat;dat=0;for(i=1;i<=8;i++){ j=tmpread()bit();dat=(j<<7)|(dat>>1);//读出的数据最低位在最前面,这样刚好一个字节在DAT里 } return(dat);} void tmpwritebyte(uchar dat)//写一个字节到 ds18b20 { uint i;uchar j;bit testb;for(j=1;j<=8;j++){ testb=dat&0x01;dat=dat>>1;if(testb)//write 1 { DS=0;i++;i++;DS=1;i=8;while(i>0)i--;} else { DS=0;//write 0 i=8;while(i>0)i--;DS=1;i++;i++;} } } int tmp()//DS18B20温度读取 { float tt;int a,b;if(init18b20()==0){ WDT_CONTR=0x36;/////喂狗 EA=0;delay(1); tmpwritebyte(0xcc);// 跳过读ROM操作 tmpwritebyte(0x44);// 启动温度转换 delay(10);init18b20();delay(1);tmpwritebyte(0xcc);tmpwritebyte(0xbe);a=tmpread();b=tmpread();temp=b;temp<<=8;//将高字节温度数据与低字节温度数据整合 temp=temp|a;c=b>>4; tt=temp*0.0625; temp=tt*10+0.5;//放大10倍输出并四舍五入 EA=1;return temp;} else not=1;} //////1062///////// void ydelay(uint x){ uint a,b;for(a=x;a>0;a--)for(b=10;b>0;b--);} void write_com(uchar com){ P0=com;lcwr=0;lcrs=0;lcden=0;ydelay(10);lcden=1;ydelay(10);lcden=0;lcwr=1;} void write_date(uchar date)//写数据 { P0=date;lcwr=0;lcrs=1;lcden=0;ydelay(10);lcden=1;ydelay(10);lcden=0;lcwr=1;} void init1602()//初始化 { write_com(0x38);//设置显示模式 ydelay(20);write_com(0x0c);//开显示 ydelay(20);write_com(0x06);//指针和光标自动加一 ydelay(20);write_com(0x01);//清屏指令 ydelay(20);} ///////显示程序////// void display(int num){ uint i,A1,A2;WDT_CONTR=0x35;/////喂狗 if(c!=0)num=~num+1;A1=num/1000;A2=num%1000/100;if(not==0){ if(c!=0){ c=0;table1[0]='-';} else if(A1==0)table1[0]=' ';else table1[0]=table[A1];if(A1==0)if(A2==0)table1[1]=' ';else table1[1]=table[A2]; table1[2]=table[num%1000%100/10];table1[4]=table[num%1000%100%10];} else { table1[0]='?';table1[1]='?';table1[2]='?';table1[4]='?';} write_com(0x80);for(i=0;i<11;i++){write_date(table7[i]);delay(2);} write_com(0x8b);for(i=0;i<5;i++){write_date(table1[i]);delay(2);} write_com(0xc0);for(i=0;i<16;i++){ if(he==1)write_date(ta5[i]);else if(in==1)write_date(ta6[i]);else write_date(table3[i]);} c=0;WDT_CONTR=0x35;/////喂狗 } ////显示2//////////////////// display2(uchar bh,int dat){ uchar a,A,B;WDT_CONTR=0x35;/////喂狗 //write_com(0x01);//清屏指令 y=dat;y=y&0x8000;if(y!=0)dat=~dat+1;A=dat/1000;B=dat%1000/100;if((bh!=4)&&(bh!=5)){ if(A!=0)table4[0]=table[dat/1000];else if((c!=0)||(y!=0)){ c=0;y=0;table4[0]='-';} else table4[0]=' ';if(B!=0)table4[1]=table[B];else table4[1]=' ';table4[2]=table[dat%1000%100/10];table4[3]='.';table4[4]=table[dat%1000%100%10];} else { table4[0]=' ';if((c!=0)||(y!=0)){ c=0;y=0;table4[1]='-';} else table4[1]=' ';table4[2]=' ';table4[3]=table[dat%1000%100/10];table4[4]=table[dat%1000%100%10];} write_com(0xc4);delay(2);for(a=0;a<5;a++)write_date(table4[a]);delay(2);write_com(0x80);switch(bh){ case 1:for(a=0;a<14;a++)write_date(ta1[a]);break;case 2:for(a=0;a<14;a++)write_date(ta2[a]);break;case 3:for(a=0;a<14;a++)write_date(ta3[a]);break;case 4:for(a=0;a<14;a++)write_date(ta4[a]);break;default:break;} } ///////////x24c02////////////////// void delay24(){;;} void init24c02()//初始化 { sda=1;delay24();scl=1;delay24();} void start()//开始信号 { sda=1;delay24();scl=1;delay24();sda=0;delay24();} void stop()//停止 { sda=0;delay24();scl=1;delay24();sda=1;delay24();} void respons()//应答 { uchar i;scl=1;delay24();while((sda==1)&&(i<250))i++;scl=0;delay24();} void write_byte(uchar date)// 写数据子函数 { uchar i,temp;temp=date; for(i=0;i<8;i++){ temp=temp<<1;scl=0;delay24();sda=CY;delay24();scl=1;delay24();} scl=0;delay24();sda=1;delay24();} uchar read_byte()// 读数据子函数 { uchar i,k;scl=0;delay24();sda=1;delay24();for(i=0;i<8;i++){ scl=1;delay24();k=(k<<1)|sda;scl=0;delay24();} return k;} ///////写数据函数/////////////////// void write_add(uchar address,uint date){ start();write_byte(0xa0);respons();write_byte(address);respons();write_byte(date/256);respons();write_byte(date%256);respons();stop();} uchar read_add(uchar address)//读数据函数 { uchar date;start();write_byte(0xa0);respons();write_byte(address);respons();start();write_byte(0xa1);respons();date=read_byte();stop();return date;} void delay1ms(uchar ms){ uchar i;while(ms--){ for(i = 0;i< 250;i++){ _nop_();_nop_();_nop_();_nop_();} } } int keyf(int *num,int up,int dn){ uint i;uchar z;for(i=0;i<600;i++){ display2(n,*num);if(key1==0){ delay1ms(30);if(key1==0){ i=0;n++;if(n>=9)n=0;while(!key1)display2(n,*num);break;} } if(key2==0){ delay1ms(10);if(key2==0){ i=0;if(*num>=up)*num=up;else if(n!=4)*num=*num+1;else if(*num<100)*num=*num+5;else *num=*num+10;for(z=0;z<65;z++){ display2(n,*num);if(key2!=0)break;} while(!key2){ for(z=0;z<2;z++)display2(n,*num);if(*num>=up)*num=up;else if(n!=4)*num=*num+1;else if(*num<100)*num=*num+5;else *num=*num+10;} } } if(key3==0){ delay1ms(10);if(key3==0){ i=0;if(*num<=dn)*num=dn;else if(n!=4)*num=*num-1;else if(*num<100)*num=*num-5;else *num=*num-10;for(z=0;z<65;z++){ display2(n,*num);if(key3!=0)break;} while(!key3){ for(z=0;z<2;z++)display2(n,*num);if(*num<=dn)*num=dn;else if(n!=4)*num=*num-1;else if(*num<100)*num=*num-5;else *num=*num-10;} } } } return(*num);} void keyjc(){ uchar i=0;if(key1==0){ delay1ms(10);if(key1==0){ EA=0; for(i=0;i<20;i++){ display(tmp());} if(key1==0){ write_com(0x01);//清屏指令 n++;if(n>=5)n=0;while(!key1){ switch(n){ case 1:display2(n,acon);break;case 0:break;} } if(n==1){ keyf(&acon,1250,-530);if((acon-bcon)<3)bcon=acon-3;} if(n==2){ keyf(&bcon,1240,-550);if((acon-bcon)<3)acon=bcon+3;} write_add(1,acon);//A delay1ms(15);write_add(3,bcon);//B n=0;write_com(0x01);//清屏指令 } EA=1;} } } key(){ uint i;if(key4==0)delay1ms(50);if(key4==0){ write_com(0x01);//清屏指令 for(i=0;i<500;i++){ if(key4==0){ delay1ms(15);if(key4==0){ i=0;n++;if(n>=5)n=0;while(!key4){ switch(n){ case 1: display2(1,acon);break;case 2: display2(2,bcon);break;default: break;} } } } switch(n){ case 1: display2(1,acon);break;case 2: display2(2,bcon);break;default: break;} } n=0;} } ///////滤波//////// int filter(){ int tm,buf[6];uchar i,j;EA=0;for(i=0;i<6;i++){ buf[i]=tmp();delay1ms(20);WDT_CONTR=0x35;/////喂狗 } for(j=0;j<5;j++)for(i=0;i<5-j;i++)if(buf[i]>buf[i+1]){ tm=buf[i];buf[i]=buf[i+1];buf[i+1]=tm;} tm=((buf[2]+buf[3])/2);EA=1;return(tm);} void main(){ uchar b,c;Init_Com();init1602();init24c02(); b=read_add(1);delay1ms(15);c=read_add(2);delay1ms(15);acon=b*256+c;b=read_add(3);delay1ms(15);c=read_add(4);delay1ms(15);bcon=b*256+c; AUXR=0x01;// 禁止ALE输出 WDT_CONTR=0x35;//启动看门狗 write_com(0x01);//清屏指令 while(1){ at=filter();display(at);keyjc();key(); wen_kong();weno();} } //////温度控制////////////// void wen_kong(){ if((flag==0)&&(not==0)){ at=filter();if(at<=bcon) { flag=1;jrk=0;xhk=0;he=1;} } } void weno(){ if(flag){ at=filter();if(at>=acon){ flag=0;jrk=1;if(rsg)xhk=0;else xhk=1;he=0;} } if(not==1){ flag=0;jrk=1;if(rsg)xhk=0;else xhk=1;he=0;} } C/C++程序编译步骤如何生成可执行文件 电子计算机所使用的是由“0”和“1”组成的二进制数,二进制是计算机的语言的基础。计算机发明之初,人们只能降贵纡尊,用计算机的语言去命令计算机干这干那,一句话,就是写出一串串由“0”和“1”组成的指令序列交由计算机执行,这种语言,就是机器语言。想象一下老前辈们在打孔机面前数着一个一个孔的情景,嘘,小声点,你的惊吓可能使他们错过了一个孔,结果可能是导致一艘飞船飞离轨道阿。 为了减轻使用机器语言编程的痛苦,人们进行了一种有益的改进:用一些简洁的英文字母、符号串来替代一个特定的指令的二进制串,比如,用“A D D”代表加法,“MO V”代表数据传递等等,这样一来,人们很容易读懂并理解程序在干什么,纠错及维护都变得方便了,这种程序设计语言就称为汇编语言,即第二代计算机语言。然而计算机是不认识这些符号的,这就需要一个专门的程序,专门负责将这些符号翻译成二进制数的机器语言,这种翻译程序被称为汇编程序。因为汇编指令和机器语言之间有着一一对应的关系,这可比英译汉或汉译英简单多了。 高级语言是偏向人,按照人的思维方式设计的,机器对这些可是莫名奇妙,不知所谓。鱼与熊掌的故事在计算机语言中发生了。于是必须要有一个桥梁来衔接两者,造桥可不是一件简单的事情。当你越想方便,那桥就得越复杂。那高级语言是如何变成机器语言的呢,这个过程让我慢慢道来。 编译:将源代码转换为机器可认识代码的过程。编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件1.编译预处理 读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。伪指令主要包括以下四个方面:(1)宏定义指令,如# define Name TokenString,#undef等。对于前一个伪指令,预编译所要作得的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。 (2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。 (3)头文件包含指令,如#include “FileName”或者#include (4)特殊符号,预编译程序可以识别一些特殊的符号。例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。2.编译阶段 经过预编译得到的输出文件中,将只有常量。如数字、字符串、变量的定义,以及C语言 的关键字,如main,if,else,for,while,{,},+,-,*,,等等。预编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。3.优化阶段 优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。上图中,我们将优化阶段放在编译程序的后面,这是一种比较笼统的表示。 对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换 循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。4.汇编过程 汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段: 代码段 该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。数据段 主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。 UNIX环境下主要有三种类型的目标文件: (1)可重定位文件 其中包含有适合于其它目标文件链接来创建一个可执行的或者共享 的目标文件的代码和数据。 (2)共享的目标文件 这种文件存放了适合于在两种上下文里链接的代码和数据。第一 种事链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文 件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。 (3)可执行文件 它包含了一个可以被操作系统创建一个进程来执行之的文件。 汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能 得到,这个就是链接程序的工作了。5.链接程序 由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例 如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调 用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链 接程序的处理方能得以解决。 链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号 同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操 作系统装入执行的统一整体。 根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种: (1)静态链接 在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最 终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相 关函数的代码。 (2)动态链接 在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个 目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及 其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行 时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数 代码。 对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态 链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内 存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使 用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。 经过上述五个过程,C源程序就最终被转换成可执行文件了 上一节我们介绍了编程语言的种类,其中包括机器语言、汇编语言和高级语言。 C/C++程序编译步骤详解 C/C++语言很多人都比较熟悉,这基本上是每位大学生必学的一门编程语言,通常还都是 作为程序设计入门语言学的,并且课程大多安排在大一。刚上大学,孩子们还都很乖,学习也比较认真,用心。所以,C/C++语言掌握地也都不错,不用说编译程序,就是写个上 几百行的程序都不在话下,但是他们真的知道C/C++程序编译的步骤么? 我想很多人都不甚清楚,如果他接下来学过“编译原理”,也许能说个大概。VC的“舒适 ”开发环境屏蔽了很多编译的细节,这无疑降低了初学者的入门门槛,但是也“剥夺”了 他们“知其所以然”的权利,致使很多东西只能死记硬背,遇到相关问题就“丈二”。实 际上,我也是在学习Linux环境下编程的过程中才逐渐弄清楚C/C++源代码是如何一步步变 成可执行文件的。 总体来说,C/C++源代码要经过:预处理、编译、汇编和链接四步才能变成相应平台下的 可执行文件。大多数时候,程序员通过一个命令就能完成上述四个步骤。比如下面这段C 的“Hello world!”代码: File: hw.c #include stdio.h> int main(intargc, char *argv[]){ printf(”Hello World!n“);return 0;} 如果用gcc编译,只需要一个命令就可以生成可执行文件hw: xiaosuo@gentuxhw $ gcc-o hwhw.c xiaosuo@gentuxhw $./hw Hello World!我们可以用-v参数来看看gcc到底在背后都做了些什么动作: Reading specs from /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/specs Configured with: /var/tmp/portage/sys-devel/gcc-3.4.6-r2/work/gcc-3.4.6/configure--prefix=/usr--bindir=/usr/i686-pc-linux-gnu/gcc-bin/3.4.6-disable-altivec--enable-nls--without-included-gettext--with-system-zlib-enable-shared--enable-threads=posix--enable-__cxa_atexit--enable-clocale=gnu Thread model: posix gcc version 3.4.6(Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1-quiet-v hw.c-quiet-dumpbase hw.c-mtune=pentiumpro-auxbasehw-version-o /tmp/ccYB6UwR.s ignoring nonexistent directory ”/usr/local/include“ ignoring nonexistent directory ”/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../../i686-pc-linux-gnu/include“ #include ”...“ search starts here: #include...> search starts here: /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include /usr/include End of search list.GNU C version 3.4.6(Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)(i686-pc-linux-gnu)compiled by GNU C version 3.4.6(Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.9).GGC heuristics:--paramggc-min-expand=81--paramggc-min-heapsize=97004 /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../../i686-pc-linux-gnu/bin/as-V-Qy-o /tmp/ccq8uGED.o /tmp/ccYB6UwR.s GNU assembler version 2.17(i686-pc-linux-gnu)using BFD version 2.17 /usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/collect2--eh-frame-hdr-m elf_i386-dynamic-linker /lib/ld-linux.so.2-o hw /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crt1.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crti.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../../i686-pc-linux-gnu/lib-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../tmp/ccq8uGED.o-lgcc--as-needed-lgcc_s--no-as-needed-lc-lgcc--as-needed-lgcc_s--no-as-needed /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crtn.o 稍微整理一下,去掉一些冗余信息后,如下: cc1 hw.c-o /tmp/ccYB6UwR.s as-o /tmp/ccq8uGED.o /tmp/ccYB6UwR.s ld-o hw /tmp/ccq8uGED.o 以上三个命令分别对应于编译步骤中的预处理+编译、汇编和连接。预处理和编译还是放 在了一个命令(cc1)中进行的,可以把它再次拆分为以下两步: cpp-o hw.ihw.c cc1 hw.i-o /tmp/ccYB6UwR.s 一个精简过的能编译以上hw.c文件的Makefile如下:.PHONY: clean all: hw hw: hw.o ld-dynamic-linker /lib/ld-linux.so.2-o hw /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o hw.o-lc /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib/crtn.o hw.o: hw.s as-o hw.ohw.s hw.s: hw.i /usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1-o hw.shw.c hw.i: hw.c cpp-o hw.ihw.c clean: rm-rfhw.ihw.shw.o 当然,上面Makefile中的一些路径是我系统上的具体情况,你的可能与我的不同。接下来我们按照编译顺序看看编译器每一步都做了什么。首先是预处理,预处理后的文件hw.i: # 1 ”hw.c“ # 1 ”“ # 1 ”“...__extension__ typedef __quad_t __off64_t;__extension__ typedefint __pid_t;__extension__ typedefstruct { int __val[2];} __fsid_t;...extern int remove(__const char *__filename)__attribute__((__nothrow__));extern int rename(__const char *__old, __const char *__new)__attribute__((__nothrow__));...int main(intargc, char *argv[]){ printf(”Hello World!n“);return 0;} 注:由于文件比较大,所以只留下了少部分具有代表性的内容。 可以看见预处理器把所有要包含(include)的文件(包括递归包含的文件)的内容都添 加到了原始的C源文件中,然后把其输出到输出文件,除此之外,它还展开了所有的宏定 义,所以在预处理器的输出文件中你将找不到任何宏。这也提供了一个查看宏展开结果的 简便方法。 第二步“编译”,就是把C/C++代码“翻译”成汇编代码:.file ”hw.c“.section.rodata.LC0:.string ”Hello World!n“.text.globl main.type main, @function main: pushl�p movl %esp, �p subl $8, %esp andl $-16, %esp movl $0, �x addl $15, �x addl $15, �x shrl $4, �x sall $4, �x subl�x, %esp subl $12, %esp pushl $.LC0 call printf addl $16, %esp movl $0, �x leave ret.size main,.-main.section.note.GNU-stack,”“,@progbits.ident ”GCC:(GNU)3.4.6(Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)" 这个汇编文件比预处理后的C/C++文件小了很多,去除了很多不必要的东西,比如说没用 到的类型声明和函数声明等。 第三步“汇编”,将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux上 一般表现为ELF目标文件。xiaosuo@gentuxhw $ file hw.o hw.o: ELF 32-bit LSB relocatable, Intel 80386, version 1(SYSV), not stripped 最后一步“连接”,将上步生成的目标文件和系统库的目标文件和库文件连接起来,最终 生成了可以在特定平台运行的可执行文件。为什么还要连接系统库中的某些目标文件(crt1.o, crti.o等)呢?这些目标文件都是用来初始化或者回收C运行时环境的,比如 说堆内存分配上下文环境的初始化等,实际上crt也正是C RunTime的缩写。这也暗示了另 外一点:程序并不是从main函数开始执行的,而是从crt中的某个入口开始的,在Linux上 此入口是_start。以上Makefile生成的是动态连接的可执行文件,如果要生成静态连接的 可执行文件需要将Makefile中的相应段修改: hw: hw.o ld-m elf_i386-static-o hw /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbeginT.o-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6-L/usr/i686-pc-linux-gnu/lib-L/usr/lib/ hw.o--start-group-lgcc-lgcc_eh-lc--end-group /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crtn.o 至此,一个可执行文件才最终创建完成。通常的项目中并不需要把编译过程分得如此之细,前三步一般是合为一体的,在Makefile中表现如下: hw.o: hw.c gcc-o hw.o-c hw.c 实际上,如果对hw.c进行了什么更改,那么前三步大多数情况下都是不可避免的。所以把 他们写在一起也并没有什么坏处,相反倒可以用--pipe参数告诉编译器用管道替代临时文 件,从而提升编译的效率。 #include void main() { int driver,mode; driver=DETECT; mode=0; initgraph(&driver,&mode,“"); setcolor(15); line(66,66,88,88); lineto(100,100); linerel(36,64); getch(); restorecrtmode(); } -------------- #include #include void circlePoint(int x,int y)/*八分法画圆程序*/ { circle(320 x*20,240 y*20,3); circle(320 y*20,240 x*20,3); circle(320-y*20,240 x*20,3); circle(320-x*20,240 y*20,3); circle(320-x*20,240 y*20,3); circle(320-x*20,240-y*20,3); circle(320-y*20,240-x*20,3); circle(320 y*20,240-x*20,3); circle(320 x*20,240-y*20,3); } void MidBresenhamcircle(int r)/* 中点Bresenham算法画圆的程序 */ { int x,y,d; x=0;y=r;d=1-r;/* 计算初始值 */ while(x { circlePoint(x,y);/* 绘制点(x,y)及其在八分圆中的另外7个对称点 */ if(d<0)d =2*x 3;/* 根据误差项d的判断,决定非最大位移方向上是走还是不走 */ else { d =2*(x-y)5; y--; } x; delay(900000); } /* while */ } main() { int i,j,r,graphmode,graphdriver; detectgraph(&graphdriver,&graphmode); initgraph(&graphdriver,&graphmode,” “); printf(”中点Bresenhamcircle算法画圆的程序n“);/*提示信息*/ printf(”注意 |r|<=11“); printf(”n输入半径值 r:“); scanf(”%d“,&r); printf(”按任意键显示图形...“); getch(); cleardevice(); setbkcolor(BLACK); for(i=20;i<=620;i =20)/*使用双循环画点函数画出表格中的纵坐标*/ for(j=20;j<=460;j) putpixel(i,j,2); for(j=20;j<=460;j =20)/*使用双循环画点函数画出表格中的横坐标*/ for(i=20;i<=620;i) putpixel(i,j,2); outtextxy(320,245,”0“);/*原点坐标*/ outtextxy(320-5*20,245,”-5“);circle(320-5*20,240,2);/*横坐标值*/ outtextxy(320 5*20,245,”5“);circle(320 5*20,240,2); outtextxy(320-10*20,245,”-10“);circle(320-10*20,240,2); outtextxy(320 10*20,245,”10“);circle(320 10*20,240,2); outtextxy(320-15*20,245,”-15“);circle(320-15*20,240,2); outtextxy(320 15*20,245,”15“);circle(320 15*20,240,2); outtextxy(320,240-5*20,”-5“);circle(320,240-5*20,2);/*纵坐标值*/ outtextxy(320,240 5*20,”5“);circle(320,240 5*20,2); outtextxy(320,240-10*20,”-10“);circle(320,240-10*20,2); outtextxy(320,240 10*20,”10“);circle(320,240 10*20,2); outtextxy(20,10,”The center of the circle is(0,0)“);/*坐标轴左上角显示提示信息*/ setcolor(RED);/*标记坐标轴*/ line(20,240,620,240);outtextxy(320 15*20,230,”X“); line(320,20,320,460);outtextxy(330,20,”Y“); setcolor(YELLOW); MidBresenhamcircle(r); setcolor(BLUE);/*绘制圆*/ circle(320,240,r*20); setcolor(2); getch(); closegraph(); } -------------------------#include void main() { int driver,mode; driver=DETECT; mode=0; initgraph(&driver,&mode,”"); setcolor(15); circle(20,20,20); getch(); } 苏州科技学院毕业论文 目录 第1章 引言............................................................................................................................1 1.1 背景介绍.................................................................................................................1 1.1.1 研究背景简介..................................................................................................1 1.1.2 C语言简介......................................................................................................1 第2章 概要设计....................................................................................................................3 2.1 方案设计.................................................................................................................3 2.1.1 开发环境..........................................................................................................3 2.1.2 工作方式..........................................................................................................3 2.1.3 系统目标..........................................................................................................3 2.2 系统功能.................................................................................................................3 2.3 需要解决问题.........................................................................................................4 2.4 系统流程.................................................................................................................4 2.4.1 基本流程..........................................................................................................4 2.4.2 具体实现..........................................................................................................5 第3章 详细设计....................................................................................................................7 3.1 系统模块化分.........................................................................................................7 3.1.1 添加文件对话框..............................................................................................7 3.1.2 编译链接函数..................................................................................................8 3.1.3 运行/评判对话框.............................................................................................9 3.2 系统模块具体实现...............................................................................................10 3.2.1 准备工作........................................................................................................10 3.2.2 实现添加文件对话框....................................................................................11 3.2.3 实现编译链接函数........................................................................................15 3.2.4 实现运行/评判对话框...................................................................................19 第4章 测试..........................................................................................................................26 4.1 测试方案...............................................................................................................26 4.2 运行界面...............................................................................................................26 4.2.1 准备系统运行................................................................................................26 4.2.2 系统测试........................................................................................................27 结论..........................................................................................................................................30 致谢..........................................................................................................................................32 参考文献..................................................................................................................................33 附录A 外文参考文献(译文)..........................................................................................34 附录B 外文参考文献(原文)..........................................................................................48 I 苏州科技学院毕业论文 第1章 引言 1.1 背景介绍 1.1.1 研究背景简介 C语言是目前国际上广泛流行的、重要的计算机高级语言之一。它适合作为系统描述语言,即可用来编写系统软件,也可用来编写应用软件。对于学习计算机专业的学生来说,学好C语言将为今后学习其他编程语言打下良好的基础,而随着计算机应用的普及,更多的其他非计算机专业也会需要学习编写简单的程序,C语言也是一个很不错的选择。 学习C语言,首先是要学习理论知识,阅读编程思想,阅读源代码,其次就是动手实践上机编写程序了。只有通过自己编写程序,才能更好的掌握理论知识,发现不足,取得进步。 传统的教学方式中,学生采用FTP,Email甚至手写的方式提交编程作业,老师一般采用逐一检查并试运行的手工检查方式,然后给出相应的得分。由于在编程作业的提交过程中,很多学生可能会把一些有语法错误以及结果不正确的程序提交上来,这就需要花费老师很多的精力和时间,效果也不是很好。当前,采用计算机对源程序直接进行评判还不是很普遍。因此,有必要开发一套简单易用的C语言源程序的自动评判系统,帮助老师检查学生的编程作业,提高老师工作效率,减轻老师负担。 1.1.2 C语言简介 C语言是国际上广泛流行的、很有发展前途的计算机高级语言。它适合作为系统描述语言,即可用来编写系统软件,也可用来编写应用软件。 早期的操作系统等系统软件主要是用汇编语言编写的(包括 UNIX操作系统在内)。由于汇编语言依赖于计算机硬件,程序的可读性和可移植性都比较差。为了提高可读性和可移植性,最好改用高级语言,但一般的高级语言难以实现汇编语言的某些功能(汇编语言可以直接对硬件进行操作),例如:对内存地址的操作、位操作等)。人们设想能否找到一种既具有一般高级语言特性,又具有低级语言特性的语言,集它们的优点于一身。于是,C语言就在这种情况下应运而生了。 C语言是在B语言的基础上发展起来的,它的根源可以追溯到ALGOL 60。1960年出现的ALGOL 60是一种面向问题的高级语言,它离硬件比较远,不宜用来编写系统程序。1963年英国的剑桥大学推出了CPL语言。1967年英国剑 1 苏州科技学院毕业论文 桥大学的Matin Richards对 CPL语言作了简化,推出了BCPL语言。1970年美国贝尔实验室的 Ken Thompson以 BCPL语言为基础,又作了进一步简化,设计出了很简单的而且很接近硬件的 B语言,并用 B语言写第一个UNIX操作系统,在PDP-7上实现。1971年在PDP-11/20上实现了B语言,并写了UNIX操作系统。但B语言过于简单,功能有限。1972年至 1973年间,贝尔实验室的 D.M.Ritchie在B语言的基础上设计出了C语言(取 BCPL的第二个字母)。最初的C语言只是为描述和实现UNIX操作系统提供一种工作语言而设计的。1973年,K.Thom-pson和D.M.ritchie两人合作把UNIX的90%以上用 C改写。 后来,C语言多次作了改进,但主要还是在贝尔实验室内部使用。直到1-975年UNIX第6版公布后,C语言的突出优点才引起人们普遍注意。1977年出现了不依赖于具体机器的C语言编译文本《可移植C语言编译程序》,使C移植到其它机器时所做的工作大大简化了,这也推动了UNIX操作系统迅速地在各种机器上实现。随着 UNIX的日益广泛使用,C语言也迅速得到推广。C语言和UNIX可以说是一对孪生兄弟,在发展过程中相辅相成。1978年以后,C语言已先后移植到大、中、小、微型机上,已独立于UNIX和PDP了。现在C语言已风靡全世界,成为世界上应用最广泛的几种计算机语言之一。 C语言主要有以下一些特点:(1)语言表达能力强。(2)语言简洁、紧凑,使用灵活,易于学习和使用。(3)数据类型丰富,具有很强的结构化控制语句。(4)语言生成的代码质量高。(5)语法限制不严格,程序设计自由度大。(6)可移植性好。 苏州科技学院毕业论文 第2章 概要设计 2.1 方案设计 本课题的任务是完成一个C源程序的自动评判系统。首先需要解决的问题是确定整个系统的开发环境和工作方式,然后是确定系统所要达到的目标,接着分析系统所应完成的功能以及可能遇到的问题,最后给出具体实现的步骤。 2.1.1 开发环境 经过与指导老师的交流,查阅有关资料,分析系统的整体情况,本系统更加类似于一个应用程序,再结合自己所学知识,决定采用微软的Visual C++集成开发环境来开发整个程序。Visual C++是Windows环境下最强大、最流行的程序设计语言之一。Visual C++可以用来开发各种类型、不同规模和复杂程度的应用程序,开发效率很高,生成的应用软件代码品质优良。 2.1.2 工作方式 分析整个系统,最主要的功能是:编译链接源程序,运行目标程序,目标程序输入输出的重定向。结合Visual C++开发环境来看,有批处理方式、普通应用程序方式、Add-in方式。考虑到Add-in的方式在使用Visual C++编译器去编译链接C源程序的时候更加方便,并且Add-in的方式也可以利用Visual C++的可视化界面的开发功能,所以工作方式采用Visual C++下的Add-in方式。Visual C++ Add-In基本上就是实现了IDSAddIn接口的COM对象。通过这个接口,Add-in能够掌管Visual C++环境,并且执行特定的任务,例如在Visual C++环境下打开工作空间,编译工程等等。在使用Add-in方式的时候,首先需要打开Visual C++环境,然后加载add-in文件(*.dll)。 2.1.3 系统目标 本系统最终目的是为了减轻老师负担,利用计算机自动评判编程作业,替代手工检查方式。要求学生将编写好的作业交至指定文件夹,然后老师运行本系统,编译链接,运行生成目标程序,通过比较程序输出与标准答案,可以得到每个源程序的评判结果。 2.2 系统功能 详细的分析整个系统功能应该包括: 1.从文件夹抽取源程序。 2.控制Visual C++编译器编译链接源程序。 苏州科技学院毕业论文 3.以测试数据运行生成的目标程序。4.程序输出与标准输出比较。5.根据比较结果得到评判结果。 2.3 需要解决问题 1.存放C源程序的文件夹的组织方式和文件夹中C源程序的命名方式。2.程序通过何种方式提取C源程序。 3.用程序控制Visual C++编译器去编译链接C源程序。4.如何处理编译链接时出错,没有生成目标程序的C源程序。5.通过何种方式运行编译链接之后生成的目标程序。 6.在运行目标程序时,如何自动完成目标程序的输入和输出。7.对于运行目标程序时异常情况的处理。 8.采用何种方式比较输出结果和正确答案,以及如何存储比较结果。 2.4 系统流程 2.4.1 基本流程 基本上本系统是一个顺序执行的过程,系统的实现也是一步一步来的。下一个模块需要用到上一个模块的数据。系统流程图如图1-1所示。 1.系统从文件夹提取出所有的C源程序。 2.系统控制Visual C++的编译器去编译链接提取出的C源程序,得到相应的生成的目标程序(exe)。 3.系统以正确输入数据运行生成的目标程序(exe)。4.系统提取运行目标程序(exe)得到的输出数据。5.系统将输出数据与正确答案进行比较。6.根据比较结果得到评判结果。 苏州科技学院毕业论文 图1-1 系统流程图 2.4.2 具体实现 根据系统应该完成的功能,结合需要解决的问题,系统的具体实现如下: 苏州科技学院毕业论文 1.文件夹的组织方式:每一个题目设置一个文件夹,同一题目的所有编程作业(只交后缀名为*.c的文件)交至对应的唯一文件夹下。 2.每个文件夹中C源程序的命名方式:以学号命名,后缀名为*.c,这样每个文件夹中的C源程序是唯一的,在评判时能更直观的得到结果。 3.设置一个基本对话框,通过列表控件来提取选择的一个文件夹下的所有C源程序。 4.编译链接方面,Visual C++不能直接编译链接后缀名为*.c的C源程序,因此提前新建一个WIN32空白工程文件,然后将C源程序的后缀名改为*.cpp,放至空白工程中,便可以解决不能直接编译链接的问题。通过Add-in接口,控制整个Visual C++环境,打开已放入改了后缀名的C源程序的空白工程文件,然后便可进行编译链接。在这里要设置循环,对列表框中的每一个C源程序都进行编译链接。 5.循环过程中,编译链接后生成目标程序的C源程序,从空白工程文件的Debug文件夹中复制其目标程序到暂存的文件夹,并用其学号来命名生成的目标程序。然后循环继续直到所有C源程序都完成编译链接。 6.对于编译链接后没有生成目标程序的C源程序,视为编译链接错误,将此结果先一步存至评判结果处。 7.编译链接全部完成后,设置弹出一个对话框,用于输入测试数据、运行正确的目标程序,得到正确答案。同时通过列表框提取已经生成的所有以学号命名的目标程序。 8.对于编译链接正确,已经生成的目标程序,设置循环,每次循环中通过新建一个进程来运行应用程序,如果程序正常结束,则关闭进程,循环继续去运行下一个目标程序。 9.在运行目标程序时,通过管道技术来完成输入输出数据的重定向问题。10.对于目标程序运行时可能出现的种种异常情况,例如死循环等,通过设定一个时间,超出这个时间后进程强制结束来处理,进程强制结束后主循环继续,同时判定为运行时错误。 11.在开始循环运行所有目标程序之前,需要老师首先运行此题目正确程序生成的应用程序,并且输入运行数据(如无需数据输入则省略此步骤),得到正确的输出数据,存入一个编辑框。然后每次运行生成的目标程序得到输出时与编辑框中字符串比较,来得到比较结果。结果是相同或是不同。 12.最后的评判结果:没有生成目标程序,判为编译链接出错-50分;生成了目标程序,但是输出与正确答案不同,判为运行错误-60分;生成了目标程序,并且输出结果与正确答案相同,判为程序正确-100分。 苏州科技学院毕业论文 第3章 详细设计 3.1 系统模块化分 根据系统所应该完成的功能,以及考虑到使用的方便性,将系统划分为三个模块,其中包括两个基本对话框和一个函数。 3.1.1 添加文件对话框 此对话框需要完成的功能、遇到的问题及解决办法: 1.初始化:在系统运行之前删除空白工程文件中不需要的文件,清空相关的文件夹。此功能通过对话框上的一个“初始化”按钮完成。 在系统运行选择C源程序文之前,需要对相关文件夹和文件进行处理,有删除文件夹、清空文件夹、删除文件。而在Visual C++中是不能直接删除非空的文件夹的,也没有清空文件夹的函数,文件是可以通过库函数DeleteFile()直接删除的。因此这里需要自己添加函数来清空文件夹,在清空文件夹之后调用库函数RemoveDirectory()来删除已空文件夹。 2.添加文件:初始化之后,从相应的存放相同题目的文件夹中提取所有的C源程序文件(过滤器设置为只能选取后缀名为*.c的文件),将所有的文件添加至对话框的一个列表控件中,列表控件的每一行项目为C源程序文件的绝对路径。完成添加C源程序后,取得项目总数的信息存入注册表,并且以项目总数设置循环,将每个C源程序文件的绝对路径信息存入注册表,然后调用编译链接函数。此功能通过对话框的一个“添加文件”按钮和一个列表控件完成。 在添加文件时选择用列表控件的方式,这样在之后设置循环对每个C源程序文件进行操作时比较方便。同时每个项目中取的是C源程序文件的绝对路径(包括文件名),因为之后的好多操作需要文件的路径,取得学号信息时也比较方便。因为在Add-in方式下,对话框类和Add-in类之间的数据传递比较特殊,所以采用将列表控件中的相关信息存至注册表的方式,以便今后在编译链接时调用。 3.确认:新建对话框时,默认的代码,无实际功能。通过点击此按钮,得到“OK”按钮按下的消息,调用编译链接函数。通过对话框的“OK”按钮完成。 此对话框的数据输入是:文件夹中选取的所有后缀名为(*.c)的C源程序文件。 苏州科技学院毕业论文 此对话框的数据输出是:列表控件中的项目总数和列表控件每个项目中C源程序文件的绝对路径。 3.1.2 编译链接函数 此函数需要完成的功能、遇到的问题及解决办法: 编译链接:从注册表提取出添加文件对话框中列表控件的项目总数,以此设置循环。在每次循环中,首先进行初始化操作,删除空白工程文件夹中相关不用的文件和文件夹,然后通过从注册表提取出添加文件对话框列表控件的项目中的C源程序文件的绝对路径,将后缀名为*.c的C源程序文件改名为与建立空白工程时建立的关联的*.cpp文件。通过Add-in的接口,在Visual C++工作空间打开空白工程文件,此时它已经成为C源程序文件的工程文件,进行编译链接。编译链接完成之后如果在空白工程文件的Debug文件夹中找到生成的目标程序,则以此C源程序文件中的学号信息来命名此目标程序(即,此时目标程序改名为学号.exe)并且将改名后的目标程序复制到暂存所有生成的目标程序的文件夹中,以备运行时使用。而如果编译链接完成之后在空白工程文件的Debug文件夹中没有找到生成的目标程序,则视为编译链接错误,直接在结果文件夹中以学号为信息给出评判结果。处理结束后进行下一个循环直到循环结束,给出编译链接已完成的信息提示框。 首先编译链接时取用的数据是刚才添加文件对话框中列表控件中添加入注册表的信息。在每次循环开始前,都需要对test文件夹进行处理,删除Debug文件夹,删除0.cpp文件,清空result、outcome文件夹,这些问题同添加文件对话框问题中的初始化一样,都需要自己编写函数来清空文件夹和删除非空文件夹。然后因为是将C源程序文件加入空白工程文件中编译,且空白的关联cpp文件已经命名为0.cpp,所以C源程序文件在编译链接前都需要改名为0.cpp文件。之后通过Add-in,控制整个Visual C++的开发环境,首先打开工作空间,然后载入工程,之后编译链接,当然这些都是Visual C++自动完成的,相当于按下了Build按钮,这也就是使用Add-in方式的关键所在。编译链接结束后,如果在Debug文件夹中生成0.exe文件,则视为编译链接成功,且此时需要将此文件转移出来,因为如不转移,编译链接下一个C源程序文件时会覆盖此0.exe文件。 此函数的数据输入是:列表控件中的项目总数和列表控件每个项目中C源程序文件的绝对路径。 此函数的数据输出是:编译链接成功后以学号命名的存至相同文件夹下的目标程序;编译链接错误后以学号为区分信息的存至结果文件夹下的评判结果。 苏州科技学院毕业论文 3.1.3 运行/评判对话框 此对话框需要完成的功能及其解决办法: 1.输入数据、运行正确程序:弹出此对话框后,进入运行目标程序,评判阶段,首先运行此题目正确程序编译链接后生成的exe程序,如果需要输入数据才能运行的,先输入数据,得到此题目正确的输出。此功能通过“选择正确程序”按钮来选择此题目正确程序生成的exe文件,通过一个编辑框显示运行的正确exe文件路径,通过另一个编辑框来实现数据输入,通过第三个编辑框来得到运行正确exe文件后得到的数据输出,即标准答案。 在编译链接结束后,进入运行/评判阶段,首先需要运行正确的程序,这是老师之前通过编译链接正确题目程序得到的正确的exe程序。如果程序需要输入数据之后运行的,在“正确输入”编辑框中输入正确数据。通过编辑框的方式输入数据方便直观。因为这里是老师输入正确程序的正确数据输入,所以老师会确保输入数据的正确性。而如果不需输入数据即可得到输出数据的话就不用输入数据即可。之后通过CreateProcess()函数运行正确程序,这里通过重定向技术使用管道来完成自动的使用输入数据运行程序并且自动的将输出数据送入编辑框而不是在显示屏显示。在正确输出编辑框中会显示出正确的结果,这样子也便于观察。 2.选择目标文件:输入正确数据,以此数据运行正确的exe文件后,得到正确输出,即标准答案。选取暂存文件夹中所有C源程序文件编译链接正确后生成的所有以学号命名的目标程序至列表控件,列表控件项目为选取的目标程序的绝对路径。此功能通过一个“添加文件”完成选取所有目标程序,通过一个列表控件来完成添加所有目标文件。 这里也是通过列表控件来完成选择文件的工作,因为知道生成的所有目标程序在test文件夹下的result文件夹中,所以在打开文件时直接定位到此程序所在的文件夹。每个项目中同样也是放的目标程序的绝对路径(包括文件名),因为之后使用函数运行程序是需要使用程序的绝对路径。 3.运行目标文件、进行评判:完成选取所有目标程序后,以列表控件的项目总数即目标程序总数设置循环。在每次循环中,以运行正确程序的exe文件时的数据输入作为运行目标程序的数据输入,即使用正确输入编辑框中的输入数据,去运行目标程序,得到输出数据,将此输出数据与正确输出编辑框中的数据比较,这两个数据都是CString类型的。如果比较结果相同,则视为运行正确,将此结果以学号信息为区别存至结果文件夹中作为评判结果;如果比较结果不同,则视为运行时错误,也将此结果以学号信息为区别存至结果文件夹作 9 苏州科技学院毕业论文 为评判结果。完成一个目标程序的运行、评判之后,循环继续,去运行、评判下一个C源程序。直到处理完所有的目标程序,至此运行、评判结束。此功能通过一个“开始”按钮完成。 在这里运行程序时的原理和运行正确程序时的一样,正确的数据输入就采用正确输入编辑框中的数据,这样可以确保程序正确时得到的输出与正确输出相同,方便评判。一个问题是需要考虑到这里运行的是学生的程序而不是老师的正确程序,所以可能出现运行异常、死循环的进程不结束的问题,那么就会出现死机的情况,主程序运行不下去,所以这里需要设置进程运行超时的处理。因为是教学中的C源程序文件作业,相对较为简单,所以设置运行时间为4秒,如果进程运行超时还没有自动结束,则强制结束,判为运行时错误。若果进程自动结束,则于正确结果比较得到评判结果。 此对话框的数据输入是:题目正确源程序生成的exe文件,正确的测试数据输入,所有C源程序文件编译链接正确后生成的目标程序。 此对话框的数据输出是:以输入的正确测试数据运行正确程序后的正确数据输出;比较运行目标程序后的数据输出与正确数据输出,结果相同后得到的以学号信息区别的存至结果文件夹的运行正确的评判结果;比较运行目标程序后的数据输出与正确数据输出,结果不同后得到的以学号信息区别的存至结果文件夹的运行错误的评判结果。 3.2 系统模块具体实现 在这一节中,将会对各个模块中系统那个实现的难点、遇到的问题、解决的方法、重点技术问题的给出详细的说明。 3.2.1 准备工作 在开始系统设计之前,在Visual C++中新建一个Win32空白工程,这里工程名为test,然后在此空白工程中新建一个C++文件,文件名为0.cpp。以后编译链接选取的C源程序文件时则将C源程序文件改名为0.cpp并替换空白工程中的0.cpp文件即可。为防止编译链接时编译链接两次,选择组件->配置,移除Win32 Release只保留Win32 Debug。在此空白工程文件夹test中,新建一个文件夹result用于保存编译链接成功后生成的以学号命名的目标程序,新建一个文件夹outcome用于存放最后的评判信息,新建一个空白文本文件file.txt。 在每次开始使用系统之前,进行的初始化操作就包括删除Debug文件夹、删除0.cpp文件,清空result和outcome文件夹。 接着开始系统的具体功能实现阶段。首先Visual C++中新建一个DevStudio Add-in Wizard工程,工程名称为Auto,并且在CCommands->ICommands中自动 10 苏州科技学院毕业论文 添加一个函数AutoCommandMethod()。这个函数是系统的切入点。这时编译链接工程之后,生成DLL文件,加载此文件后,工具栏会添加一个按钮,此按钮实现的是AutoCommandMethod()的函数功能。系统的使用从点击这个按钮开始。 3.2.2 实现添加文件对话框 建立起DevStudio Add-in Wizard工程后,在工作空间,右击Auto Classes添加一个对话框类AddFile,即添加文件对话框。编辑此对话框,添加“初始化”、“添加文件”按钮和一个列表控件,保留默认的“OK”、“Cancle”按钮。 1.“初始化”按钮:此按钮完成的功能是删除Debug文件夹、删除0.cpp文件,清空result和outcome文件夹。在Visual C++中可以通过库函数DeleteFile()删除一个文件。 (1).在此遇见的问题是在Visual C++中不能直接删除一个不为空的文件夹以及没有现成的库函数来清空一个文件夹,因此自己添加两个函数DeleteFolder和ClearFolder,分别用于删除文件夹(不管其是否为空)和清空文件夹(清空其中的文件而保留文件夹),函数代码如下: a.DeleteFolder函数 void AddFile::DeleteFolder(CString sPath){ CFileFind ff; BOOL bFound; bFound = ff.FindFile(sPath + “*.*”); while(bFound) { bFound = ff.FindNextFile(); CString sFilePath = ff.GetFilePath(); if(ff.IsDirectory()) { if(!ff.IsDots()) DeleteFolder(sFilePath); } else { if(ff.IsReadOnly()) 苏州科技学院毕业论文 { SetFileAttributes(sFilePath,FILE_ATTRIBUTE_NORMAL); } DeleteFile(sFilePath); } } ff.Close(); SetFileAttributes(sPath,FILE_ATTRIBUTE_NORMAL); RemoveDirectory(sPath); } b.ClearFolder函数 void AddFile::ClearFolder(CString sPath){ CFileFind ff; BOOL bFound; bFound = ff.FindFile(sPath + “*.*”); while(bFound) { bFound = ff.FindNextFile(); CString sFilePath = ff.GetFilePath(); if(ff.IsDirectory()) { if(!ff.IsDots()) DeleteFolder(sFilePath); } else { if(ff.IsReadOnly()) { SetFileAttributes(sFilePath,FILE_ATTRIBUTE_NORMAL); } DeleteFile(sFilePath); 苏州科技学院毕业论文 } } ff.Close(); SetFileAttributes(sPath,FILE_ATTRIBUTE_NORMAL);}(2).完整的“初始化”按钮代码如下: { CString StrDestFile=“D: est .cpp”;DeleteFile(StrDestFile);//删除0.cpp文件 CString DirectoryDebugName=“D: estDebug”;DeleteFolder(DirectoryDebugName);//删除DEBUG文件夹 CString DirectoryResultName=“D: estresult”;ClearFolder(DirectoryResultName);//清空result文件夹 ClearFolder(“D: estoutcome”);//清空outcome文件夹 GetDlgItem(IDC_addfile_BUTTON)->EnableWindow(true);//设置“添加文件”按钮禁用,点击“初始化”后启用 GetDlgItem(IDOK)->EnableWindow(true);// “初始化”后启用 } 2.“添加文件”按钮:点击之后弹出选择文件对话框,完成选择所有的C源程序文件,然后将所有的C源程序文件添加至对话框的列表控件中,列表控件的每一行项目为C源程序文件的绝对路径。然后将列表控件中的项目总数和每个项目所指向的C源程序文件的绝对路径存入注册表,以备编译链接时使用。 (1).列表控件的初始化工作需要首先设置列表控件的属性属性->样式->查看->报告,然后在AddFile类的Message中选择WM_INITDIALOG添加函数OnInitDialog(),最后在此函数中添加代码如下: m_list.InsertColumn(0,“file”,LVCFMT_CENTER,500,-1);(2).点击“添加文件”按钮后弹出打开文件对话框,通过CFileDialog来实现,设置过滤器以便只能选择后缀名为*.c的C源程序文件,同时相关参数设置为能够选择多个文件,其代码如下: 设置“OK”按钮禁用,点击 13 苏州科技学院毕业论文 CFileDialog Dlg(TRUE,“*.c”,NULL,OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT,“C文件(*.c)|*.c|”,NULL);(3).对于选择多个文件添加至列表控件中,需要设置指针,通过GetStartPosition()函数使指针指向选取的第一个文件开始,直至最后一个文件,然后通过GetNextPathName()函数来将选取的C源程序文件的绝对路径添加入列表控件,代码如下: POSITION pos=Dlg.GetStartPosition(); while(pos!=NULL) { CString FileName=“"; FileName = Dlg.GetNextPathName(pos); m_list.InsertItem(0, ”“, 0);m_list.SetItem(0, 0, LVIF_TEXT, FileName,0, 0, 0, 0);}(4).将选取的所有C源程序文件的绝对路径存入列表控件后,通过AfxGetApp()->WriteProfileInt将列表框项目总数写入注册表,然后以项目总数设置循环,通过AfxGetApp()->WriteProfileString依次将每个C源程序文件的绝对路径写入注册表,以备编译链接时使用,其代码如下: AfxGetApp()->WriteProfileInt(”Configure“, ”count“,m_list.GetItemCount());for(int i = 0;i < m_list.GetItemCount();i++){ }(5).完整的“添加文件”按钮代码如下: { CFileDialog Dlg(TRUE,”*.c“,NULL,OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT,”C文件(*.c)|*.c|“,NULL);if(Dlg.DoModal()==IDOK) { char szRegKey[50];wsprintf(szRegKey, ”%d“, i);AfxGetApp()->WriteProfileString(”Configure“, szRegKey, m_list.GetItemText(i, 0));14 苏州科技学院毕业论文 POSITION pos=Dlg.GetStartPosition(); while(pos!=NULL) { CString FileName=”“; FileName = Dlg.GetNextPathName(pos); m_list.InsertItem(0, ”“, 0);m_list.SetItem(0, 0, LVIF_TEXT, FileName,0, 0, 0, 0);} AfxGetApp()->WriteProfileInt(”Configure“, ”count“,m_list.GetItemCount()); } 3.“OK”按钮:此按钮的代码是新建对话框时的默认代码,无实际功能,其按下的意义在于,取得按下“OK”的消息,调用编译链接函数。此消息的实现是在系统切入点AutoCommandMethod()函数出,点击工具栏按钮后弹出添加文件对话框,进行完选取C源程序文件的操作后,点击“OK”按钮,AutoCommandMethod()函数得到按下“OK”按钮的消息,然后调用编译链接函数,进入下一步工作,这里AutoCommandMethod()函数相关代码如下: AddFile AddFileDlg;//弹出添加文件对话框 if(AddFileDlg.DoModal()==IDOK)//若按下添加文件对话框的“OK”按钮 { } build();//调用编译链接函数 for(int i = 0;i < m_list.GetItemCount();i++) { char szRegKey[50];wsprintf(szRegKey, ”%d“, i);AfxGetApp()->WriteProfileString(”Configure“, szRegKey, m_list.GetItemText(i, 0)); } } 3.2.3 实现编译链接函数 首先建立编译链接函数,这个函数是在Add-in方式下建立的,目的是操控Visual C++的开发环境,实现在工作空间打开工程文件,调用Visual C++自带的 15 苏州科技学院毕业论文 编译器去编译链接工程。建立此函数的过程是:右击CCommands类下的ICommands,选择Add Method,然后方法名称为build,即建立了一个build()函数,此时函数代码为空,不实现功能。 在添加文件对话框完成所有工作后,点击“OK”按钮,发出按钮按下的消息,在AutoCommandMethod()函数中调用build()函数。 此函数要实现的功能是: 1.初始化工作,在每次编译链接C源程序文件前需要删除Debug文件夹和o.cpp文件,以保证编译链接成功后在Debug文件夹中生成的目标程序是对应当前的C源程序文件的,其代码如下: DeleteFile(”D: est .cpp“);//删除0.cpp文件 DeleteFolder(”D: estDebug“);//删除Debug文件夹 2.取得C源程序文件中的学号信息,因为C源程序文件存至列表控件中的是其绝对路径,包括路径和完整的文件名(因为在命名时有要求,所以文件名也就是“学号.c”),所以需要提取出文件名中的学号(即文件标题),学号是唯一区别标志,以便在给出评判结果时使用学号信息,代码如下: CString stillfilename;CString filetitle;CString StrFile=AfxGetApp()->GetProfileString(”Configure“, RegKey, ”“);//获取文件路径和完整文件名 stillfilename=StrFile.Right(StrFile.GetLength()-StrFile.ReverseFind('')-1);//获取文件名,带后缀 filetitle=stillfilename.Left(stillfilename.ReverseFind('.'));// 获 取文件标题(学号),即文件名去掉后缀 3.取得注册表中存入的添加文件对话框中列表控件的项目总数和每个项目中选取的C源程序文件的绝对路径,并以项目总数设置循环。每次循环中首先进行初始化操作,然后将C源程序文件改名为o.cpp文件并复制到空白工程test文件夹下。新打开一个工作空间,打开此时的test工程文件(test.dsw),最后编译链接此工程文件。 (1).取得项目总数,并以此设置循环的代码如下: int nNumWorkspaces= AfxGetApp()->GetProfileInt(”Configure“,”count“,0);for(int nWorkspaceNumber = nNumWorkspaces;nWorkspaceNumber >= 1;nWorkspaceNumber--)(2).改名复制C源程序文件,新建工作空间,打开工程文件,编译链接的 16 苏州科技学院毕业论文 代码如下: long lCount; char RegKey[50];wsprintf(RegKey, ”%d“, nWorkspaceNumber-1); CString StrDestFile=”D: est .cpp“;CopyFile(StrFile,StrDestFile,false);//拷贝并改名C文件至空白工程 CComBSTR bszWorkspacePathName = ”D: est est.dsw“; CComPtr CComQIPtr CComPtr VERIFY_OK(m_pApplication->get_Projects(&pDispProjects));CComQIPtr for(long i = 1;i < lCount+1;i++){ long lNumConfigs;CComVariant Vari = i;CComPtr OleVariant, &pWorkspace));17 苏州科技学院毕业论文 } CComPtr } VERIFY_OK(m_pApplication->Build(VarDisp));CComVariant Varj = j; CComPtr VERIFY_OK(pConfigs->Item(Varj, &pConfig));CComVariant VarDisp = pConfig;CComBSTR bszStr; VERIFY_OK(pConfig->get_Name(&bszStr));(3).在循环中遇到的问题是每次打开一个工作空间并对工程进行编译链接后没有关闭文档,从而使每次开始新循环后无法实现删除Debug文件夹和o.cpp文件,造成目标程序永远是对应第一个C源程序文件的,解决的办法是在编译链接结束后关闭文档,即工作空间,代码如下: DsSaveStatus d;VARIANT vtSaveChanges;vtSaveChanges.lVal=dsSaveChangesNo;vtSaveChanges.vt=VT_I4;VERIFY_OK(pDocuments->CloseAll(vtSaveChanges,&d));4.每次循环中,编译链接结束后,关闭工作空间,在空白工程test文件夹中的Debug下,如果能找到生成的目标程序test.exe文件,则视为编译链接正确,将此test.exe文件复制到暂存目标程序的result文件夹,并改名为以学号命名的exe文件,即学号.exe文件,作为此C源程序文件编译链接成功生成的目标程序。而如果编译链接结束后,在Debug文件夹中没有找到生成的目标程序test.exe文件,则视为编译链接错误,复制空白的文本文件file.txt并改名为以此C源程序文件的学号命名的“学号-编译错误-50”文本文件,作为评判结果存至结果文件夹outcome。这一部分代码如下: CFileFind find;18 苏州科技学院毕业论文 CFileFind findexe;if(find.FindFile(exefile)){ CopyFile(exefile,”D: estresult est.exe“,false); { } } else { CString errorfile;CString file=”D: estfile.txt“;errorfile=”D: estoutcome“+filetitle+”编译错误-50“+”.txt“;CopyFile(file,errorfile,false);} 5.编译链接完所有的C源程序文件,在result文件夹中存储所有编译链接成功后得到的以学号命名的目标程序,在outcome文件夹中存储所有编译链接错误没有得到目标程序的以学号为区别信息的评判结果之后,弹出消息提示框,提示编译链接已经结束,点击确认进入运行/评判对话框。代码如下: CString StrInfo=”编译连接结束,点击进行运行评判“;if(AfxMessageBox(StrInfo,MB_YESNO)==IDYES) { } run RunDlg;//这里run是建立的运行/评判对话框的类名 RunDlg.DoModal();CString newexefile=”D: estresult“+filetitle+”.exe“;MoveFile(”D: estresult est.exe“,newexefile);} {if(findexe.FindFile(”D: estresult est.exe“))3.2.4 实现运行/评判对话框 调用编译链接函数,对所有的C源程序文件进行编译链接,处理生成的目标文件,并且先一步给出编译链接不正确的C源程序文件的评判结果后,弹出运行/评判对话框。这就首先需要在工作空间,右击Auto Classes添加一个对话框类run,即添加运行/评判对话框。编辑此对话框,首先添加一些必要的用于说 19 苏州科技学院毕业论文 明性的静态文本,再添加编辑框一用于显示运行的正确的exe程序绝对路径;编辑框二用于输入运行正确程序的输入数据,此数据也用于作为运行所有生成的目标程序的输入数据;编辑框三用于输出运行正确exe程序后的正确输出结果,并且在运行所有的目标程序得到输出结果后与此编辑框中的正确输出结果比较以得到评判结果。另外,添加一个列表控件,添加一个“选择正确程序”按钮,一个“运行正确程序”按钮,一个“选择文件”按钮,保留默认的“OK”按钮和“Cancle”按钮。 1.“选择正确程序”按钮:在开始选择C源程序文件之前,需要老师先在Visual C++下运行一次题目正确的程序,得到正确的exe程序。之后点击这个按钮,弹出选择文件对话框,设置过滤器为只能选择exe文件,然后选择正确的exe文件,并在编辑框中显示出其绝对路径。代码如下: CFileDialog Dlg(TRUE,”*.exe“,NULL,OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT,”可执行文件(*.exe)|*.exe|“,NULL); if(Dlg.DoModal()==IDOK) { CString StrFileName=Dlg.GetPathName(); GetDlgItem(IDC_run_BUTTON)->EnableWindow(true); GetDlgItem(IDC_exe_EDIT)->SetWindowText(StrFileName); } 2.“运行正确程序”按钮:在选择了题目正确的exe程序之后,在相应的正确输入编辑框输入数据,以此数据来运行正确的exe程序,而如果程序是不需要数据输入运行后直接得到数据输出的,则也省略掉数据输入这一步。在运行正确的exe程序时,利用CreateProcess()函数来新建一个进程运行exe程序,因为是正确的程序,所以这里不设置进程运行超时强制结束。关于输入、输出数据的重定向问题,通过建立两个管道来解决,输入数据取正确输入编辑框中的数据,输出数据输出到正确输出编辑框。其代码如下: SECURITY_ATTRIBUTES sa,sa2; HANDLE hInputRead,hInputWrite;HANDLE hRead,hWrite; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; 苏州科技学院毕业论文 CreatePipe(&hRead,&hWrite,&sa,0); sa2.nLength = sizeof(SECURITY_ATTRIBUTES); sa2.lpSecurityDescriptor = NULL; sa2.bInheritHandle = TRUE; CreatePipe(&hInputRead,&hInputWrite,&sa2,0); STARTUPINFO si; PROCESS_INFORMATION pi; si.cb = sizeof(STARTUPINFO); GetStartupInfo(&si); si.hStdError = hWrite; si.hStdOutput = hWrite;si.hStdInput = hInputRead;si.wShowWindow = SW_HIDE; si.dwFlags DWORD dwWritten;UpdateData(TRUE); CreateProcess(m_exeEdit,NULL,NULL,NULL,TRUE,NULL,NULL,NULL,&si, &pi);CloseHandle(hInputRead);CloseHandle(hWrite); UpdateData(TRUE);CString put=m_inEdit+”“;//输入框输入数据,类型为CString char szInPut[20];memcpy(szInPut,put,20);//将CString类型数据输入到char中 WriteFile(hInputWrite,szInPut,strlen(szInPut),&dwWritten,NULL); char buffer[4096] = {0}; = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 苏州科技学院毕业论文 DWORD bytesRead; while (true) { if(ReadFile(hRead,buffer,4095,&bytesRead,NULL)==NULL) break; m_outEdit += buffer; UpdateData(false); Sleep(200); } GetDlgItem(IDC_add_BUTTON)->EnableWindow(true);3.“选择文件”按钮:此按钮的作用是在得到正确输出数据后,点击此按钮,弹出选择文件对话框,选择所有C源程序文件编译链接正确后生成的以学号命名的目标程序。由于这些文件都存放在result文件夹下,所以选择文件时直接定位至result文件夹,并且将选择的所有目标程序加入列表控件。列表控件的每个项目显示的是目标文件的绝对路径。列表控件的初始化工作与添加文件对话框中的列表控件相同。其代码如下: CFileDialog Dlg(TRUE,”*.exe“,NULL,OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT,”可执行文件(*.exe)|*.exe|“,NULL);Dlg.m_ofn.lpstrInitialDir=”D: estresult“;if(Dlg.DoModal()==IDOK) { POSITION pos=Dlg.GetStartPosition(); while(pos!=NULL) { CString FileName=”“; FileName = Dlg.GetNextPathName(pos); m_exelist.InsertItem(0, ”“, 0); m_exelist.SetItem(0, 0, LVIF_TEXT, FileName,0, 0, 0, 0); } } GetDlgItem(IDOK)->EnableWindow(true);4.“OK”按钮:在将C源程序文件编译链接正确后生成的所有目标程序加入列表控件后,取得列表控件项目总数,以此设置循环,每次循环中进行对一个目标程序进行如下操作: 苏州科技学院毕业论文 (1).首先从对应目标程序的绝对路径中取得文件名去掉后缀后的标题,即学号信息,作为之后给出评判结果时使用。代码如下: CString filepath=m_exelist.GetItemText(i, 0);CString filename = filepath.Right(filepath.GetLength()-filepath.ReverseFind('')-1);CString filetitle=filename.Left(filename.ReverseFind('.'));//获取文件标题,文件名去掉后缀,即得到学号信息 (2).通过 CreateProcess()函数来新建一个进程运行目标程序,运行结束后自动关闭进程。通过管道技术,建立两个管道来实现输入、输出数据的重定向问题。以正确输入编辑框中的数据作为输入数据运行目标程序,如果无需数据输入则视为输入为空;将运行后得到的数据存入一个字符串中。其代码如下: CString outcome;SECURITY_ATTRIBUTES sa,sa2; HANDLE hInputRead,hInputWrite;HANDLE hRead,hWrite; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; CreatePipe(&hRead,&hWrite,&sa,0); sa2.nLength = sizeof(SECURITY_ATTRIBUTES); sa2.lpSecurityDescriptor = NULL; sa2.bInheritHandle = TRUE; CreatePipe(&hInputRead,&hInputWrite,&sa2,0); STARTUPINFO si; PROCESS_INFORMATION pi; si.cb = sizeof(STARTUPINFO); GetStartupInfo(&si); si.hStdError = hWrite; si.hStdOutput = hWrite; 苏州科技学院毕业论文 si.hStdInput = hInputRead;si.wShowWindow = SW_HIDE; si.dwFlags DWORD dwWritten;UpdateData(TRUE); CreateProcess(m_exeEdit,NULL,NULL,NULL,TRUE,NULL,NULL,NULL, &si,&pi); CloseHandle(hInputRead);CloseHandle(hWrite); UpdateData(TRUE);CString put=m_inEdit+”“;//输入框输入数据,类型为CString char szInPut[20];memcpy(szInPut,put,20);//将CString类型数据输入到char中 WriteFile(hInputWrite,szInPut,strlen(szInPut),&dwWritten,NULL); char buffer[4096] = {0}; DWORD bytesRead; while(true) { if(ReadFile(hRead,buffer,4095,&bytesRead,NULL)==NULL) break; outcome += buffer; Sleep(200); }(3).为防止运行时出现死循环等异常情况,设置进程的运行时间为4秒,超时则强制结束进程,且判为错误。如果有输出数据能存入字符串outcome,则将outcome与正确输出编辑框的正确输出比较。如果结果相同则视为运行正确,复制空白的文本文件file.txt并改名为以此目标程序对应的C源程序文件的学号命名的“学号-运行正确-100”文本文件,作为评判结果存至结果文件夹outcome。 = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 苏州科技学院毕业论文 如果结果不同则视为运行时出错,复制空白的文本文件file.txt并改名为以此目标程序对应的C源程序文件的学号命名的“学号-运行错误-60”文本文件,作为评判结果存至结果文件夹outcome。其代码如下: if(outcome!=”“) { if(outcome==m_outEdit) {CString rightfile=”D: estoutcome“+filetitle+”运行正确--100“+”.txt“; DWORD Event; Event=WaitForSingleObject(pi.hProcess,4000); if(Event=WAIT_TIMEOUT) }//如果进程没有自动终止,4秒钟后自动关闭,并给出评判结果 */ } { DWORD exitcode=0; TerminateProcess(pi.hProcess,exitcode); CString errorfile=”D: estoutcome“+filetitle+”运行错误--60“+”.txt“; CopyFile(”D: estfile.txt“,errorfile,false);{CString errorfile=”D: estoutcome“+filetitle+”运行错误--60“+”.txt“; CopyFile(”D: estfile.txt“,errorfile,false);} } { CopyFile(”D: estfile.txt“,rightfile,false);} else else 最后给出消息提示框,提示评判结束,点击确认后退出,代码如下: MessageBox(”运行结束,请查看结果“);CDialog::OnOK();完成系统的详细设计之后,对系统进行测试,同时演示出运行时的界面,给用户以直观的使用帮助。 苏州科技学院毕业论文 第4章 测试 4.1 测试方案 鉴于本系统的目的是用于评判教学中学生编写的C源程序文件,程序的复杂度不是很高,输入输出数据也相对较为简单。所以测试数据取两组,一组的题目无需数据输入,运行后直接得到数据输出;一组的题目需要输入数据运行后才能得到输出数据。将这两组数据以唯一序号命名,存入两个不同的文件夹。为了测试出各种质量的程序的评判结果是否正确,分别选取各文件夹中的一个程序修改使之存在语法错误而无法通过编译链接,选取另一个程序修改使之语法正确能够通过编译链接但输出内容与正确输出不同。在运行这两个题目正确的C源程序得到正确的exe文件后,开始进入测试。 4.2 运行界面 4.2.1 准备系统运行 由于系统采用了Visual C++的Add-In方式,所以在开始使用系统前,需要加载程序开发编译链接后生成的DLL文件。选择Visual C++菜单栏中“工具”->“定制”->“附加项和宏文件”->“浏览”,在弹出的选择文件对话框中首先选择文件类型为Add-ins(.dll),然后找到Auto工程文件夹下Debug文件夹中的Auto.dll文件,打开,如图4-1所示,此时定制对话框中显示已加载了dll文件。 苏州科技学院毕业论文 图4-1 加载dll文件成功 点击“关闭”后,在Visual C++开发环境的工具栏中显示添加了一个新的按钮,如图4-2所示此按钮即是系统的开始使用按钮,所谓Add-In的方式,也就是把程序功能嵌入到Visual C++开发环境中。 图4-2 工具栏添加按钮 4.2.2 系统测试 1.添加文件对话框 点击此工具栏中按钮,弹出添加文件对话框,点击“初始化”,启用“添加文件”、“OK”按钮。点击“添加文件”,选择测试文件夹下的所有测试程序,这里先使用需要输入数据的源程序。添加完成后,如图4-3所示,并点击“OK“按钮。 图4-3 添加文件 2.点击“OK”按钮后,关闭添加文件对话框,调用编译链接函数build(),系统自动进行编译链接部分的工作,结束后弹出信息提示框,如图4-4所示,点击“是(Y)”后进入运行/评判对话框。 苏州科技学院毕业论文 图4-4 编译链接结束信息提示框 3.进入运行/评判对话框后,首先点击“选择正确程序”按钮,选则之前生成的正确的exe程序,然后输入正确的输入数据,这里用数字333。再点击“运行正确程序”,在正确输出编辑框得到正确的数据输出。测试程序的目的是计算一个三位数的各个位数上的数字之和,所以这里的重定向后正确输出为: Please import a number : The sum is : 9 再点击“选择文件”,选择result文件夹下的所有目标程序添加至列表控件,最后点击“OK”按钮,运行这些目标程序并且进行评判。因为文件205.c设置的是语法错误,无法通过编译链接,所以在目标程序中没有205.exe文件,如图4-5所示。 图4-5 选择正确程序,输入数据后运行,得到正确输出,再添加目标程序 运行/评判结束后,弹出信息提示框,提示所有评判工作已经完成,点击“是(Y)”后,退出运行/评判对话框,这样再点击工具栏按钮便可以进行下一条题目的评判。信息提示框如图4-6所示。 图4-6 评判结束信息提示框 苏州科技学院毕业论文 最后,在outcome文件夹中可以查看评判结果,结果是一个个与C源程序文件对应的空白文本文件,文件名是对应的评判信息。空白处右击->查看->详细信息,便可以直观的得到评判信息了。结果如图4-7所示。 图4-7 评判结果 经过检查,评判的结果与预先设置的文件质量相同,能够区分出编译链接错误,运行错误和运行正确。在测试另一组无需数据输入题目的C源程序文件后,结果也正确。 苏州科技学院毕业论文 结论 经过设计、开发、测试,整个系统得以实现。首先整个系统在Visual C++开发环境下能够通过加载DLL文件顺利开始运行。在C语言源程序题目需要数据输入和无需数据输入的两种情况下,都可以完成对于C语言源程序的基本评判工作,能够结合学生上交题目的学号给出相应的直观明了的评判结果,减轻老师负担。整个系统的运行过程力求操作最简,按照顺序和提示一步一步进行最简单的点击、输入等各种,即可以完成对于大量C源程序文件的评判各种。最终完成的系统达到了预期的目标。 在整个系统的开发过程中,遇到的技术难点有三个。第一就是Add-in的开发方式,这是之前完全没有接触过的一个方式,不同于一般的对话框方式。这个方式在变量的调用,函数的调用方面都具有自身的特点,例如平时使用的MessageBox()函数,在Add-in方式下需改为AfxMessageBox(),这些难点是参考MSDN中的相关说明后基本解决。第二个就是对于编译链接成功后得到的目标程序如何运行,在Visual C++中运行一个exe程序有多种方法,例如可以通过ShellExecute()、WinExec()来运行,但是考虑到需要在运行时重定向输入输出数据,最后决定通过新建一个进程来运行目标程序,即使用CreateProcess()函数来运行。第三个问题是关于如何在运行目标程序时重定向输入输出,即如何自动的载入输入数据和将输出数据重定向到一个字符串中以方便和正确答案比较,最后这个问题是通过使用通道来解决。 通过这次毕业设计的过程,让自己在各方面得到提高。首先,大大增加了自己对于编程的兴趣。其次,使自己更加熟悉的C语言、C++语言,以及Visual C++的开发环境,学习了以前没有深入学习过的知识。第三,提高了自己解决自己之前未接触过的问题的能力,通过请教老师、同学,系统而深入的研究MSDN,网上查找资料等等方式,在解决遇到的各种问题时,都是行之有效的方法。第四,提高了自己攥写论文的能力,系统开发出来,还要能够说明自己开发的整个过程,从接到任务到完成任务。第五,提高了自己对于软件工程的认识,以前不太在意的测试工作现在也认识到是软件开发过程中的重要问题。 经过上网查询,现有的类似软件比较多的是带有自动评判的考试系统,哈尔滨工业大学在C语言课程的上机实验中采用了一个C语言自动评判,但是具体信息并未公开。其他的是一些带有自动评判功能的测试软件,多用于大型软件的测试,一些代码测试则是直接给出错的代码行。对于课题要求的题目,再来开发还是比较有意义的,一来这方面的C源程序自动评判还是比较少的,二来本课题的目的是尽量减少老师的工作,采用尽量简单的操作来完成大部分重 30 苏州科技学院毕业论文 复而烦琐的工作。在完成C源程序自动评判系统得开发后,对于开发类似的源程序评判系统也提供了不少经验。 当然,本系统还不尽完善,评判的是实际教学中学生学习C语言时编写的较为简单的源程序,输入输出相对而言也较为简单,对于评判结果,也还没有做到进一步细致的区分各种情况,暂时只有三种结果。还有就是对于一种特殊情况不能发现,即能够编译链接,程序的内容其实是错误的,但碰巧输出结果与正确答案相同,这里本系统判为正确,而实际上是错误的。今后将不断完善系统的功能,争取达到更加准确的评判C源程序文件,给出更细致的评判结果。 苏州科技学院毕业论文 致 谢 在本次毕业论文的撰写过程中,严迪新老师给予了我极大的帮助和支持。在论文开题阶段,他针对设计任务给予了很多建议。在程序设计阶段,对于出现的问题,反复实验,帮助我解决。论文初稿完成后,他又多次提出修改意见和独到见解。这些都使我受益匪浅。在此,我谨对严迪新老师的悉心指导和帮助表示由衷的感谢! 苏州科技学院毕业论文 参 考 文 献 [1] 罗斌.Visual C++编程技巧精选500例.北京:中国水利水电出版社,2005 [2] Charles Wright.Visual C++程序员实用大全.北京:中国水利水电出版社,2001 [3] Kris Jamsa.C/C++/C#程序员实用大全-C/C++/C#最佳编程指南.北京:中国水利水电出版社,2005 [4] AI Steven Clayton Walnum.标准C++宝典.北京:电子工业出版社,[5] Dale Rogerson.COM技术内幕.北京:清华大学出版社,1993 [6] Microsoft公司.MSDN Library Visual Studio 6.0 版 2005 苏州科技学院毕业论文 附录A 外文参考文献(译文) 文献一 Visual C++基础 第一章 句柄和消息技术内幕 也许你希望直截了当开始研究代码,但事实上并非如此。的确,Windows编程势不可挡。先让我们了解一下Wimdows是如何工作的。所有编程的骨架都是响应和发送消息。什么是消息(message)?消息就是一个指定事件的32比特数。例如,若移动鼠标,就会往活动窗口上发送一条消息(此消息定义为WM_MOUSEMOVE)。若按下某个键,也会往活动窗口上发送一条消息(此消息定义为WM_KEYDOWN)。若改变窗口大小,同样会往活动窗口发送一条消息(此消息定义为WM_SIZE)。了解大概情况了吧? 那么,这些消息到哪里去了呢?它们将排队等候,最终将有窗口把它们从队列中取出,并做出相应的反应。例如,当窗口获取WM_MOVE消息后,它更改窗口的坐标,并在屏幕上重绘窗口。 下面再谈谈句柄(handle)。Windows具有很强的面向对象特性。Windows对象有很多,譬如桌面、读取所使用的程序等等。在非面向对象语言中,程序员如何区分这些东西呢?使用句柄。句柄是引用不同Windows对象的方式。可以使用Windows的句柄、文件的句柄、分配内存的句柄、图像的句柄等等。可以将这些句柄看作指针。必须用某种方式创建这些句柄。当不再使用它们时,应当销毁它们。如果不销毁它们,最终将导致资源泄漏(resource leak)。资源泄漏将导致系统崩溃。所以,务必确保在适当的时候销毁不再使用的句柄。 现在将消息与句柄联系起来。假如有一个窗口,且拥有该窗口的一个句柄(称做一个HWND)。命名句柄为your_HWND。因为其他窗口刚刚从该窗口上移走,操作系统希望重绘窗口。Windows传递如下所示消息: PostMessage(your_HWND, WM_PAINT, 0,0); 这个函数通过句柄your_HWND给窗口发送了一条绘制消息。最后两个参数用作消息的额外信息。暂时不必深究它们的具体细节。 现在,应用程序中有一个函数用一个庞大case语句来处理所有消息。例如: void HandleTheMessage(long Message){ switch(Message)34 苏州科技学院毕业论文 { case WM_PAINT: DrawWindow(); break; case WM_KEYDOWN: break; //等等 } } 以上就是Windows的大致工作过程。了解这些原理后,就可以开始讨论MFC了。 第二章 C++精髓 如果你希望使用Microsoft Visual c++,在确实熟悉c++的情况下会大有帮助。 所有的操作都与类有关。如果你习惯于使用一般的C语言,只有实际运用类一段时间后,才能真正体会类的用途。在开始使用VC++之前,先介绍一些有关类的必备知识。 在很大程度上,类(class)就是一种结构。下面用一个实例来加以说明,这个类表达一条直线。在.h文件中,定义类如下: class CLine { int m_nX1;int m_nY1;int m_nX2;int m_nY2;public: // 结构函数 CLine();CLine(int x1, int y1, int x2, int y2); 苏州科技学院毕业论文 // 析构函数 ~CLine(); // 设置直线数据 void SetPoints(int x1, int y1, int x2, int y2); // 绘制直线 void Draw();} 先简单说说命名约定。类名字一般以C开头,成员变量名字通常以m_为前缀。于是按微软的方式,用一个字母表明其数据类型。在名字中,所有新单词的第一字母大写。不要使用下划线以及类似的字符。之所以在此推荐微软标准(称做匈牙利标记法,Hungarian notation),是因为这一标记法已被广泛接受,而且非常容易阅读。看到m_pPoint这样的名字,就会想到这是一个指向某个点的类的其中一个成员变量(它是一个指针)。看到fData这样的名字,就会想到它是一个浮点值。 言归正传,再回到类的主题上。int变量是直线的终点。注意,它们在公有(public)部分的前面。这意味着,使用这个类的程序员不能直接操纵这些变量。它们并非用于公有目的。public语句下的函数用于公有用途。头3个称做构造(constructors)函数。在创建新CLine类的时候调用这些函数。下面是调用它们的一些例子: // 下面调用 CLine()CLine MyLine; // 这是一个指向 CLine 类的指针 CLine *pMyLine; //下面调用 CLine()pMyLine = new CLine; //这是一个指向 CLine 类的指针 CLine *pMyLine;// 下面调用 CLine(int x1, int y1, int x2, int y2)pMyLine = new CLine(0,0,10,10);36 苏州科技学院毕业论文 //下面调用CLine(int x1, int y1, int x2, int y2)CLine MyLine(0,0,10,10); 所有这些代码生成一条直线。一些代码将它初始化为缺省设置,另外一些代码复制坐标。在C++中,关键字new用于创建一个新的对象,这与C中的ma.Uoc一样。对于创建的对象在不用时需要调用delete,如同在C中调用flee释放它们一样。使用如下代码可以分配一个包含100个整数的数组: // 下面调用CLine()CLine MyLine; // 这是一个指向CLine类的指针 CLine *pMyLine; // 下面调用CLine()pMyLine = new CLine; // 这是一个指向CLine类的指针 CLine *pMyLine;// 下面调用CLine(int x1, int y1, int x2, int y2)pMyLine = new CLine(0,0,10,10); // 下面调用CLine(int x1, int y1, int x2, int y2)CLine MyLine(0,0,10,10); 所有这些代码生成一条直线。一些代码将它初始化为缺省设置,另外一些代码复制坐标。在C++中,关键字new用于创建一个新的对象,这与C中的malloc一样。对于创建的对象在不用时需要调用delete,如同在C中调用free释放它们一样。使用如下代码可以分配一个包含100个整数的数组: // 整数的指针 int *pNumbers; // 为其中的100个项开辟内存 pNumbers = new int[100]; // 将首元素设置为0 37 苏州科技学院毕业论文 pNumbers[0]=0; // 将最后一个元素设置为99 pNumbers[99]=99; // 释放内存 delete [] pNumbers; 请注意delete之后的[]。它告诉程序删除整个数组。如果是删除pNumbers,则只释放首元素的内存。因而可能会泄漏内存。内存泄漏在忘记释放内存时发生。如果耗尽了计算机的所有内存,最终导致计算机的崩溃。 很抱歉,又跑题了。请再回过头来考察CLine的构造函数。在创建一条新直线时,将自动调用这些构造函数,如下所示: CLine::CLine(){ m_nX1=0;m_nX2=0;m_nY1=0;m_nY2=0;} CLine::CLine(int x1, int y1, int x2, int y2){ m_nX1=x1;m_nX2=x2;m_nY1=y1;m_nY2=y2;} 注意以上的函数声明与常规C函数很相似,只是在函数名字的前面放置类名以及一对冒号(CLine:)。构造函数的一个区别是,它们没有返回值。对于析构函数也是如此。析构函数是删除CLine或越出作用域时自动调用的函数,例如: // 这是Cline类的一个指针 CLine *pMyLine; // 下面调用CLine()pMyLine = new CLine;38 苏州科技学院毕业论文 // 清除这个类占用的内存,并调用~CLine()delete pMyLine;{ // 下面调用CLine()CLine MyLine;} // 这个'}'表征程序中的有MyLine效域的结束 // 调用~CLine()(因为MyLine越出了作用域) 对于本例中的类,~CLine()不需要做任何事。不过,有时可能希望在此放置一些清除代码,如删除类中的一些已分配内存。因为在这里不需要它做任何事,所以,函数代码为空: CLine::~CLine(){ // 不做任何事 } 下面,再添加其他两个函数。 void CLine::SetPoints(int x1, int y1, int x2, int y2){ m_nX1=x1;m_nX2=x2;m_nY1=y1;m_nY2=y2; return;} void CLine::Draw(){ // 这里使用的是伪码 // 它们是绘制直线的操作系统函数 MoveTo(m_nX1, m_nY1);LineTo(m_nX2, m_nY2);39 苏州科技学院毕业论文 return;} 如何调用这些函数呢?下面列举两个例子,其中一个例子使用指针。另一个例子不使用指针: CLine *pLine = new CLine(0,0,10,10);pLine->Draw();delete pLine; CLine MyLine;MyLine.SetPoints(0,0,10,10);MyLine.Draw(); 以上介绍的是类中的代码。现在可以将这个类用于其它类中了。假设有一个类CSquare,其中有4个CLine类: class CSquare { CLine m_LineTop;CLine m_LineLeft;CLine m_LineBottom;CLine m_LineRight;//等等 } 也可以使用已有的CLine类创建自定义类。在Visual C中可以事半功倍地完成。假如想在程序中绘制一条直线,而且希望利用现有的直线类,但这个类缺少一个重要的特征:不能通过它设置颜色。在这种情况下,不必从头开始创建新类。只需继承类CLine即可达到目的。实现代码如下所示: class CColorLine : public CLine { public: void Draw(long color);}; 这些代码的作用是什么?通过这个类,我们拥有其他类的所有功能,现在也可以使用其他Draw()函数来设置颜色。 cpp代码如下所示: 苏州科技学院毕业论文 void CColorLine::Draw(long color){ // 这里使用的是伪码 // 它们是绘制直线的操作系统函数 SetColor(color); CLine::Draw();return;} 现在,其他类具有所有功能,不过。同时还添加了一个额外的函数,这就是Draw。但这个名字与其他Draw同名。没有关系,Cpp的智能性足以知道是否调用Draw(color)来使用新函数;如果调用Draw(),它将使用旧函数。代码的奇怪部分是CLine::Draw(),它告诉程序来调用基类的Draw函数。因此,我们不必再次编写LineTo和MoveTo代码。够酷吧?因此可以完成如下所示任务: CColorLine MyLine;MyLine.SetPoints(0,0,10,10); // 假设0为黑色,下面将绘制一条黑色直线 MyLine.Draw(0); 当然,以上代码中省略了其他一些重要的代码,如定义运算符、覆盖函数、虚函数、受保护和私有成员……在下面的课程中,我还会继续补充、完善这段代码。作为起步,知道这些已经足够了。 第三章 Visual C++ IDE和工作区 Windows编程很复杂。也不要轻信别人的一面之词。微软为我们提供了高效率的编程IDE,它称做Developer Studio。它能处理所有的编译和连接,提供帮助,自动生成大量代码,并且提供一种“可视化”设计环境,使你轻松地得到精巧的对话框。 在开始使用Developer Studio之前,必须掌握一些相应的知识。最重要的是,掌握使用在线帮助(online help)。Windows函数如此之多,你会发现,至少在头几个月50%的时间里,你都在浩瀚的帮助文档中“游荡”。如果你安装有Visual c++6.0,则需要安装MSDN来获取帮助。为了方便学习,请安装MSDN。如果你安装的是版本4.2或5.0,则不必单独安装其他帮助文档,可以直接使用内置的帮助系统。 然后是最重要的组合键Ctrl+W。这一组合键能调出Class Wizard。它能为 41 苏州科技学院毕业论文 程序员在项目中插入大量代码。它处理所有连接函数与Windows所发送消息的代码。 有时也用到Resource视图。它用于设计对话框。可以在对话框或其他框架上面放置按钮。通过几次鼠标点击,就可以布局好整个应用程序。然后,使用Class Wizard,可以完成项目的50%。剩下的工作是填充Class Wizard生成的函数,来处理按钮点击消息等。 下面介绍如何启动一个新程序,从一段实际代码开始讲解。打开菜单“File”,并选择“New”。然后,打开“Projects”标签,并选择“MFC AppWizard”。输入应用程序的名字,并按下“OK”键。此时,惟一重要的选项是第一个选项。这是需要构建的应用程序的类型。最简单的是Dialog Based。你可以尝试使用其他类型,不过,这里首先尝试Dialog Based。选择所有缺省值,之后再按下“Next”和“OK”按钮。最后得到若干文件,不过,我们暂时不讨论这些文件。 打开Resource视图,并在对话框上面放入一些按钮和其他控件。然后进行适当的调整。感觉良好后,通过菜单“Build”生成项目,并运行之(我总是按组合键Ctrl+F5,生成并运行项目)。应当生成项目所有文件并运行它们。瞧,就这么简单——现在,应用程序已经完成,并开始运行了。 运用IDE的最后一个技巧。如果你右击菜单栏,马上会弹出一个对话框。选择其中 的“Customize”。这一功能非常有用。通过从菜单栏中拖拽按钮,可以生成一个自定义菜单,其中包括所有必需的按钮。你甚至可以更改菜单布局(例如,把“File”、“New”命令作为工具栏上的按钮)。在“Customize”对话框的“Build”类别中有一个非常有用的控件。这就是下拉列表.它用于选择现行配置。将它拖到主具栏上,调整它的大小,直到外观谐调为止。在我的屏幕上只有一个工具栏,它包含如下按钮:Save、Save All、Compile、Build、Stop Build,Rebuild All、Set Active Configuration,Find,Find in Files,Show View、Show Output Window、Set Break Point、Remove Break Point、Tile Windows 和FindinHelp。 第四章 MFC基础 现在可以开始编写程序了吧?不,还未完全就绪。你并不希望我讲解如何编写一个极其简单的“hello world”应用程序,对吧?如果你想充分利用Visual C++,就必须使用微软基础类库(MFC)。这些类非常强大,因为它们包含第一章中谈到的所有句柄。现在,对你来说,最重要的是类CWnd。它封装了需要窗口句柄(HWND)的函数。还记得我曾经提到的PostMessage函数吗? PostMessage(your_HWND, WM_PAINT, 0,0);42 苏州科技学院毕业论文 好了,下面来探讨Windows类,并调用它的成员函数。 MyCWnd.PostMessage(WM_PAINT, 0, 0); 以上代码做的是相同的事情,但现在我们不必跟踪窗口句柄。不过,也不要被迷惑了,它们仍然存在,我们仍然可以使用它们。它们现在只是类的成员变量。与CWnd关联的窗口句柄是成员变量m_hWnd。可以按如下方式调用旧的发送消息: ::PostMessage(MyCWnd.m_hWnd, WM_PAINT, 0,0); 这些冒号(::)用于通知MFC:此时正调用旧方式的函数版本。也可以在大部分时间不使用它们,但我将它们放在这里,是为了让你在Microsoft的代码中看到它们时,不至于感到困惑。Cwnd类是其他一些类(如CButton和CDialog)的基类,这些类的名字具有自解释性。从CButton也可以访问窗口句柄(你一定感到惊奇,窗口包含如此多的对象:滚动条、编辑框、树视图、桌面等等)。 下一个最重要的类就是CWinApp,不过,不会在太多的地方明确使用它。这个类是将来所有MFC应用程序的骨架。正是这个类承担着主要的繁重工作。你的程序也将有一个CWinApp类,在创建这个类时,开始执行程序。构建CWinApp时调用的主函数是InitInstance()。在这个函数中,创建窗口,并启动应用程序。将CwinApp中的InitInstance()函数看作C程序中的main()函数。 最后介绍一个非常有用的MFC,而且以后必定会用到,这就是CString。它是微软的支持类之一,用于简化字符串的操纵。因为CString覆盖了许多常规运算符,如=和+,所以可以进行如下运算: CString strMyString;strMyString=”May the Force be with you“;strMyString+=” young Jedi.“ printf(”%s“, strMyString);//输出将是”May the Force be with you young Jedi.“; 第五章 基于对话框的应用程序 前几课还没有生成对话框应用程序,这~课作进一步的介绍,让你对对话框应用程序的工作过程有一个大概的了解。我认为,对话框应用程序是最简单的。在IDE中,运行菜单命令“File”、“New”、“。Projects”、“MFC AppWizard(exe)”,并输入项目名字。点击“Next”按钮。选择“Dialog Application”作为应用程序的类型,然后点击“Finish”按钮。接着,进入“File”视图。在此视图中可以看到自动创建的源文件。现在可以编译和运行这个应用程序。所有这些文件起什么作用?一切行为均归结到CWinApp派生类以及Dialog派生类(它从CWnd派生)。观察 43 苏州科技学院毕业论文 按项目命名的源文件。在此可以找到InitInstance()函数。在该函数中,可以看到构建了一个对话框类,它被设置为应用程序的“主窗口”,而且它通过DoModal()函数来显示。一旦退出对话框应用程序,DoModal()函数返回,而且隐藏该对话框。InitInstance()返回FALSE,同时,应用程序结束。那么,DoModal()是什么昵? 有两种方式创建一个对话框:有模式(modal)和无模式(modeless)。对于有模式对话框,在用户按下“OK”、“Cancel”或“Close”按钮之前,程序一直挂起。而对于无模式对话框,以点击按钮或进行应用程序的其他操作时,对话框可以保持打开。有模式对话框的一个例子是讨厌的错误提示框,在这个框上而有一个“OK”按钮。在此,我只介绍此类对话框。要创建一个有模式对话框,只需要调用对话框的DoModal()函数。它要么返回:IDOK,要么返回IDCANCEL。,根据退出对话框的具体方式而定。在内部,对话框将调用OnInitDialog(),这是初始化对话框变量的理想地方。如果创建一个对话框应用程序,则可以注意到,已经创建了一个缺省的对话框类和资源。类的文件名为项目名字加上“Dlg”。 虽然现在还不能生成一个应用程序,但必须告诉你如何在对话框上放置一些有用的东西。首先,打开“Resource”视图,然后,在编辑器中打开对话框。右击对话框,选择“Properties”。确保选中了“Visible”复选框。如果没有选中,则不能看到对话框(请记住这一点,以后还会常常提醒你)。在这里,可以更改对话框的标题,也可以更改其他属性。 现在,把对话框上的某个地方拖入一个按钮控件。右击这个控件,并选择“„Properties”。可以更改按钮的ID,使其描述更贴切,如IDC_SHOWMESSAGE。也可以把按钮上的文本更改成描述更贴切的文本,如Show Message。至此,就得到了一个按钮。但这个按钮不会执行任何动作。这不难解决。按下组合键Ctrl+W,打开Class Wizard。点击第一个标签。此时,在左边的列表中可以看到对话框的名字和按钮的ID。如果选择对话框的名字,则在右边可以看到通过Class Wizard可以添加的代码的所有函数和消息。可以找到曾经讲过的WM_PAINT和其他所有消息。如果选择按钮的ID,可以看到按钮发送的消息。双击消息CLICK,并接受缺省的函数名字。它将出现在底部的列表中。现在,双击底部列表中的函数。立即切换到cpp文件中,光标所在处正是需要填充代码的地方。让我们做一些简单的事,只添加下面代码: AfxMessageBox(”Stupid Text“); 编译、生成并运行该应用程序(按Ctrl+F5即可)。按下此按钮后,就会弹出一条消息。有许多Afx函数是有用的。我认为消息框是其中最有用的。它属于即时的反馈。 苏州科技学院毕业论文 以上过程的确很简单。现在,你已经明白了如何添加消息处理器(像按钮点击一样),但至少需要处理一些更重要的信息,才能让对话框有用,这就涉及如何使用MFC中的自动数据处理类。为了更好地描述这一点,请再次回到代码。再次打开Resource编辑器,这一次在对话框上添加一个编辑框。再次右击编辑框,赋予它一个恰当和友好的ID。按组合键Ctrl+W打开Class Wizard。这一次要使用第二个标签。在这里添加与对话框中控件关联的成员变量。双击编辑框的ID,然后可以给项目添加一个变量了。给该变量起一个名字,如m_strMessage,因为它是用于消息框的字符串。确保在底部选择数据类型“CString”。点击“OK”按钮完成变量的添加,再次点击“OK”按钮退出Class Wizard。 按如上方式在对话框上添加成员变量时,运用如下调用,可以把值设置为对话框中控件的值: UpdateData(TRUE); 同样,也可以更改对话框中显示的数据,以表示变量的值,运用的调用如下; UpdateData(FALSE); 将以上代码运用到应用程序中,以此结束这一节的讲解。请定位到OnInitDialog()函数的结尾处,放入如下两行: m_strMessage = ”Initial text"; UpdateData(FALSE); 然后。进入点击按钮时调用的函数中,用如下代码行替换AfxMessageBox()代码行: UpdateData(TRUE); AfxMessageBox(m_strMessage); 好了,全部完成了。以上所做的是,通过UpdateData(FALSE)将编辑框中的初始文本设置为“Initial text”。然后,点击按钮时,消息框中显示该文本,因为通过UpdateData(TRUE)从对话框中获取了该文本。 通过摸索、查阅帮助(或者阅读优秀参考书),可以掌握如何使用其他控件。若不寻求帮助,最难掌握的其中一个控件是滚动条。如果你使用其中一个控件,必须处理对话框的滚动消息。这里只是稍作提示。 文献二 线程 引言 这篇文章介绍多线程编程(multithreading programming)方面的概念。大部分讲解基丁Windows NT环境。大部分材料来自于MSDN。 苏州科技学院毕业论文 何谓线程 Windows NT的基本执行单元是线程(thread)。这意味着,Windows NT不执行进程(process),相反,它执行的是线程。线程是多任务性运行于单个栈中的代码序列。每个线程有自己的代码序列,它以并发的方式得到执行。因此,线程有助于获得并发处理。Windows NT规划器控制系统中所有运行线程的执行。在Windows NT中,每个线程均有自己的优先级(priority),编号从0到31,具体优先级根据Windows NT对每个线程的规划而定。优先级编号越高,线程的优先级越高。进程可能只有一个线程,也可能拥有多个线程。单个进程最少具有一个线程。进程可以创建线程,也可以终止线程。在任何时刻,线程处于以下3种状态之一: ·运行状态(running)——线程正在其中一个CPU上运行。 ·等待状态(waiting)——线程处于挂起状态,直到指定的事件发生才能运行。·就绪状态(ready)——线程准备运行,但当前无可用的处理器。 就绪和运行状态的线程称做可运行线程(runnable thread)。可运行线程可以使用空闲的CPU周期。系统上当前运行的线程数受可用CPU的限制。在单处理器系统上,在某一时刻运行的线程数量等于1。在多处理器系统上,在某一时刻运行的线程数等于处理器的数量。 当一个CPU空闲时,OS找出具有最高优先级的就绪线程,从队列首部删除该线程,并运行它。这个过程称做上下文切换(context switch)。每次上下文切换都会耗费一定的开销。因此,随着线程数量的增加,系统会变得迟缓。Windows NT是一个抢占式多任务系统。这意味着,线程运行的时间段长短由OS决定,而不是由线程自身决定。如果有足够的CPU时间可用,而没有其他更高优先级的就绪线程,OS就允许运行线程完成它的定额时间,它称做线程时间片(thread quantum)。一个线程完成线程的时间片后,OS在最高优先级队列首部寻找就绪线程,并运行它。如果线程完成了它的线程时间片,同时有一个更高优先级的就绪线程,则OS抢占当前运行的线程,而运行更高优先级的线程。为什么要使用多线程 使用多线程的原因有多种,最重要的原因是提升应用程序的性能。这样的应用程序包括后台执行某些任务的应用程序,这些任务与前台(用户接口)执行的任务无关。在这样的应用程序中,可能有一个工作线程(worker thread),它执行后台任务,还有一个主线程(main thread),它执行与用户接口相关的任务。 也许你想知道this从何而来,它在非静态成员函数上隐式提供。因为线程函数定义没有this,所以需要在类定义中将函数定义为静态。有时,多线程应用程序设计比不具备多线程的设计逻辑性更强、复杂性更 46 苏州科技学院毕业论文 少。控制温度的应用程序以固定间隔从两个高温计(测量温度的仪器)获得测试的温度。这个应用程序廊该在确定的时间内反应这些测量(实时)。运行两个线程,从每个高温计接收测量值,并回送响应,这更合乎逻辑。 因此,确定应用程序是采用多线程,还是采用单线程,这是一个重要的设计准则。但没有现成的规则可照搬照套。更复杂的情况 在多线程应用程序中,如果线程间相互独立,则设计、开发和调试这样的应用程序容易得多。但在实际应用中,情况要复杂得多。线程间总是相互依赖,而且它们共享公共资源。这就涉及到线程同步(thread synchronization)方面的内容了。 苏州科技学院毕业论文 附录B 外文参考文献(原文) Literature one Visual C++ BASE Lesson 1: Behind the Scenes with Handles and Messages Though you think you want to dive right into the code, you really don't.Windows programming is overwhelming at first.Let's take a quick look at how Windows works.The backbone of all of your programming will be responding to and sending messages.What are messages? Messages are simply a 32bit number designating some event.Example: You move the mouse, a message(defined as WM_MOUSEMOVE)is 'posted' to the active window.You press a key, a message(WM_KEYDOWN)is 'posted' to the active window.You resize the window, a message(WM_SIZE)is 'posted' to the active window.Get the picture? Now where do these messages go? They get queued up and a window eventually takes them out of the queue and reacts to them.For instance when a window gets the WM_MOVE message it changes the coordinates of the window and redraws it on the screen.(continued) Let's move on to Handles.Windows is very much object oriented.You have several window objects(like the desktop, the program your reading this with, etc...).How does the programmer distinguish all of these things in an non-object-oriented language? He uses handles.Handles are a way to reference different windows objects.You can have handles to windows, handles to files, handles to allocated memory, handles to images, etc.You can think of them as pointers.You must create them some how.And when you are done with them, you must destroy them.If you don't you will end up with what is called a resource leak.This could bring your system to a grinding halt.So take care to always make sure they are destroyed at sometime.Now lets tie these two things together.Say you have a window.You will have a handle to it(called an HWND).Lets name your handle your_HWND.The operating 48 苏州科技学院毕业论文 system wants to tell you to redraw your window because it was just uncovered by some other window.Windoze passes you a message like this: PostMessage(your_HWND, WM_PAINT, 0,0);This function posts a paint messages to the window with handle your_HWND.The last two parameters are used for extra information about the message.Don't worry about them for now.Now your application will have a function with a big case statement in it to handle all of the messages.For example: void HandleTheMessage(long Message){ switch(Message){ case WM_PAINT: DrawWindow(); break; case WM_KEYDOWN: break; //etc...} } Ok that is basically how windows works under the hood.That should be enough to get you going when we start talking about MFC.Lesson 2: C++ Essentials If you want to use Microsoft Visual C++, it helps a ton if you really know C++.Everything is about classes.If you are used to plain C, you won't really see the big deal with classes until you use them for a while.Let's review what you need to know about classes to get started with VC++.49第二篇:c语言源程序
第三篇:C语言源程序至可执行文件步骤[范文模版]
第四篇:C语言编程 “画圆”源程序
第五篇:C语言源程序的自动评判毕业设计论文