您好,欢迎来到九壹网。
搜索
您的当前位置:首页关于C语言的void main

关于C语言的void main

来源:九壹网
关于C语言的void main()

作者:admin 发布时间:2012-02-13 17:41 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣15

很多人甚至市面上的一些书籍,都使用了void main( ),其实这是

错误的。C/C++中从来没有定义过void main( )。C++之父Bjarne Stroustrup在他的主页上的FAQ中明确地写着The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C.( void main( )从来就不存在于C++或者C)。下面我分别说一下C和C++标准中对main函数的定义。

一、 C语言中的main()

在C中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的经典巨著The C programming Language 2e(《C 程序设计语言第二版》)用的就是main( )。不过在最新的C99标准中,只有以下两种定义方式是正确的:

int main(void)

int main(int argc, char *argv[])

(参考资料:ISO/IEC 99:1999 (E) Programming languages ? C 5.1.2.2.1 Program startup)

当然,我们也可以做一点小小的改动。例如:char *argv[]可以写成char **argv;argv和argc可以改成别的变量名(如intval和charval),不过一定要符合变量的命名规则。

如果不需要从命令行中获取参数,请用int main(void);否则请用int main(int argc, char *argv[])。

main函数的返回值类型必须是int,这样返回值才能传递给程序的调用者(如操作系统)。

如果main函数的最后没有写return语句的话,C99规定编译器要自动在生成的目标文件中(如exe文件)加入return 0;,表示程序正常退出。不过,我还是建议你最好在main函数的最后加上return语句,虽然没有这个必要,但这是一个好的习惯。注意,vc6不会在目标文件中加入return 0;,大概是因为vc6是98年的产品,所以才不支持这个特性。现在明白我为什么建议你最好加上return语句了吧!不过,gcc3.2(Linux下的C编译器)会在生成的目标文件中加入return 0;。

二、 C++中的main()

C++98中定义了如下两种main函数的定义方式:

int main( )

int main(int argc, char *argv[])

参考资料:ISO/IEC 14882(1998-9-01)Programming languages ? C++ 3.6 Start and termination

int main( )等同于C99中的int main(void);int main(int argc, char *argv[])的用法也和C99中定义的一样。同样,main函数的返回值类型也必须是int。如果main函数的末尾没写return语句,C++98规定编译器要自动在生成的目标文件中加入return 0;。同样,vc6也不支持这个特性,但是g++3.2(Linux下的C++编译器)支持。

三、 关于void main()

在C和C++中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把main函数定义成void main(void)。然而这是错误的!main函数的返回值应该定义为int类型,C和C++标准中都是这样规定的。虽然在一些编译器中,void main可以通过编译(如vc6),但并非所有编译器都支持void main,因为标准中从来没有定义过void main。g++3.2中如果main函数的返回值不是int类型,就根本通不过编译。而gcc3.2则会发出警告。所以,如果你想你的程序拥有很好的可移植性,请一定要用int main。

不要用“我的老师告诉我这么做是对的”之类的话来为自己开脱;老师们总是习惯犯错误(teachers have a bad habit of being wrong)。写安全的,合乎标准的代码,大家就可以专注于你程序中其它的问题而不是在这种规范方面的东西上浪费时间。

应当指出:在某些系统中,若程序使用void main定义或没有return值,则可能导致堆栈异常从而导致系统故障。(详见后面英文部分)

四、返回值的作用

main函数的返回值用于说明程序的退出状态。如果返回0,则代表程序正常退出;返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。下面我们在winxp环境下做一个小实验。首先编译下面的程序:

int main(void)

{

return 0;

}

然后打开附件里的“命令提示符”,在命令行里运行刚才编译好的可执行文件,然后输入“echo %ERRORLEVEL%”,回车,就可以看到程序的返回值为0。假设刚才编译好的文件是a.exe,如果输入“a && dir”,则会列出当前目录下的文件夹和文件。但是如果改成“return -1”,或者别的非0值,重新编译后输入“a && dir”,则dir不会执行。因为&&的含义是:如果&&前面的程序正常退出,则继续执行&&后面的程序,否则不执行。也就是说,利用程序的返回值,我们可以控制要不要执行下一个程序。这就是int main的好处。如果你有兴趣,也可以把main函数的返回值类型改成非int类型(如float),重新编译后执行“a && dir”,看看会出现什么情况,想想为什么会出现那样的情况。顺便提一下,如果输入a || dir的话,则表示如果a异常退出,则执行dir。

五、那么int main(int argc, char *argv[], char *envp[])呢?

这当然也不是标准C/C++里面定义的东西!char *envp[]是某些编译器提供的扩展功能,用于获取系统的环境变量。因为不是标准,所以并非所有编译器都支持,故而移植性差,不推荐使用——除非你的程序是专门设计用于工作在特定的环境中而且需要获取系统的环境变量。

在C语言有一定基础之后,怎么样能够

更快的成长?

作者:admin 发布时间:2012-02-13 18:49 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣0

这个问题比较常见,通常在程序语言有了一定基础之后,成长最快的

方法就是多编程序,没有其他任何捷径,在实践中碰到问题/排查问题,提高程序的实现能力和调试能力,在实践中自然而然将各种知识融会贯通,在实现程序的过程中注意体会每个问题的解决方法以及每个Bug的排除方法,实践能力提高了,程序设计的技巧和能力也就提高了.

祝大家的程序越写越好!

写好C程序的10条秘籍

作者:admin 发布时间:2012-02-15 18:22 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣16

神乎其技,惟C程序,功到自成,十大建议!

人围观

1. 汝应频繁催动lint工具,据其语法声明修习内力,此事皆因lint之思虑决断实远在君上。

2. 不可依随NULL指针,如若不然,混沌痴颠必俟君于彼岸。 3. 纵有天赋大智慧,知晓其事无碍,汝亦当尽数强制挪移函数参数为原型所期之数据类型,以免一时疏忽,致使数据类型向汝讨还血债。 4. 若头文件未于函数原型之中声明返回值类型,汝当亲为此事,更须谨慎再三,以防不测降临汝身。

5. 汝须亲核字符串、数组操作之越界与否。古之圣人有言: 尝祈门人对答“然也”,不意门人答曰“吾了然于胸无须多虑尽请宽心他日趋庭必当重谢”――所期者短,所获者长,此于数组,实最险要处也。 6. 若函数声明内提及,于异常时将返回错误代码云云,汝当谨慎校核该返回值。或有校核语句使汝之代码增大数倍,令汝之手指因敲键而痛楚莫名之事,汝亦当如此。不然,汝固以为此异常之事难得一见,上苍亦必借此惩戒汝之傲慢。

7. 汝应研习库函数,不当亲笔重写之。如是,汝之代码必短小易读,汝之心境必清爽恬淡。

8. 纵汝不愿,汝亦应借括号、缩进之属,使汝之代码间架清晰,可为后者借鉴。汝之大智慧施于决疑解难则可,施于敷设谜团、淆乱文体则万万不可。 9. 外部标识符之前六字符当与众不同。此律法看似粗陋,和者寥寥,然其效用自能延续永远。汝若不从此言,他日,汝欲连接程序于旧作之上时,必抓耳挠腮、蓬头垢面,狼狈之相尽现矣。

10.或有族类,大言炎炎,云“普天之下,莫非VAX”。于此等异端,汝当摒弃之、断绝之、远避之。更有异人,笃信魔道,以为纵汝所用电脑频繁换代,汝之程序亦能长久适用。汝亦不可与此类恶人来往,谨记谨记

C语言中史上最愚蠢的Bug

作者:admin 发布时间:2012-02-15 18:59 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣8

本文来自“The most stupid C bug ever”,很有意思,分享给大家。

我相信这样的bug,就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。

首先,作者想用一段程序来创建一个文件,如果有文件名的话,就创建真正的文件,如果没有的话,就调用?tmpfile()?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。

else if (code == 200) { // Downloading whole file

/* Write new file (plus allow reading once we finish) */ g = fname ? fopen(fname, \"w+\") : tmpfile(); } 但是这个程序,只能在Unix/Linux下工作,因为 Microsoft 的?tmpfile()的实

现?居然选择了 C: 作为临时文件的存放目录,这对于那些没有管理员权限的人来说就出大问题了,在Windows 7下,就算你有管理员权限也会有问题。所以,上面的程序在Windows平台下需要用不同的方式来处理,不能直接使用Windows的tmpfile()函数。

于是作者就先把这个问题记下来,在注释中写下了FIXME:

else if (code == 200) { // Downloading whole file

/* Write new file (plus allow reading once we finish) */ // FIXME Win32 native version fails here because

// Microsoft’s version of tmpfile() creates the file in C: g = fname ? fopen(fname, \"w+\") : tmpfile(); } 然后,作者觉得需要写一个跨平台的编译:

FILE * tmpfile ( void ) { #ifndef _WIN32 return tmpfile(); #else

//code for Windows; #endif }

然后,作者觉得这样实现很不好,会发现名字冲突,因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() –

w32_tmpfile,然后,在Windows 下用宏定义来重命名这个函数为tmpfile()。(注:这种用法是比较标准的跨平台代码的写法)

#ifdef _WIN32

#define tmpfile w32_tmpfile #endif

FILE * w32_tmpfile ( void ) { //code for Windows; }

搞定!编译程序,运行。靠!居然没有调用到我的w32_tmpfile(),什么问题?调试,单步跟踪,果然没有调用到!难道是问号表达式有问题?改成if – else 语句,好了!

if(NULL != fname) { g = fopen(fname, \"w+\"); } else {

g = tmpfile(); }

问号表达式不应该有问题吧,难道我们的宏对问号表达式不起作用,这难道是编译器的预编译的一个bug?作者怀疑到。

现在我们把所有的代码连在一起看,并比较一下:

能工作的C语言代码

#ifdef _WIN32

# define tmpfile w32_tmpfile #endif

FILE * w32_tmpfile ( void ) { code for Windows;

} else if (code == 200) { // Downloading whole file

/* Write new file (plus allow reading once we finish) */ // FIXME Win32 native version fails here because

// Microsoft’s version of tmpfile() creates the file in C: //g = fname ? fopen(fname, \"w+\") : tmpfile(); if(NULL != fname) { g = fopen(fname, \"w+\"); } else {

g = tmpfile(); } }

不能工作的C语言代码

#ifdef _WIN32

# define tmpfile w32_tmpfile #endif

FILE * w32_tmpfile ( void ) { code for Windows;

} else if (code == 200) { // Downloading whole file

/* Write new file (plus allow reading once we finish) */ // FIXME Win32 native version fails here because

// Microsoft’s version of tmpfile() creates the file in C: g = fname ? fopen(fname, \"w+\") : tmpfile(); }

也许你在一开始就看到了这个bug,但是作者没有。所有的问题都出在注释上:

/* Write new file (plus allow reading once we finish) */ // FIXME Win32 native version fails here because

// Microsoft’s version of tmpfile() creates the file in C:

你看到了最后那个C:吗?在C中,“” 代表此行没有结束,于是,后面的代码

也成了注释。这就是这个bug的真正原因!

而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码,所以,那段能工作的代码成了:

/* Write new file (plus allow reading once we finish) */

// FIXME Win32 native version fails here because Microsoft’s version of tmpfile() creates the file in C: //g = fname ? fopen(fname, \"w+\") : tmpfile();

if(NULL != fname) { g = fopen(fname, \"w+\"); } else {

g = tmpfile(); }

我相信,当作者找到这个问题的原因后,一定会骂一句“妈的”!我也相信,这个bug花费了作者很多时间!

最后,我也share一个我以前犯的一个错。

我有一个小函数,需要传入一个int* pInt的类型,然后我需要在我的代码里 把这个int* pInt作除数。于是我的代码成了下面的这个样子:

float result = num/*pInt; „.

/* some comments */

-x<10 ? f(result):f(-result); 因为我在我当时用vi编写代码,所以没有语法高亮,而我的程序都编译通过了,但是却出现了很奇怪的事。我也不知道,用gdb调式的时候,发现有些语句直接就过了。这个问题让我花了很多时间,最后发现问题原来是没有空格导致的,TNND,下面我用代码高亮的插件来显示上面的代码,

float result = num/*pInt; .... /* some comments */

-x<10 ? f(result):f(-result); Holly Shit! 我的代码成了:

float result = num-x<10 ? f(result):f(-result);

妈的!我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼

对于C语言,该如何选择进程与线程

作者:admin 发布时间:2012-02-17 21:33 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣1

人围观

进程vs线程,如何选择?

我们编写程序,到底是采用多线程还是多进程?这里是有区别的,采用不同的机制能够获得的效率也不一样。如何选择适合我们自己的程序的机制呢?下面是一些常见的选择的看法,不过也只是提供给大家参考参考,具体设计的时候还是要自己处理。

一个程序里面的所有的线程都在同一个运行空间中执行。而一个程序的子进程则是运行在另外的执行空间中的,这里是通过调用了exec函数来实现的。

同一个进程中的某个线程的故障可以影响其它的线程,因为所有的线程共享同一个虚拟内存空间以及其他资源。例如,某个线程对没有初始化的指针进行写操作,就可能影响其它的线程。而一个出了问题的进程是不会影响其它的进程的,因为它们分别在不同的进程空间进行自己的操作。

创建新的进程需要进行内存的拷贝操作,这就额外的增加了系统负担,而线程则不需要这个拷贝过程。不过由于现在的操作系统的实现是仅仅当内存需要改变的时候才拷贝改动的部分,所以这里的影响相对还是比较小的。

线程通常用在某些需要比较好的同步操作的场合。例如,某个问题可以分解为多个几乎对等同步处理的任务的话,则是用线程是很好的选择。进程则适合应用在不需要严格的同步的场合。

线程之间共享数据是很方便的,因为不同的线程本来就是共享同样的存储空间。(然而这里就要非常仔细的处理竞争的情况。)而不同进程之间共享数据则需要使用一些ipc机制,例如管道、共享内存、套接字等等

三款C语言编程软件对比【优缺点】

作者:admin 发布时间:2012-02-19 22:20 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣0

常用的C语言编程软件有Visual Stdio 6.0、Tubor C和Win-TC等

等,笔者下面就三款软件的优点和缺点做一下简单地对比。

1. Visual Stdio 6.0:一般简称VC(版本是6.0),这款软件是使用最多的一款C语言编程软件,有简装版、绿色版、企业版等各个不同的版本,可以满足不同人的需求。

注:企业版的安装有点困难。

2. Tubor C:小巧快速,但是界面是DOS风格的,不好看,并且不习惯! 3. Win-TC:体积比较小,界面也漂亮,但是有一些用户反应再编程的过程中无法输入汉字。

C语言的应用范围和发展前途简介

作者:admin 发布时间:2012-02-19 22:38 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣6

C一般用来底层开发,如操作系统,嵌入式开发,或者要求效率,高

可移植性的地方。C对人要求很高,程序员要考虑的地方太多。他的特点就是每一个字节都可以精确控制,不象C++,编译器为你自动加的东西太多,效率也就低了。

windows 就是用它来开发的,linux很多程序也用它来写。

例如:用C语言编写简单的接口程序.在当今,流行的编程软件种类繁多,它们编程方便、易于维护,但是在与硬件直接打交道和编制系统软件时却束手无策,于是C语言就有了用武之地。C语言作为汇编语言与高级语言之间的一种过渡语言,兼有汇编语言的高效和高级语言的方便。

在通讯中,为了保证行运安全可靠,标准的串行口必须具有许多握手信号和状态信息。这是因为通讯的各个计算机CPU速度不一样(这会导致“错帧”)以及发送机发送数据速度比接收机接收速度快(这会导致“过冲”)。为解决这个问题,我们采用一个简单的握手信号,即发送机每次仅发送半个字节(低4位)的数据,而另外半个字节(高4位)则用来传送信息。我们可以对信息位(高4位)进行如下简单的编码:

0H:发送的是新的半个字节数据 1H:重新发送上次传送错误的数据 2H:文件名结束 3H:文件结束

这样,每当发送机发送一个字节以后,就等待接受机发回送信号,这回送信号就是发送机发送过来的那个字节。发送机接收到回送信号后,把它与刚发送的字节相比较,如果相同,就发送新的半个字节,否则就重新发送。新数据与旧数据通过信息位来区分。下面就是我用C语言编写控制串行口的程序。

我们以一个发送文件的程序为例,介绍一下用C语言实现对接口的控制。用C语言编写简单的接口程序源代码:

#include \"dos.h\" /*c的特点在于只要有头文件,就能和任意文件接上*/ #include \"stdlib.h\" #include \"stdio.h\" #define PORT 0

void SendFile(char fname); /* 发送文件*/ void Send(int s); /*发送一个字节*/

void SendFileName(char fname); /*发送文件名*/ void ReceiveFile(); /*接收文件*/

void GetFileName(char f); /*接收文件名*/

void InitPort(int port,unsigned char para); /*初始化端口*/

void SendPort(int port,char c); /*端口发送*/ int ReadPort(int port); /*读端口字节*/ int CheckState(int port); /*检查端口状态*/ int Receive(int port,int G); /*接收一个字节*/ main(argc,argv) int argc;

char *argv[]; {

if(argc<2){

printf(\"Please input R(receive) or S(sent) parametre:\"); exit(1); }

InitPort(PORT,231);

if(argv[1]=='S') /*检查选择的有效性*/ SendFile(argv[2]); else if(argv[1]=='R') ReceiveFile(); else{

printf(\"Error parament.Please input again.\"); exit(1); } }

void SendFile(fname) char *fname; {

FILE *fp; int ch,s;

if(!(fp=fopen(fname,\"rb\"))){

printf(\"Can't open the file.\\n\"); exit(1); }

SendFileName(fname); do{

ch=(int)getc(fp); if(ferror(fp)){

printf(\"Error reading file.\\n\"); break; }

s=ch%16; /*取文件中一个字节的低4位*/ Send(s);

s=ch/16; /*取文件中一个字节的高4位*/ Send(s);

}while(!feof(fp));

s=46; /*发送文件结束信息*/

Send(s); Send(s); fclose(fp); }

void Send(s) int s; {

int G;

SendPort(PORT,s);

G=ReadPort(PORT); /*等待握手信号*/ if(s!=G) s=s+16; do{

SendPort(PORT,s);

G=ReadPort(PORT);/*等待握手信号*/ }while(s!=G); }

void SendFileName(fname) char *fname; {

int s,ch;

printf(\"Now transmit the file.Please wait...\"); while(*fname){ ch=(int)fname++;

s=ch%16; /*取文件名中一个字节的低4位*/ Send(s); s=ch/16;

Send(s); /*取文件名中一个字节的低4位*/ }

s=32; /*发送文件名结束标志*/ Send(s); Send(s); }

void ReceiveFile(){ FILE *fp; char ch;

int G1,G2,G3; char fname[15]; GetFileName(fname);

printf(\"Receiving file %s.\\n\remove(fname);

if(!(fp=fopen(fname,\"wb\"))){

printf(\"Can't open output file.\\n\"); exit(1);

}

/*循环为检测每次接受的数据是否为新数据,如果不是,*/ /*则用此次接收的数据覆盖上次接收的数据*/ G1=ReadPort(PORT); G2=Receive(PORT,&G1); do{

G3=Receive(PORT,&G2);

ch=(char)(G1%16+G2*16);/*恢复分开的数据,组合高4位和低4位*/ putc(ch,fp); if(ferror(fp)){

printf(\"\\nError writing file.\"); exit(1); }

G2=Receive(PORT,&G3); G1=G3;

}while(G1/16!=48);

printf(\"\\nTransmit finished.\"); fclose(fp); }

int Receive(port,G) int port,*G; {

int GM;

SendPort(port,*G); GM=ReadPort(port); if(GM/16==0) return GM;

else if(GM/16==1){ do{ *G=GM;

SendPort(port,GM); GM=ReadPort(port); }while(GM/16==1); }

return GM; }

void GetFileName(f) char *f; {

int G1,G2,G3; char ch;

G1=ReadPort(PORT); G2=ReadPort(PORT); do{

G3=Receive(PORT,&G3); ch=(char)(G1%16+G2/16); *f=ch; *f++;

G2=Receive(PORT,&G3); G1=G3;

}while(G1/16!=32);

printf(\"File name transmit finished.\\n\"); }

void InitPort(port,para) int port;

unsigned char para; {

union REGS reg; reg.x.dx=port; reg.h.ah=0; reg.h.al=para; int86(0x14,?,?); }

void SendPort(port,c) int port; char c; {

union REGS reg; reg.x.dx=port; reg.h.al=c; reg.h.ah=1;

int86(0x14,?,?); if(reg.h.ah&128){

printf(\"\\nSend mistakes!\"); exit(1); } }

int ReadPort(port) int port; {

union REGS reg;

while(!(CheckState(port)&256)){

if(kbhit()){/*如端口长期无数据可人为终止等待*/ printf(\"Press any key to exit.\"); getch(); exit(1); } }

reg.x.dx=port; reg.h.ah=2;

int86(0x14,?,?); if(reg.h.ah&128){

printf(\"\\nRead mistake!\"); exit(1); }

return reg.h.al; }

int CheckState(port) int port; {

union REGS reg; reg.x.dx=port; reg.h.ah=3;

int86(0x14,?,?); return reg.x.ax; }

以上程序可传送各种格式的文件,也有一定的自动纠错能力,但对于异常情况的处理能力比较弱,读者可以自己改进。由于篇幅,对于中断14H的功能、入口参数及返回参数的意义请读者自己查有关资料。 **********************************

附录:现在大多数串行口都遵循RS-232标准,以下是最常用的RS-232信号: 名称 针号 含义

RTS 4 Request to send(请求发送) CTS 5 Clear to send(清除发送)

DSR 6 Data set ready(数据设备准备好)

DTR 20 Data terminal ready(数据终端准备好) TXD 2 Transmit data(发送数据) RXD 3 Receive data(接收数据) GRD 7 Ground(接地)

---------------------------------------------------------------- 附:经站长调试后在TC2.0下通过的代码

#include \"dos.h\" #include \"stdlib.h\" #include \"stdio.h\" #define PORT 0

void SendFile(char *fname); /* 发送文件*/ void Send(int s); /*发送一个字节*/

void SendFileName(char *fname); /*发送文件名*/ void ReceiveFile(); /*接收文件*/

void GetFileName(char *f); /*接收文件名*/

void InitPort(int port,unsigned char para); /*初始化端口*/ void SendPort(int port,char c); /*端口发送*/ int ReadPort(int port); /*读端口字节*/ int CheckState(int port); /*检查端口状态*/ int Receive(int port,int *G); /*接收一个字节*/ main(int argc,char *argv[]) {

if(argc<2){

printf(\"Please input R(receive) or S(sent) parametre:\"); exit(1); }

InitPort(PORT,231);

if(*argv[1]=='S') /*检查选择的有效性*/ SendFile(argv[2]); else if(*argv[1]=='R') ReceiveFile(); else{

printf(\"Error parament.Please input again.\"); exit(1); } }

void SendFile(char *fname) {

FILE *fp; int ch,s;

if((fp=fopen(fname,\"rb\"))==NULL) {

printf(\"Can't open the file.\\n\"); exit(1); }

SendFileName(fname); do{

ch=(int)getc(fp); if(ferror(fp)){

printf(\"Error reading file.\\n\"); break; }

s=ch%16; /*取文件中一个字节的低4位*/ Send(s);

s=ch/16; /*取文件中一个字节的高4位*/ Send(s);

}while(!feof(fp));

s=46; /*发送文件结束信息*/

Send(s); Send(s); fclose(fp); }

void Send(s) int s; {

int G;

SendPort(PORT,s);

G=ReadPort(PORT); /*等待握手信号*/ if(s!=G) s=s+16; do{

SendPort(PORT,s);

G=ReadPort(PORT);/*等待握手信号*/ }while(s!=G); }

void SendFileName(fname) char *fname; {

int s,ch;

printf(\"Now transmit the file.Please wait...\"); while(*fname){ ch=(int)fname++;

s=ch%16; /*取文件名中一个字节的低4位*/ Send(s); s=ch/16;

Send(s); /*取文件名中一个字节的低4位*/ }

s=32; /*发送文件名结束标志*/ Send(s); Send(s); }

void ReceiveFile(){ FILE *fp; char ch;

int G1,G2,G3; char fname[15]; GetFileName(fname);

printf(\"Receiving file %s.\\n\remove(fname);

if((fp=fopen(fname,\"wb\"))==NULL) {

printf(\"Can't open output file.\\n\");

exit(1); }

/*循环为检测每次接受的数据是否为新数据,如果不是,*/ /*则用此次接收的数据覆盖上次接收的数据*/ G1=ReadPort(PORT); G2=Receive(PORT,&G1); do{

G3=Receive(PORT,&G2);

ch=(char)(G1%16+G2*16);/*恢复分开的数据,组合高4位和低4位*/ putc(ch,fp); if(ferror(fp)){

printf(\"\\nError writing file.\"); exit(1); }

G2=Receive(PORT,&G3); G1=G3;

}while(G1/16!=48);

printf(\"\\nTransmit finished.\"); fclose(fp); }

int Receive(port,G) int port,*G; {

int GM;

SendPort(port,*G); GM=ReadPort(port); if(GM/16==0) return GM;

else if(GM/16==1){ do{ *G=GM;

SendPort(port,GM); GM=ReadPort(port); }while(GM/16==1); }

return GM; }

void GetFileName(char *f) {

int G1,G2,G3; char ch;

G1=ReadPort(PORT); G2=ReadPort(PORT); do{

G3=Receive(PORT,&G3); ch=(char)(G1%16+G2/16); *f=ch; *f++;

G2=Receive(PORT,&G3); G1=G3;

}while(G1/16!=32);

printf(\"File name transmit finished.\\n\"); }

void InitPort(port,para) int port;

unsigned char para; {

union REGS reg; reg.x.dx=port; reg.h.ah=0; reg.h.al=para; int86(0x14,?,?); }

void SendPort(port,c) int port; char c; {

union REGS reg; reg.x.dx=port; reg.h.al=c; reg.h.ah=1;

int86(0x14,?,?); if(reg.h.ah&128){

printf(\"\\nSend mistakes!\"); exit(1); } }

int ReadPort(port) int port; {

union REGS reg;

while(!(CheckState(port)&256)){

if(kbhit()){/*如端口长期无数据可人为终止等待*/ printf(\"Press any key to exit.\"); getch(); exit(1); } }

reg.x.dx=port; reg.h.ah=2;

int86(0x14,?,?); if(reg.h.ah&128){

printf(\"\\nRead mistake!\"); exit(1); }

return reg.h.al; }

int CheckState(port) int port; {

union REGS reg; reg.x.dx=port; reg.h.ah=3;

int86(0x14,?,?); return reg.x.ax; }

C语言关键字大全【共32个】

作者:admin 发布时间:2012-02-19 22:45 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣54

C语言一共有32个关键字,如下所述:

auto :声明自动变量

short :声明短整型变量或函数 int: 声明整型变量或函数

long :声明长整型变量或函数 float:声明浮点型变量或函数 double :声明双精度变量或函数 char :声明字符型变量或函数 struct:声明结构体变量或函数 union:声明共用数据类型 enum :声明枚举类型

typedef:用以给数据类型取别名 const :声明只读变量

unsigned:声明无符号类型变量或函数 signed:声明有符号类型变量或函数 extern:声明变量是在其他文件正声明 register:声明寄存器变量 static :声明静态变量

volatile:说明变量在程序执行中可被隐含地改变

人围观

void :声明函数无返回值或无参数,声明无类型指针 if:条件语句

else :条件语句否定分支(与 if 连用)

switch :用于开关语句 case:开关语句分支 for:一种循环语句 do :循环语句的循环体

while :循环语句的循环条件 goto:无条件跳转语句

continue:结束当前循环,开始下一轮循环 break:跳出当前循环

default:开关语句中的“其他”分支 sizeof:计算数据类型长度

return :子程序返回语句(可以带参数,也可不带参数)循环条件

C语言和C++的区别【完整版】

作者:admin 发布时间:2012-02-19 22:49 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣10

C和C++的关系:就像是win98跟winXP的关系。C++是在C的基础上

增加了新的理论,玩出了新的花样。所以叫C加加。

C和C++的区别:

1. C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。

2. C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。 下面我们一步一步来分析C++与C的不同:

一、类,类对于初学者,它是一个累赘。类的封装使得初学者对程序产生厌倦,感到不适和麻烦。

二、引用,引用是C++中最好尽量不要用它,除非万不得已。引用对于初学者就更容易产生混淆,不知道哪个是引用,哪个是变量。

三、函数的重载,初学者学函数的重载好像没什么坏处,但是,这会使初学者潜意识里对C语言的变量类型的重要性产生淡化,要记住C语言是对变量类型最敏感了的,变量的类型在C语言里的重要性是不言而喻的。

四、流操作符,和上面同样的道理,使得对变量类型的重要性产生淡化,有时会产生使初学者莫名其妙的结果。

五、操作符重载,典型的高级应用,初学者可能根本用不着,这个东东会让他们

觉得C++很难,门槛高,看不懂。

六、继承,以及虚函数,看起来深奥,实用价值很低。还有些东东我就不发表评论了,如:new,delete操作符等 七、误区:以问答形式:

问:C++是面向对象化的而C是面向过程化的?

答:第二对,第一问错,C++并非完全面向对象化,真正的面向对象化的语言恐怕只有Java才算得上。

问:C++能实现C所不能的功能吗? 答:至少我还没有发现

问:学了C再学C++有障碍吗?比如程序设计思想 答:至少我还没有看见谁有此症状。 问:学了C再学C++又要重头开始吗?

答:不,C++下可以实现C语言的一切功能。 问:我学完了C一定还要学C++才能编程吗? 答:完全没必要。

问:C++比C好在哪里? 答:更加符合软件工程学

问:学完了C再学C++是不是很容易?

答:那要看你是不是真正的学完了C语言。

C与C++的最大区别:在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”,而就语言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,错!算法是程序设计的基础,好的设计如果没有好的算法,一样不行。而且,“C加上好的设计”也能写出非常好的东西。

对语言本身而言,C是C++的子集,那么是什么样的一个子集?从上文可以看出, C实现了C++中过程化控制及其它相关功能,而在C++中的C(我称它为“C+”),相对于原来的C还有所加强,引入了重载、内联函数、异常处理等等玩艺儿,C++更是拓展了面向对象设计的内容,如类、继承、虚函数、模板和包容器类等等。 再提高一点,在C++中,数据封装、类型这些东东已不是什么新鲜事了,需要考虑的是诸如:对象粒度的选择、对象接口的设计和继承、组合与继承的使用等等问题。

所以相对于C,C++包含了更丰富的“设计”的概念,但C是C++的一个自洽子集,也具有强大的功能,同样值得学习

几点学习建议:

1.基本概念很重要。无论学C,还是学C++,基本概念都是第一位的,也是比较困难的,但只有把握了基本概念才能把握整体脉络,才能居高临下。

2.C是C++的子集,它的基本概念和设计方法相对比较容易理解,初学者可从它入手。

3.如果要学好C++,建议初学者最好别在如VC,BCB平台下写程序,那种自动化的代码生成,花花绿绿的界面,会让你手足无措。最好先找一片空地(unix,

dos),从头做起,写几个大点的程序,数个回合,再到VC,BCB下看看,你会轻松得很。在我看来,学好C/C++是成为VC,BCB高手的必由之路。

4.不要妄想速成,必须得一个byte,一个bit的去抠,尽量搞清楚每一个问题。

如何学好C语言

作者:admin 发布时间:2012-03-30 16:21 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣14

前言:两个原因促使我写下这篇文章,第一就是作为过来人根据我自

己的经验我知道编程的初学者一般都很兴奋也都有种困惑,一方面对运行在计算机(注:不特别指明都指个人微型计算机)的五花八门如精彩的游戏程序,多姿多彩的网站等等漂亮的视窗程序所着迷,另一方面大部分的初学者(如计算机专业的低年级学生)都是从简单命令行程序“Hello world!”开始入手而且很长一段时间都无法接触到日常经常使用的视窗程序,由此好奇者有之,迷惑者有之等等,更遗憾的是也有少部分人因为困惑对编程逐渐失去兴趣。第二就是今天妹妹看C程序设计语言(潭浩强的那本,相信大部分学过C语言的人都听过)碰到一个例题没有看懂,而作为计算机专业毕业的老哥自然义不容辞地承担起了讲解的任务,在噼里啪啦的大说之后,老妹终于弄懂了例题程序的意思,但仍然不过瘾,竟然问起我如何学好C语言,弄得我心一惊,这里我还是有点自知之明的,因为我知道我并不是什么大师,甚至自己对计算机世界也经常是一片茫然,不过看着她那“崇拜”的眼神,不说点什么似乎有点不大好,只好硬着头皮把自己以前的一些理解讲了一番。所以既然知道有这个需要,尽管自己见识浅薄,也抱着“区区陋见假如能够对后学者能够有所帮助那是再好不过了,即使有所偏颇当当笑话看看能有助于大家饭后生活也是美德一件”的心情就写下了这篇文章。

我是很乐意将计算机(程序设计)语言跟人类语言做类比的,在我看来学习程序设计语言就象学习外语一样,通过计算机语言与计算机交流就如同在使用外语与老外交流一样。通常学习语言比如英语有单词,语法,语义三个部分组成,单词是构成语言的最基本的要素,语法告诉我们单词如何构成语句,语义则告诉我们单词构成的语句的意思,类似的程序设计语言中也是有[1] “单词”,“语法”,“语义”三部分组成,其中跟人类语言一样,“单词”“语法”是基础,而为达到交流的目的“语义”是整个程序设计语言的重点。当然相对人类语言(以英语为例)而言程序设计语言(以C语言为例)显然要有简单得多的组成,所以这里我们要首先确立自己对学好程序设计语言的信心。下面试着加以说明,其中可能有些类比不太适当,不过好象也差不多吧,你自己也可以试着举出更好的例子以使自己更容易明白,我这里就抛砖引玉吧。

1、基础

先从“单词”谈起,在英语中就是由26个英文字母以及26个英文字母构成的单词组成,学习语言我们首先应该记忆的就是“单词”,26个英文字母A,B,C,„„然后就是令人头痛的英文单词,记忆的过程是痛苦的至少我学英语时最恼火的地方就是反复记忆老是忘记的单词,类似的在C语言中我们可以作这样的类比,C

语言的单词是由26个英文字母、数字(0,1,2„„9)、运算符以及其他特殊符号组成,所以相比而言学习C语言是多么的幸福。

“语法”,在英语中是如常见构词法、时态、常用句型、特殊用法、固定用法等等(呵呵,不是语言专家如有兴趣可以参考英语语法书籍),仅拿构词法来说动词后面加er就变成名词(work-worker),名词后面加y就变成形容词(wind-windy)等十多种变换还要记忆些特殊变换,复杂且繁琐,假如算上时态、句型等那就更复杂了。在C语言中也同样也存在有语法,比如标识符、常量、表达式、数据定义、函数定义、变量初始化、语句构成、预处理命令等大致8种语法,如标识符[2]可由字母、数字以及下划线组成,且必须以字母或下划线开头,区分大小写,另外在不同的编译器中字符数目有不同的规定,一般为7个字符。不过这里有些可能会被忽略的就是书写格式,在C语言中书写格式也是需要被注意的,因为格式也可以当作C语言的语法项目,如语句是以分号‘;’作为结束的标志的而不是以换行作为结束标志,其实英语也有要求比如什么时候该使用句号什么时候该使用逗号等等只不过我们日常要求并不注意,而且也可以看的懂所以不太较真。

综上所述我们要学好一门程序设计语言首先就要象学习英语一样先熟记至少要熟悉这门语言的“单词”和“语法”然后才能谈得上使用,俗话说“万丈高楼平地起”,学好C语言的必要前提就是先打下坚实的C语言基础,所以潭先生的书作为一本很好的教材,我建议是不能丢的,而且还要反复看,至少刚开始我学习的时候上机机会不多,就是反复看懂看明白这本书,有些东西该记的还是要记尤其是一些重要的语法比如标识符、数据类型、语句的特点、函数的栈式结构,指针的意义等等。

最后始终记住这点在没有实践之前,先打下坚实的理论基础吧,毕竟理论是实践的先行,理论可以更好的指导实践。即使再低点的要求至少要先熟悉理论吧,毕竟“摸着石头过河”感觉还是盲目以及痛苦的。

2、重点

“语义”即语句的意思、含义。同人类语言一样,程序设计语言最重要的目的还是“交流”,所以写一段没有任何作用的代码就如同说一句没有任何意思的话是没有意义的,在这一点上两者是共同的,至于在其他某些方面两者也有惊人的相似之处,试举一例吧,比如说可以有不同的表达表示同一个意思,如微型计算机同个人计算机就是同一个意思,在C语言中比如要让变量i自增1,可以有i=i+1或者i+=1等等。不过与人类语言相比,程序设计语言毕竟是与计算机交流,交流对象计算机与人类的不同也决定了程序设计语言与人类语言还是有所区别,甚至可以说是存在巨大的“鸿沟”,这也就是学习程序设计语言的重点。

与人类相区别,计算机最大的不同之处在于没有“自己”的“思维”,没有象人类那样丰富的感情,所以在交流过程中对于人类的跳跃性的思维,对于人类的“幽默”无法理解也没有丝毫的兴趣。比如说对与同样一句话“Do it”,在不

同的场景不同的说话语气,对10个人来说就会有10种不同的回答,有的人可能会认为这是合理的要求,有的人可能会认为这可能是个玩笑而不与理睬,有的人则可能认为语气要求有些过分而感到非常愤怒„„,但对所有的计算机来说则是严格执行。不过也正是由于计算机“机械”“笨着”的可爱而深得人们的喜爱并被广泛使用,因为对我们来说,计算机就象一个忠实的士兵一样在执行我们的命令并且作为指挥者,当然我们希望计算机在合适的时间干切当的事情。而要达到这个目的,很凑巧有点类似于社会的“权利与义务”法则一样,我们也有自己的义务那就是下达明确而又正确的“命令”。

首先明确,即我们要在与计算机交流时“语义”必须明确,否则计算机将无所适从,因此作为交流工具的程序设计语言的“语义”也必须是能够明确无二义的。当然程序设计语言的设计者已经注意到这一点,在设计时就回避了这个问题,所以我们——程序设计语言的使用者——程序员也就无须过多关注这点,也就是说使用何种程序设计语言(C,Basic,Pascal等)并不是我们要关注的重点。 其次正确,包括正确的输入以及正确的逻辑。由于计算机 “忠实”得有点“盲目”以至于对事情的对错毫无判断力,就象一把一样,在手里它就是正义的化身,而在土匪手里就成了恶魔的代言人,因此要时刻记住计算机是没有错的,“错误”掌握在我们自己手里,这也就决定了我们在编程时是不是应该投入更多的精力来纠正我们自己所犯下的错误上面来?除此之外,我们智慧的结晶——程序,在我们的上帝——顾客,在程序的使用者看来就象个黑匣子,他们只管输入哪怕是错误的输入却同样期待能得到正确的结果,而对于上帝我们除了祈祷和期望之外还能有什么选择?因此对于输入的不可预期的程序源数据,我们是不是也应该花更多的时间进行处理呢?

先小结下,由于我们在编程时需要对于输入的不可预期的程序源数据以及提高我们自己的逻辑正确性花费更多的时间,因此这也就是我们编程训练所应该关注的重点,而不是关注于何种实际的程序设计语言。

为了加深印象,下面我们举个实际的例子来说明这点。先看题目要求“输入一行字符串(全字母或空格),统计其中的单词数量,单词之间用空格分开”,应该说这是个简单的题目,有些人可能一看到这个题目很快就给出了自己的答案。如有些人会想单词是以空格分开的,我们统计其中空格的数量再加上最后一个单词不就是单词的个数吗,因此便很快给出如下答案。

/*Program1*/

#include main() {

char string[81]; int i,num = 0; char c;

gets(string);

for(i = 0;(c=string[i])!='\\0';i++) if(c == ' ')

num++;

printf(\"There are %d words in the line.\\n\}

编译运行,然后输入“I am a student”,得出正确答案“There are 4 words in the line.”在输入几个类似的测试用例,都得出正确答案,最后确信无误便自信满满的交出自己的答卷。应该说正确的输入情况下,这个程序毫无疑问是能够正确运行的,但这种正确性是非常脆弱的,你不能指望用户和你一样聪明并且不犯错误,何况即使是神仙那也有打盹的时候。假如用户输入“ I am a student”,“ I am a student”, “ I am a student ”„„这种情况下,你是不是应该重新修正你的思路呢?现在我这样考虑一个单词应该是字母后面紧接着一个空格或者结束符号,所以重新写下如下程序。

/* Program 2*/ #include main() {

char string[81]; int i,num = 0; char c;

gets(string);

for(i = 0;(c = string[i])!='\\0';i++)

if(c != ' ' &&(string[i+1] ==' '|| string[i+1]== '\\0')) num++;

printf(\"There are %d words in the line.\\n\}

运行一遍,发现结果是正确的,单词之间不管你是有几个空格,不管是两个也好,三个也好都能够正确运行,但是回过头来我们看下这个程序是不是还有些可以改进的地方呢?我们再看如下一个程序 /* Program 3[3]*/ #include main() {

char string[81];

int i,num = 0,word = 0;/*word标志位*/ char c;

gets(string);

for(i = 0;(c = string[i])!='\\0';i++) if(c == ' ') word = 0; else if(word == 0) {

word = 1; num++;

}

printf(\"There are %d words in the line.\\n\}

输入测试用例也能够完全正确,看看源程序,其中增加了一个标志位便把所有的情况给考虑进去了,相比前一个程序,程序2就是直白的描述了解题的思路,而程序3看起来好象不好理解但似乎更能满足我们作为“天才程序员”的欲望,这也就是编程时使用的一些小技巧。

总体比较三个程序,可以说类似程序1的错误——逻辑错误是我们程序员比较容易犯的,也是在编程调试过程中最费时间的,至于语法错误编译器可以很快的给我们找出错误的地方程序2逻辑正确但相比程序1思路要更难想到,程序3思路不但正确而且还在编程的过程中使用了一些编程技巧,所有这些开阔的逻辑思路(解决问题),快速的调试方法及过程(发现错误),累积的编程技巧(让程序让“好看”)都需要我们在编程实践中去获得,这是宝贵的经验也我们作为程序员最珍贵的地方,而且最重要的这些经验是属于我们自己的,独一无二的。

3、两者之间的关系

综合上面的叙述,我们把编程看成程序设计语言的“单词”“语法”“明确的语义”“正确的语义”四个部分,可以看出前面三个部分是几乎所有的程序设计语言自身所具有的,“单词”“语法”不同的程序设计语言有所不同,但如果你仔细观察所有的程序设计语言,可以分成几类,而且有很多地方也有相通的地方,有点象一通百通的味道,这也就是为什么很多有经验的程序员可以在很短的时间就能熟悉其他语言的原因。至于“明确的语义”这是所有的程序设计语言都应该具备的,只是表述的方法由于程序设计的“单词”“语法”不同表述不同而已,但让计算机明白该做什么这点是相同的。最后“正确的语义”包括开阔的,正确的逻辑思路以及发现并纠正错误的能力则是属于我们自己的,这也是我们区别于其他人的地方,这些都需要在经常性的编程实践中去积累去开阔,所以是我们所应该关注的重中之重。

跟许多前辈一样,我是建议初学编程者先熟练掌握一门语言的,至于怎么学习,我想书本是纲领,我们首先要熟悉一门语言的粗枝大叶,在编程的过程中不至于老是去翻书查找一门语言的基本语法,比如说函数的传递方式,函数调用的一些特点,这样不仅非常浪费时间而且一直困扰于语法中使得自己有种身缘“庐山”中的那种非常迷茫的感觉。就象施工一样,建造一个茅屋可能没有图纸,抱着边建边看的态度兴许能完成(事实上你自己脑海里至少也有个大致的样子),但要建造一座大厦,没有图纸,困难可想而知。当然光有图纸还是不够的,我们可能在施工的过程中发现一些问题,这样我们在慢慢修改原来的方案或者原先有些地方描述不清楚通过具体施工确认我们的想法。因此编程实践同样不可少,编程实践不仅可以“让语义正确”即开阔编程思路,发现错误(调试),累计编程技巧,而且可以在编程过程中熟悉这门语言,熟悉这门语言的细节部分,但也请记住这些细节虽然重要,但也仅仅是细节。当然光看书,光有理论那也是不行的,有“纲

领”而没有“章程”,有“纲领”而没有实施那同样也是没用的,毕竟实践是检验真理的唯一标准,实践过程中给理论修枝剪叶。

总得来说熟悉一门程序设计语言的语法是编程实践的先行,而编程实践是可以更好促进对程序设计语言基本语法知识的理解和熟悉,而积累的宝贵的编程经验则我们自己最珍贵的地方,最独一无二的地方,是所有程序设计语言所没有的。

4、写在最后面的话

写到这里,我想现在应该是回答第一个问题的时候了,有人说我学了这么长时间的C语言还是没编过一个漂亮花哨的程序,编出来的都是丑陋的而且看起来没什么价值的程序,一点自豪感都没有便丧失了对编程的兴趣,为什么前面我一直强调一门语言丰富的编程经验是你最珍贵的地方呢?原因就在于编程不仅仅是把我们的想法把我们的思路用程序设计语言写下来就够了,要得到实际运用还需要与计算机的其他知识如网络,低层硬件,图形图象,开发环境等,甚至还有其他学科知识比如自动控制,通讯等相结合,而这些知识需要我们在以后的学习过程中学习,比如一些后续课程,数据结构与算法,操作系统,数据库,计算机网络,软件工程等等,如果有兴趣你也可以在以后的课程中专门学习视窗程序的编程,或者也可以学习自己做个网站,那时侯就真的是有自豪感并且货真价实的了。当然那时侯可能你会有另外的想法比如说做嵌入式程序员,网络程序员,甚至去搞算法研究等等了。不管你选择做什么,书本知识都是大家所共有的,是全人类的财富,而自身的知识,尤其是经验却实实在在是你自己的,最后我还想强调一遍,这是我们最珍贵的地方。

所以在此之前,我知道打基础是痛苦而且没有多少成就感的过程,但是在化蛹成蝶之前,我们还是要继续我们做而且认真的做我们的毛毛虫,因为我们知道我们会有变成美丽蝴蝶的那一天

为什么要学习C语言,而不是其他语

言?

作者:admin 发布时间:2012-03-30 16:38 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣4

每个程序员在他们的编程生涯中都应该学习C语言,因为它有太多难

以忽视的好处了。除了它会给你提供更多的工作机会之外,C语言还会教给你更多的关于计算机的知识。它能给我们提供的裨益,,简单列举如下:

1. 相比较其他的编程语言(像C++,JAVA),C语言是个低级语言。从总体上来说,低级的编程语言可以让你更好的了解计算机。 2. 设备驱动程序和操作系统只能用C语言来编写。现在,你可能还从来没有编写过一个设备驱动程序或者一个操作系统,但是如果你需要去修改他们的时候,怎么办?

3. 如果你想要得到一份编写微控制器程序的工作的时候,该怎么办?他们都是用C语言编写的。就因为不想学习一门新的语言,你就准备你能得到工作的机会吗?

4. C的程序比其他用别的语言写的程序,实现相同的功能,它用的代码行数更少,而它带来的运行效率却更快。有时候,你的程序所需要的速度,只有C语言能做到。 5. 如果你学习过C语言,你就能学习现在任何的高级编程语言。因为所有的高级语言都是以C语言为基础的(像JAVA,C++,C#等等)。 6. 因为C语言已经存在很多年了,它有广泛的使用团体并且有大量的现成代码可以利用。这就使你能在过去程序的基础上,快速和高效的编写新的算法和函数。

7. C语言是一个开源组织的语言。一个开源组织的产物--LINUX,就是用C语言写的。如果你会C语言,你就能参加这个组织并且还能向众多的开源组织投稿,比如参加Source Forge并且给他们投稿。

8. C语言是唯一一个向你阐述指针的本质的语言。而C#和Java干脆跳过了指针这个题目。可是指针确实使C语言变得更加强大。

9. 找编程开发方面的工作时,C语言仍然是最普遍需要的语言。所以它值得你花时间去学会它。

10.任何里面有微处理器的设备都支持C语言。从微波炉到手机,都是由C语言技术来推动的。 11.好了,说了这么多,大家应该感到我们应该学习C语言的必要性和迫切性了吧!那就赶快学吧!让它成为我们取得更大成功的阶梯!

C语言编程时常犯十八个错误

作者:admin 发布时间:2012-04-28 18:53 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣36

C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法

检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。

1、书写标识符时,忽略了大小写字母的区别。 main() {

int a=5;

printf(\"%d\}

编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。

2、忽略了变量的类型,进行了不合法的运算。 main() {

float a,b;

printf(\"%d\}

%是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。

3、将字符常量与字符串常量混淆。 char c; c=\"a\";

在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\\0',而把它赋给一个字符变量是不行的。

4、忽略了“=”与“==”的区别。

在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写

if (a=3) then „

但C语言中,“=”是赋值运算符,“==”是关系运算符。如: if (a==3) a=b;

前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。

5、忘记加分号。

分号是C语句中不可缺少的一部分,语句末尾必须有分号。 a=1 b=2

编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。 {

z=x+y; t=z/100;

printf(\"%f\}

对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。

6、多加分号。

对于一个复合语句,如: {

z=x+y; t=z/100;

printf(\"%f\};

复合语句的花括号后不应再加分号,否则将会画蛇添足。又如: if (a%3==0); I++;

本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1。再如: for (I=0;I<5;I++); {scanf(\"%d\printf(\"%d\

本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。

7、输入变量时忘记加地址运算符“&”。 int a,b;

scanf(\"%d%d\

这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指a在内存中的地址。

8、输入数据的方式与要求不符。

①scanf(\"%d%d\",&a,&b);

输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法: 3,4

输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。

②scanf(\"%d,%d\

C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的: 3,4

此时不用逗号而用空格或其它字符是不对的。 3 4 3:4 又如:

scanf(\"a=%d,b=%d\输入应如以下形式: a=3,b=4

9、输入字符的格式与要求不一致。

在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。

scanf(\"%c%c%c\如输入a b c

字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。

10、输入输出的数据类型与所用格式说明符不一致。 例如,a已定义为整型,b定义为实型 a=3;b=4.5;

printf(\"%f%d\\n\

编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。

11、输入数据时,企图规定精度。 scanf(\"%7.2f\

这样做是不合法的,输入数据时不能规定精度。

12.switch语句中漏写break语句。

例如:根据考试成绩的等级打印出百分制数段。 switch(grade) {

case 'A':printf(\"85~100\\n\"); case 'B':printf(\"70~84\\n\"); case 'C':printf(\"60~69\\n\"); case 'D':printf(\"<60\\n\"); default:printf(\"error\\n\"); }

由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。例如 case 'A':printf(\"85~100\\n\");break;

13、忽视了while和do-while语句在细节上的区别。

(1)main() {int a=0,I;

scanf(\"%d\while(I<=10) {a=a+I; I++; }

printf(\"%d\}

(2) main()

{int a=0,I;

scanf(\"%d\do

{a=a+I; I++;

}while(I<=10); printf(\"%d\}

可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I>10时,二者结果就不同了。因为while循环是先判断后执行,而do- while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。

14、定义数组时误用变量。 int n;

scanf(\"%d\int a[n];

数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的大小作动态定义。

15、在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。 main()

{static int a[10]={1,2,3,4,5,6,7,8,9,10}; printf(\"%d\}

C语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[10]是不存在的。

17、在不应加地址运算符&的位置加了地址运算符。 scanf(\"%s\

C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应改为: scanf(\"%s\

18、同时定义了形参和函数中的局部变量。 int max(x,y) int x,y,z; {

z=x>y?x:y; return(z); }

形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为: int max(x,y) int x,y; {

int z;

z=x>y?x:y; return(z); }

手把手教你如何优化C语言程序

作者:admin 发布时间:2012-04-17 22:32 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣18

人围观

程序进行优化,通常是指优化程序代码或程序执行速度。优化代码和优化速度

实际上是一个予盾的统一,一般是优化了代码的尺寸,就会带来执行时间的增加,如果优化了程序的执行速度,通常会带来代码增加的副作用,很难鱼与熊掌兼得,只能在设计时掌握一个平衡点。

一、程序结构的优化

1、程序的书写结构

虽然书写格式并不会影响生成的代码质量,但是在实际编写程序时还是应该尊循一定的书写规则,一个书写清晰、明了的程序,有利于以后的维护。在书写程序时,特别是对于While、for、do…while、if…elst、switch…case等语句或这些语句嵌套组合时,应采用“缩格”的书写形式,

2、标识符

程序中使用的用户标识符除要遵循标识符的命名规则以外,一般不要用代数符号(如a、b、x1、y1)作为变量名,应选取具有相关含义的英文单词(或缩写)或汉语拼音作为标识符,以增加程序的可读性,如:count、number1、red、work等。

3、程序结构

C语言是一种高级程序设计语言,提供了十分完备的规范化流程控制结构。因此在采用C语言设计单片机应用系统程序时,首先要注意尽可能采用结构化的程序设计方法,这样可使整个应用系统程序结构清晰,便于调试和维护。于一个较大的应用程序,通常将整个程序按功能分成若干个模块,不同模块完成不同的功能。各个模块可以分别编写,甚至还可以由不同的程序员编写,一般单个模块完成的功能较为简单,设计和调试也相对容易一些。在C语言中,一个函数就可以认为是一个模块。所谓程序模块化,不仅是要将整个程序划分成若干个功能模块,更重要的是,还应该注意保持各个模块之间变量的相对性,即保持模块的性,尽量少使用全局变量等。对于一些常用的功能模块,还可以封装为一个应用程序库,以便需要时可以直接调用。但是在使用模块化时,如果将模块分成太细太小,又会导致程序的执行效率变低(进入和退出一个函数时保护和恢复寄存器占用了一些时间)。

4、定义常数

在程序化设计过程中,对于经常使用的一些常数,如果将它直接写到程序中去,一旦常数的数值发生变化,就必须逐个找出程序中所有的常数,并逐一进行修改,这样必然会降低程序的可维护性。因此,应尽量当采用预处理命令方式来定义常数,而且还可以避免输入错误。

5、减少判断语句

能够使用条件编译(ifdef)的地方就使用条件编译而不使用if语句,有利于减少编译生成的代码的长度,能够不用判断语句则少用判断用语句。

6、表达式

对于一个表达式中各种运算执行的优先顺序不太明确或容易混淆的地方,应当采用圆括号明确指定它们的优先顺序。一个表达式通常不能写得太复杂,如果表达式太复杂,时间久了以后,自己也不容易看得懂,不利于以后的维护。

7、函数

对于程序中的函数,在使用之前,应对函数的类型进行说明,对函数类型的说明必须保证它与原来定义的函数类型一致,对于没有参数和没有返回值类型的函数应加上“void”说明。如果果需要缩短代码的长度,可以将程序中一些公共的程序段定义为函数,在Keil中的高级别优化就是这样的。如果需要缩短程序的执行时间,在程序调试结束后,将部分函数用宏定义来代替。注意,应该在程序调试结束后再定义宏,因为大多数编译系统在宏展开之后才会报错,这样会增加排错的难度。

8、尽量少用全局变量,多用局部变量。

因为全局变量是放在数据存储器中,定义一个全局变量,MCU就少一个可以利用的数据存储器空间,如果定义了太多的全局变量,会导致编译器无足够的内存可以分配。而局部变量大多定位于MCU内部的寄存器中,在绝大多数MCU中,使用寄存器操作速度比数据存储器快,指令也更多更灵活,有利于生成质量更高的代码,而且局部变量所的占用的寄存器和数据存储器在不同的模块中可以重复利用。

9、设定合适的编译程序选项

许多编译程序有几种不同的优化选项,在使用前应理解各优化选项的含义,然后选用最合适的一种优化方式。通常情况下一旦选用最高级优化,编译程序会近乎病态地追求代码优化,可能会影响程序的正确性,导致程序运行出错。因此应熟悉所使用的编译器,应知道哪些参数在优化时会受到影响,哪些参数不会受到影响。

在ICCAVR中,有“Default”和“Enable Code Compression”两个优化选项。 在CodeVisionAVR中,“Tiny”和“small”两种内存模式。 在IAR中,共有7种不同的内存模式选项。

在GCCAVR中优化选项更多,一不小心更容易选到不恰当的选项。

二、代码的优化

1、选择合适的算法和数据结构

应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序

或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。.选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。

数组与指针语句具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。但是在Keil中则相反,使用数组比使用的指针生成的代码更短。。

3、使用尽量小的数据类型

能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。

在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和%s格式说明符),少用长整型参数(%ld、%lu、%lx和%lX格式说明符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。在其它条件不变的情况下,使用%f参数,会使生成的代码的数量增加很多,执行速度降低。

4、使用自加、自减指令

通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。在AVR单片适用的ICCAVR、GCCAVR、IAR等C编译器以上几种书写方式生成的代码是一样的,也能够生成高质量的inc和dec之类的的代码。

5、减少运算的强度

可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。如下:

(1)、求余运算。 a=a%8; 可以改为: a=a&7;

说明:位操作只需一个指令周期即可完成,而大部分的C编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。通常,只要求是求2n方的余数,均可使用位操作的方法来代替。

(2)、平方运算 a=pow(a,2.0); 可以改为: a=a*a;

说明:在有内置硬件乘法器的单片机中(如51系列),乘法运算比求平方运算快得多,因为浮点数的求平方是通过调用子程序来实现的,在自带硬件乘法器的AVR单片机中,如ATMega163中,乘法运算只需2个时钟周期就可以完成。既使是在没有内置硬件乘法器的AVR单片机中,乘法运算的子程序比平方运算的子程序代码短,执行速度快。

如果是求3次方,如: a=pow(a,3.0); 更改为: a=a*a*a;

则效率的改善更明显。

(3)、用移位实现乘除法运算 a=a*4; b=b/4;

可以改为: a=a<<2; b=b>>2;

说明:通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如: a=a*9

可以改为: a=(a<<3)+a

6、循环 (1)、循环语

对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、指针运算、数组访问等,应该将没有必要执行多次的操作全部集合在一起,放到一个init的初始化程序中进行。

(2)、延时函数:

通常使用的延时函数均采用自加的形式: void delay (void) {

unsigned int i;

for (i=0;i<1000;i++) ; }

将其改为自减延时函数: void delay (void) {

unsigned int i;

for (i=1000;i>0;i--) ; }

两个函数的延时效果相似,但几乎所有的C编译对后一种函数生成的代码均比前一种代码少1~3个字节,因为几乎所有的MCU均有为0转移的指令,采用后一种方式能够生成这类指令。

在使用while循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更少1~3个字母。

但是在循环中有通过循环变量“i”读写数组的指令时,使用预减循环时有可能使数组超界,要引起注意。

(3)while循环和do…while循环

用while循环时有以下两种循环形式: unsigned int i; i=0;

while (i<1000) { i++;

//用户程序 } 或:

unsigned int i; i=1000; do i--;

//用户程序 while (i>0);

在这两种循环中,使用do…while循环编译后生成的代码的长度短于while循环。

7、查表

在程序中一般不进行非常复杂的运算,如浮点数的乘除及开方等,以及一些复杂的数学模型的插补运算,对这些即消耗时间又消费资源的运算,应尽量使用查表的方式,并且将数据表置于程序存储区。如果直接生成所需的表比较困难,也尽量在启动时先计算,然后在数据存储器中生成所需的表,后以在程序运行直接查表就可以了,减少了程序执行过程中重复计算的工作量。

8、其它

比如使用在线汇编及将字符串和一些常量保存在程序存储器中,均有利于优化

C语言中变量的位置与程序优化

作者:admin 发布时间:2012-04-17 22:35 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣1

前几天一个朋友在网上问我:“怎么区分全局变量,静态变量和自动

变量?”我觉得奇怪,他怎么问一个初学者的问题,我这位朋友其实编程挺厉害的,呵呵。接着他说是:“计算机怎么区分”。确实,人区分各种变量一看就知道了,但是计算机怎么区分呢?没有编译知识可能一下子也难弄懂。

计算机理解力是没法和我们相比的,但是它比我们快。我们可以把这些变量分开

来放,从不同地方取来的就是不同的变量。告诉计算机去找的就是符号表。当然,生成符号表是编译器的事,我们不用管。但是了解一点总是有好处的。这里我以Unix ELF(Executalbe and Linkable Format)格式文件为例说明编译器是怎么安排全局变量,静态变量和自动变量的位置的。

ELF可重定位目标文件包括:ELF头以

及.text,.rodata, .data ,.bss ,.symtab, .rel.text, .rel.data 等节。 1、全局变量:已初始化的保存在.data段中 ,未初始化的表示为.bss段的一个占位符;

2、静态变量:根据是否初始化分别在.data 和.bss段中分配空间;

3、自动变量(非静态局部变量):在运行时保存在栈中。既不在.data 段中也不在.bss段中。

其实我们可以编写一个简单的程序,编译了以后用objdump命令查看各种变量所在的节。

知道了各种变量在运行时的位置也就知道了他们的储存期,而且了解变量的位置对编写高性能程序也非常有帮助。

我们知道嵌入式对程序的性能要求是非常高的,函数的参数列表越长那么函数调用的开销就越大,这个时候我们可以使用全局变量提高程序的性能。将函数和变量声明为static可防止函数和变量被其它模块不正确的使用。

关于自动变量,请看下面的两个程序: long product;

void factorialA(long n) {

long i;

for(i = 1; i <= n;i++ ){ product *= i; }

void factorialB(long n) {

long i; long x = 1;

for(i = 1; i <= n;i++ ){ x *= i; }

product = x; }

在n值较大的时候,上面两个程序的性能是有显著差别的。 这是利用了程序的局部性原理。

如何在C语言中嵌入汇编语言

作者:admin 发布时间:2012-04-17 22:37 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣1

c语言中取得伪随机数主要使用srand()和rand()这两个函数。

rand()函数会返回一个伪随机数,但是这是通过一种数学公式推算出来的,得到的随机数分布太集中,这时候要使用srand()函数来设定产生随机数的种子,一般采用当前时间作为种子,这样可以得到分布比较均匀的伪随机数。

rand(),srand()函数位于stdlib.h文件中,取得时间的time()函数位于time.h文件中。随机数测试如下:

#include #include #include void main() {

int results[10];/*用于保存随机数产生的结果*/ int i=0;

srand((int)time(0));/*设定种子*/ for(i=0;i<10;i++) {

results[i]=0; }

for(i=0;i<10000;i++) {

(results[rand()%10])++;/*取一万次随机数,使用求余运算符使得最后取得的随机数小于10*/ }

for(i=0;i<1 0;i++) {

printf(\" %d %d \\n\输出0-9之间的随机数出现的次数*/ } }

经多次运行试验,产生的随机数比较均匀

在C语言中嵌入汇编语言是如此的简单

作者:admin 发布时间:2012-04-17 22:39 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣7

人围观

两种方法在c语言中嵌入汇编:

(1) __asm(\" 字符串序列\"); 例如:

__asm(\" MOVN A,#1\"); /*第一个双引号右边有一个空格*/

(2) #pragram asm 汇编语句

#pragram endasm 例如:

#pragram asm MOVN A,#1 MOVW _temp,A #pragram endasm

什么是ANSI C,为什么学习C语言要以

它为标准?

作者:admin 发布时间:2012-04-17 22:40 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣5

C语言起源于1969年Ken Thompson开发的B语言,后来D.M.Ritchie

于1971年在B语言的基础上开发了NEW B语言,也就是我们现在说的C语言。最初,C语言的主要客户是编译器设计者,这也就是为什么“数组是从0开始而不是从1开始的原因”(其实还有许多为编译器设计者设计的特性)。1973年Ken Thompson和D.M.Ritchie将UNIX用C语言重写(原来是用汇编语言实现的),UNIX第5版诞生,从此UNIX就和C语言成了孪生兄弟。

不久,C语言越来越受欢迎。1978年Brian W.Kernighan和D.M.Ritchie以Steve Johnson编写的PCC这个C语言编译器(后来这个编译器被广泛移植)为基础,编著了《The C Programming Language》(简称TCPL),人们把这个C语言版本成为K&R C。

20世纪80年代,C语言广泛流行,动摇了BASIC的地位。许多人为其写变种,各种C版本涌出。因为C语言没有一个统一的标准,导致了它的代码无法在各种编译器上实现,C语言受到了变种松散的威胁——因此C语言必须进行标准化,1983年美国国家标准化组织(ANSI)成立了C语言工作小组,开始对C进行标准化,19年12月ANSI C最终被ANSI委员会接纳(我们学习C语言应该学习标准化后的C语言版本)。1990年国际标准化组织(ISO)也接纳了ANSI C,并做了一些小的修改,也就形成了ISO C。1990年初,ANSI委员会重新接纳了ISO C。

因此ANSI C(ISO C)是C语言进行标准化后的产物,现在所有的编译器都应该符合ANSI C(ISO C)标准,我们学习的C语言应该也是ANSI C(ISO C)。

C语言内存调试技巧—C语言最大难点

揭秘

作者:admin 发布时间:2012-04-17 22:43 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣6

人围观

本文将带您了解一些良好的和内存相关的编码实践,以将内存错误保持在控制

范围内。内存错误是 C 和 C++ 编程的祸根:它们很普遍,认识其严重性已有二十多年,但始终没有彻底解决,它们可能严重影响应用程序,并且很少有开发团队对其制定明确的管理计划。但好消息是,它们并不怎么神秘。引言

C 和 C++ 程序中的内存错误非常有害:它们很常见,并且可能导致严重的后果。来自计算机应急响应小组(请参见参考资料)和供应商的许多最严重的安全公告都是由简单的内存错误造成的。自从 70 年代末期以来,C 程序员就一直讨论此类错误,但其影响在 2007 年仍然很大。更糟的是,如果按我的思路考虑,当今的许多 C 和 C++ 程序员可能都会认为内存错误是不可控制而又神秘的顽症,它们只能纠正,无法预防。

但事实并非如此。本文将让您在短时间内理解与良好内存相关的编码的所有本质:

正确的内存管理的重要性

存在内存错误的 C 和 C++ 程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。从 1988 年著名的莫里斯蠕虫攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关:“大多数计算机安全漏洞都是缓冲区溢出”,Rodney Bates 在 2004 年写道。

在可以使用 C 或 C++ 的地方,也广泛支持使用其他许多通用语言(如 Java?、Ruby、Haskell、C#、Perl、Smalltalk 等),每种语言都有众多的爱好者和各自的优点。但是,从计算角度来看,每种编程语言优于 C 或 C++ 的主要优点都与便于内存管理密切相关。与内存相关的编程是如此重要,而在实践中正确应用又是如此困难,以致于它支配着面向对象编程语言、功能性编程语言、高级编程语言、声明性编程语言和另外一些编程语言的所有其他变量或理论。

与少数其他类型的常见错误一样,内存错误还是一种隐性危害:它们很难再现,症状通常不能在相应的源代码中找到。例如,无论何时何地发生内存泄漏,都可能表现为应用程序完全无法接受,同时内存泄漏不是显而易见。

因此,出于所有这些原因,需要特别关注 C 和 C++ 编程的内存问题。让我们看一看如何解决这些问题,先不谈是哪种语言。

内存错误的类别

首先,不要失去信心。有很多办法可以对付内存问题。我们先列出所有可能存在的实际问题: 1.内存泄漏

2.错误分配,包括大量增加 free()释放的内存和未初始化的引用 3.悬空指针 4.数组边界违规

这是所有类型。即使迁移到 C++ 面向对象的语言,这些类型也不会有明显变化;无论数据是简单类型还是 C 语言的 struct或 C++ 的类,C 和 C++ 中内存管理和引用的模型在原理上都是相同的。以下内容绝大部分是“纯 C”语言,对于扩展到 C++ 主要留作练习使用。

1、内存泄漏

在分配资源时会发生内存泄漏,但是它从不回收。下面是一个可能出错的模型(请参见清单 1):

清单 1. 简单的潜在堆内存丢失和缓冲区覆盖 以下是引用片段:

void f1(char *explanation) {

char p1;

p1 = malloc(100); (void) sprintf(p1,

\"The f1 error occurred because of '%s'.\explanation); local_log(p1); }

您看到问题了吗?除非 local_log()对 free()释放的内存具有不寻常的响应能力,否则每次对 f1的调用都会泄漏 100 字节。在记忆棒增量分发数兆字节内存时,一次泄漏是微不足道的,但是连续操作数小时后,即使如此小的泄漏也会削弱应用程序。

在实际的 C 和 C++ 编程中,这不足以影响您对 malloc()或 new的使用,本部分开头的句子提到了“资源”不是仅指“内存”,因为还有类似以下内容的示例(请参见清单 2)。FILE句柄可能与内存块不同,但是必须对它们给予同等关注:

清单 2. 来自资源错误管理的潜在堆内存丢失 以下是引用片段:

int getkey(char *filename) {

FILE *fp;

int key;

fp = fopen(filename, \"r\"); fscanf(fp, \"%d\return key; }

fopen的语义需要补充性的 fclose。在没有 fclose()的情况下,C 标准不能指定发生的情况时,很可能是内存泄漏。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑。

2、内存错误分配

错误分配的管理不是很困难。下面是一个示例(请参见清单 3):

清单 3. 未初始化的指针

以下是引用片段: void f2(int datum) {

int *p2;

/* Uh-oh! No one has initialized p2. */ *p2 = datum; ... }

关于此类错误的好消息是,它们一般具有显著结果。在 AIX 下,对未初始化指针的分配通常会立即导致 segmentation fault错误。它的好处是任何此类错误都会被快速地检测到;与花费数月时间才能确定且难以再现的错误相比,检测此类错误的代价要小得多。

在此错误类型中存在多个变种。free()释放的内存比 malloc()更频繁(请参见清单 4):

清单 4. 两个错误的内存释放 以下是引用片段:

/* Allocate once, free twice. */ void f3() {

char *p;

p = malloc(10); ...

free(p); ...

free(p); }

/* Allocate zero times, free once. */ void f4() {

char *p;

/* Note that p remains uninitialized here. */ free(p); }

这些错误通常也不太严重。尽管 C 标准在这些情形中没有定义具体行为,但典型的实现将忽略错误,或者快速而明确地对它们进行标记;总之,这些都是安全情形。

3、悬空指针

悬空指针比较棘手。当程序员在内存资源释放后使用资源时会发生悬空指针(请参见清单 5):

清单 5. 悬空指针

以下是引用片段: void f8() {

struct x *xp;

xp = (struct x *) malloc(sizeof (struct x)); xp.q = 13; ...

free(xp); ...

/* Problem! There's no guarantee that the memory block to which xp points hasn't been overwritten. */ return xp.q; }

传统的“调试”难以隔离悬空指针。由于下面两个明显原因,它们很难再现:

即使影响提前释放内存范围的代码已本地化,内存的使用仍然可能取决于应用程序甚至(在极端情况下)不同进程中的其他执行位置。

悬空指针可能发生在以微妙方式使用内存的代码中。结果是,即使内存在释放后立即被覆盖,并且新指向的值不同于预期值,也很难识别出新值是错误值。悬空指针不断威胁着 C 或 C++ 程序的运行状态。

4、数组边界违规

数组边界违规十分危险,它是内存错误管理的最后一个主要类别。回头看一下清单 1;如果 explanation的长度超过 80,则会发生什么情况?回答:难以预料,但是它可能与良好情形相差甚远。特别是,C 复制一个字符串,该字符串不适于为它分配的 100 个字符。在任何常规实现中,“超过的”字符会覆盖内存中的其他数据。内存中数据分配的布局非常复杂并且难以再现,所以任何症状都不可能追溯到源代码级别的具体错误。这些错误通常会导致数百万美元的损失。

内存编程的策略

勤奋和自律可以让这些错误造成的影响降至最低限度。下面我们介绍一下您可以采用的几个特定步骤;我在各种组织中处理它们的经验是,至少可以按一定的数量级持续减少内存错误。

1、编码风格

编码风格是最重要的,我还从没有看到过其他任何作者对此加以强调。影响资源(特别是内存)的函数和方法需要显式地解释本身。下面是有关标头、注释或名称的一些示例(请参见清单 6)。

清单 6. 识别资源的源代码示例

以下是引用片段: /******** * ... *

* Note that any function invoking protected_file_read() * assumes responsibility eventually to fclose() its * return value, UNLESS that value is NULL. *

********/

FILE *protected_file_read(char *filename) {

FILE *fp;

fp = fopen(filename, \"r\"); if (fp) { ...

} else { ... }

return fp; }

/******* * ...

*

* Note that the return value of get_message points to a * fixed memory location. Do NOT free() it; remember to * make a copy if it must be retained ... *

********/

char *get_message() {

static char this_buffer[400]; ...

(void) sprintf(this_buffer, ...); return this_buffer; }

/******** * ...

* While this function uses heap memory, and so * temporarily might expand the over-all memory * footprint, it properly cleans up after itself. *

********/

int f6(char *item1) {

my_class c1; int result; ...

c1 = new my_class(item1); ...

result = c1.x; delete c1; return result; }

/******** * ...

* Note that f8() is documented to return a value * which needs to be returned to heap; as f7 thinly * wraps f8, any code which invokes f7() must be * careful to free() the return value. *

********/ int *f7() {

int *p;

p = f8(...); ...

return p; }

使这些格式元素成为您日常工作的一部分。可以使用各种方法解决内存问题: 专用库 语言 软件工具

硬件检查器在这整个领域中,我始终认为最有用并且投资回报率最大的是考虑改进源代码的风格。它不需要昂贵的代价或严格的形式;可以始终取消与内存无关的段的注释,但影响内存的定义当然需要显式注释。添加几个简单的单词可使内存结果更清楚,并且内存编程会得到改进。

我没有做受控实验来验证此风格的效果。如果您的经历与我一样,您将发现没有说明资源影响的策略简直无法忍受。这样做很简单,但带来的好处太多了。

2、检测

检测是编码标准的补充。二者各有裨益,但结合使用效果特别好。机灵的 C 或 C++ 专业人员甚至可以浏览不熟悉的源代码,并以极低的成本检测内存问题。通过少量的实践和适当的文本搜索,您能够快速验证平衡的 *alloc()和 free()或者 new和 delete的源主体。人工查看此类内容通常会出现像清单 7中一样的问题。

清单 7. 棘手的内存泄漏

以下是引用片段:

static char *important_pointer = NULL; void f9() {

if (!important_pointer)

important_pointer = malloc(IMPORTANT_SIZE); ...

if (condition)

/* Ooops! We just lost the reference important_pointer already held. */

important_pointer = malloc(DIFFERENT_SIZE); ... }

如果 condition为真,简单使用自动运行时工具不能检测发生的内存泄漏。仔细进行源分析可以从此类条件推理出证实正确的结论。我重复一下我写的关于风格的内容:尽管大量发布

的内存问题描述都强调工具和语言,对于我来说,最大的收获来自“软的”以开发人员为中心的流程变更。您在风格和检测上所做的任何改进都可以帮助您理解由自动化工具产生的诊断。

3、静态的自动语法分析

当然,并不是只有人类才能读取源代码。您还应使静态语法分析成为开发流程的一部分。静态语法分析是 lint、严格编译和几种商业产品执行的内容:扫描编译器接受的源文本和目标项,但这可能是错误的症状。

希望让您的代码无 lint。尽管 lint已过时,并有一定的局限性,但是,没有使用它(或其较高级的后代)的许多程序员犯了很大的错误。通常情况下,您能够编写忽略 lint的优秀的专业质量代码,但努力这样做的结果通常会发生重大错误。其中一些错误影响内存的正确性。与让客户首先发现内存错误的代价相比,即使对这种类别的产品支付最昂贵的许可费也失去了意义。清除源代码。现在,即使 lint标记的编码可能向您提供所需的功能,但很可能存在更简单的方法,该方法可满足 lint,并且比较强键又可移植。

4、内存库

补救方法的最后两个类别与前三个明显不同。前者是轻量级的;一个人可以容易地理解并实现它们。另一方面,内存库和工具通常具有较高的许可费用,对部分开发人员来说,它们需要进一步完善和调整。有效地使用库和工具的程序员是理解轻量级的静态方法的人员。可用的库和工具给人的印象很深:其作为组的质量很高。但是,即使最优秀的编程人员也可能会被忽略内存管理基本原则的非常任性的编程人员搅乱。据我观察,普通的编程人员在尝试利用内存库和工具进行隔离工作时也只能感到灰心。

由于这些原因,我们催促 C 和 C++ 程序员为解决内存问题先了解一下自己的源。在这完成之后,才去考虑库。

使用几个库能够编写常规的 C 或 C++ 代码,并保证改进内存管理。Jonathan Bartlett 在 developerWorks 的 2004 评论专栏中介绍了主要的候选项,可以在下面的参考资料部分获得。库可以解决多种不同的内存问题,以致于直接对它们进行比较是非常困难的;这方面的常见主题包括垃圾收集、智能指针和智能容器。大体上说,库可以自动进行较多的内存管理,这样程序员可以犯更少的错误。

我对内存库有各种感受。他们在努力工作,但我看到他们在项目中获得的成功比预期要小,尤其在 C 方面。我尚未对这些令人失望的结果进行仔细分析。例如,业绩应该与相应的手动内存管理一样好,但是这是一个灰色区域——尤其在垃圾收集库处理速度缓慢的情况下。通过这方面的实践得出的最明确的结论是,与 C 关注的代码组相比,C++ 似乎可以较好地接受智能指针。

5、内存工具

开发真正基于 C 的应用程序的开发团队需要运行时内存工具作为其开发策略的一部分。已介绍的技术很有价值,而且不可或缺。在您亲自尝试使用内存工具之前,其质量和功能您可能还不了解。

本文主要讨论了基于软件的内存工具。还有硬件内存调试器;在非常特殊的情况下(主要是在使用不支持其他工具的专用主机时)才考虑它们。市场上的软件内存工具包括专有工具(如 IBM Rational Purify 和 Electric Fence)和其他开放源代码工具。其中有许多可以很好地与 AIX 和其他操作系统一起使用。

所有内存工具的功能基本相同:构建可执行文件的特定版本(很像在编译时通过使用 -g标记生成的调试版本)、练习相关应用程序和研究由工具自动生成的报告。请考虑如清单 8所示的程序。

清单 8. 示例错误 以下是引用片段: int main() {

char p[5];

strcpy(p, \"Hello, world.\"); puts(p); }

此程序可以在许多环境中“运行”,它编译、执行并将“Hello, world.\\n”打印到屏幕。使用内存工具运行相同应用程序会在第四行产生一个数组边界违规的报告。在了解软件错误(将十四个字符复制到了只能容纳五个字符的空间中)方面,这种方法比在客户处查找错误症状的花费小得多。这是内存工具的功劳。

结束语

作为一名成熟的 C 或 C++ 程序员,您认识到内存问题值得特别关注。通过制订一些计划和实践,可以找到控制内存错误的方法。学习内存使用的正确模式,快速发现可能发生的错误,使本文介绍的技术成为您日常工作的一部分。您可以在开始时就消除应用程序中的症状,否则可能要花费数天或数周时间来调试

如何将C语言代码转换为应用程序

作者:admin 发布时间:2012-04-17 22:58 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣2

C语言是高级语言,它的语法接近于人类的自然语言,但比自然语言

严谨。计算机无法直接将C语言的代码运行,他们并不懂得什么是C语言,实际上,计算机只处理他们的机器语言,所以我们必须为自己找一个翻译,这个翻译可分为2种:

1.编译器

编译器是“文章的译者”,它在我们完成创作后将其翻译(实际上是编译)成为机器语言。

2.解释器

解释器是“随声翻译”,代码运行的同时它们就开始工作,BASIC就是使用解释器,一般认为这种方法效率很低。

C语言要请第1种翻译,要想让C语言代码执行,就请让它帮你翻译成机器语言。常见的C语言编译器有:Turbo C\\Win-TC\\C-Free\\Visual C++6等,要想知道怎么使用它们,你可以去翻翻有关的手册

C语言程序设计入门学习六步曲

作者:admin 发布时间:2012-04-29 19:52 来源:C语言中文网 分享到:QQ空间新浪微博人人网腾讯微博豆瓣19

人围观

初学者遇到最多的困惑是:上课也能听懂,书上的例题也能看明白,可是到自

己动手做编程时,却不知道如何下手。发生这种现象的原因有三个:

一、所谓的看懂听明白,只是很肤浅的语法知识,而我们编写的程序或软件是要根据要解决问题的实际需要控制程序的流程,如果你没有深刻地理解C语言的语句的执行过程(或流程),你怎么会编写程序解决这些实际问题呢?

二、用C语言编程解决实际问题,所需要的不仅仅是C语言的编程知识,还需要相关的专业知识。例如,如果你不知道长方形的面积公式,即使C语言学得再好你也编不出求长方形的面积的程序来。 三、C语言程序设计是一门实践性很强的课程,“纸上谈兵”式的光学不练是学不好C语言的。例如,大家都看过精彩自行车杂技表演,假如,你从来没有骑过自行车,光听教练讲解相关的知识、规则、技巧,不要说上台表演、就是上路你恐怕都不行。

出现问题原因清楚了,那么如何学习呢?请你看【C语言学习六步曲】

特别提醒:在使用本方法之前一定要先阅读C语言的相关内容,要初步掌握相关知识的要点,然后按下述方法学习,可以达到理解、巩固、提高C语言知识和提高程序调式能力的目的。

第一步、验证性练习

在这一步要求按照教材上的程序实例进行原样输入,运行一下程序是否正确。在这一步基本掌握C语言编程软件的使用方法(包括新建、打开、保存、关闭C程序,熟练地输入、编辑C程序;初步记忆新学章节的知识点、养成良好的C语言编程风格)。 单击此处查看【C语言的编程风格】

使用《Turbo C/C++ for Windows 集成实验与学习环境》软件可以很方便地看着软件集成的《C语言入门教程》输入练习其中的程序例题。具体使用方法参见软件帮助部分。

初学者最容易犯的错误是:

1、没有区分开教材上的数字1和字母l,字母o和数字0的区别,造成变量未定义的错误。另一个易错点是将英文状态下的逗号,分号;括号()双引号\"\"输入出入成中文状态下的逗号,分号;括号(),双引号“”造成非法字符错误。

2、C语言初学者易犯语法错误:使用未定义的变量、标示符(变量、常量、数组、函数等)不区分大小写、漏掉“;”、“{”与“}”、“(”与“)”不匹配、控制语句(选择、分支、循环)的格式不正确、调用库函数却没有包含相应的头文件、调用未声明的自定义函数、调用函数时实参与形参不匹配、数组的边界超界等。

3、修改C语言语法错误时要注意以下两点: (1)、由于C语言语法比较自由、灵活,因此错误信息定位不是特别精确。例如,当提示第10行发生错误时,如果在第10行没有发现错误,从第10行开始往前查找错误并修改之。 (2)、一条语句错误可能会产生若干条错误信息只要修改了这条错误,其他错误会随之消失。特别提示:一般情况下,第一条错误信息最能反映错误的位置和类型,所以调试程序时务必根据第一条错误信息进行修改,修改后,立即运行程序,如果还有很多错误,要一个一个地修改,即,每修改一处错误要运行一次程序。

第二步、照葫芦画瓢

在第一步输入的C程序的基础上进行试验性的修改,运行一下程序看一看程序结果发生了什么变化,分析结果变化的原因,加深新学知识点的理解。事实上这和第一步时同步进行的,实现“输入”加深知识的记忆,“修改”加深对知识的理解。记忆和理解是相辅相成的,相互促进。

例如:将最简单的Hello World!程序 #include \"stdio.h\" int main() {

printf(\"Hello World!\\n\"); return 0; }

中的printf(\"Hello World!\\n\");中的Hello World!改成你的姓名,运行一下程序,看有什么变化?

再如求1+2+3...+100的和的程序 #include main() {

int i,sum=0;

for(i=1;i<=100;i++) {

sum=sum+i; }

printf(\"sum=%d\\n\}

第1次将for(i=1;i<=100;i++)中的100改成50,运行一下程序,看有什么变化? 第2次将for(i=1;i<=100;i++)中的i++改成i=i+2,运行一下程序,看有什么变化?

找出程序结果变化的原因,就加深了对C语句的理解。

第三步、不看教材看是否能将前两步的程序进行正确地输入并运行。

在这一步要求不看教材,即使程序不能运行,看能否将其改正,使其能正确运行。目的是对前两步的记忆、理解进一步强化。

第四步、增强程序的调试能力

在《Turbo C/C++ for Windows 集成实验与学习环境》集成的教材中每章都有C语言初学者易犯的错误,按照易出错的类型,将教材中的正确的程序改成错误的程序,运行一下程序,看出现的错误信息提示,并记下错误信息,再将程序改成正确的,运行一下程序。这样反复修改,就能够学习C语言程序发生错误的原因和修改错误的能力。

注意:每次只改错一个地方,目的是显示发生该错误的真正原因,避免一次改动多个地方,搞清发生错误的真正原因,切记!!!!

注意:上机调试程序时要带一个记录本,记下英文错误提示信息和解决该错误问题的方法,积累程序调试经验,避免在编程犯同样的错误,切记!!!!

例如,将Hello World程序中语句printf(\"Hello World!\\n\");中的;改成中文的分号;运行一下程序,看有什么结果?

C语言的错误信息的形式:(下面例子是的Turobo C2.0错误信息,如图1) 错误 文件名 行号 冒号 错误内容

↓ ↓ ↓ ↓ ↓

Error E:\\WinTc\\WinTc\\frist.c 5 : Function call missing) in function main

C语言的错误信息的形式:(下面例子是Visual C++6.0 错误信息,如图2) 文件名 行号 冒号 错误代码 冒号 错误内容

↓ ↓ ↓ ↓ ↓ ↓

e:\\wintc\\wintc\\frist.c ( 5 ) : error C2143 : syntax error : missing ')' before ';'

软件集成了高校教学用的最多的两个编译器Visual C++6.0和Turbo c 2.0,支持C、标准C、C++、标准C++、WINDOWS C程序的编辑、编译、和调试。软件根据用户输入程序的类型智能选择编译器,不用使用者干涉。

调试程序是一种实践性很强的事,光纸上谈兵是是没用的,就像游泳运动员只听教练讲解示范,而不亲自下水练习,是永远学不会游泳的。 即使在优秀的程序员编写程序也会犯错误的,可能事最低级的语法错误,但他能快速发现错误并改正错误,而我们C语言初学者面对错误提示,不知道发生了什么错误,如何改正,这就是差别。

第五步、研究典型的C语言程序,提高程序设计能力

经过上述过程的学习,我们已经学会了C语言各种语句的流程(即计算机是如何执行这些语句的过程),然后就可以研读别人编写C语言经典程序,看懂别人是如何解决问题的,学习解决问题的方法和程序设计技巧,提高自己的程序设计能力。

在软件中有50多个典型的源程序,研究它的实现方法,提高自己的程序设计能力。

第六步、研究课程设计源成序,提高C语言程序设计的能力。

C语言课程设计的目的:是让学生综合利用所学的C语言知识,解决一些接近实际问题题目,提高程序设计和调试较大程序的能力,为进一步进行软件开发打下坚实的基础。

最后送C语言初学者一句话来共勉:首先要相信自己是有能力学好C语言的,然后不惜一切代价把这种能力表现出来,你就成功了。做任何事,何尝不是这样呢?兴趣是学习C语言的最大动力,学习方法给你指明努力的方向,让你事半功倍。如果你感觉对你有帮助的话,请回帖支持,让更多的朋友知道它,谢谢支持!!!

爱因斯坦难题的C语言程序设计

作者:admin 发布时间:2012-04-18 12:48 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣25

题目是:

1、 在一条街上,有5座房子,喷了5种颜色。 2、 每个房里住着不同国籍的人

3、 每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物

约束条件:

1、 英国人住红色房子

2、 瑞典人养狗 3、 丹麦人喝茶

4、 绿色房子在白色房子左面 5、 绿色房子主人喝咖啡

6、 抽Pall Mall 香烟的人养鸟 7、 黄色房子主人抽、Dunhill 香烟 8、 住在中间房子的人喝牛奶 9、 挪威人住第一间房

10、抽Blends香烟的人住在养猫的人隔壁 11、养马的人住抽Dunhill 香烟的人隔壁 12、抽Blue Master的人喝啤酒 13、德国人抽Prince香烟 14、挪威人住蓝色房子隔壁

15、抽Blends香烟的人有一个喝水的邻居

问题是:谁养鱼? #include

char*COL[]={NULL,\"红\黄\绿\蓝\白\char*PAD[]={NULL,\"狗\猫\鱼\鸟\马\

char*DRK[]={NULL,\"茶 \牛奶\咖啡\啤酒\水 \char*GUO[]={NULL,\"挪威\英国\德国\丹麦\瑞典\char*SMK[]={NULL,\"Blends\Mall\

struct{ char guojia, color, pads, drink, smoke; } aa[5]; int OK(void) {

int i,j;

for(i=0;i<5;i++)

if(aa[i].guojia==2 && aa[i].color==1)goto next1; return 0; next1:

for(i=0;i<5;i++)

if(aa[i].guojia==5 && aa[i].pads==1)goto next2; return 0; next2:

for(i=0;i<5;i++)

if(aa[i].guojia==4 && aa[i].drink==1)goto next3; return 0; next3:

for(i=0;i<5;i++)

if(aa[i].color==3)goto next3_1; return 0; next3_1:

for(j=i+1;j<5;j++)

if(aa[j].color==5)goto next4; return 0; next4:

for(i=0;i<5;i++)

if(aa[i].color==3 && aa[i].drink==3)goto next5; return 0; next5:

for(i=0;i<5;i++)

if(aa[i].smoke==4 && aa[i].pads==4)goto next6; return 0; next6:

for(i=0;i<5;i++)

if(aa[i].color==2 && aa[i].smoke==3)goto next7; return 0; next7:

for(i=0;i<5;i++)

if(aa[i].guojia==3 && aa[i].smoke==5)goto next10; return 0; next10:

for(i=0;i<5;i++)

if(aa[i].smoke==2 && aa[i].drink==4)goto next11; return 0; next11:

for(i=0;i<5;i++)

if(aa[i].smoke==3)goto next12_1; next12_1:

if(i==0){if(aa[1].pads==5)goto next13;}

else if(i==4){if(aa[3].pads==5)goto next13;}

else if(aa[i-1].pads==5||aa[i+1].pads==5)goto next13; return 0; next13:

for(i=0;i<5;i++)

if(aa[i].smoke==1)goto next13_1; next13_1:

if(i==0){if(aa[1].pads==2)goto next14;}

else if(i==4){if(aa[3].pads==2)goto next14;}

else if(aa[i-1].pads==2||aa[i+1].pads==2)goto next14; return 0; next14:

for(i=0;i<5;i++)

if(aa[i].smoke==1)goto next14_1; next14_1:

if(i==0){if(aa[1].drink==5)goto next15;}

else if(i==4){if(aa[3].drink==5)goto next15;}

else if(aa[i-1].drink==5||aa[i+1].drink==5)goto next15; return 0; next15: return 1; }

char color[4]={1,2,3,5}; char drink[4]={1,3,4,5}; char guojia[4]={2,3,4,5}; char smoke[]={1,2,3,4,5}; char pads[5]={1,2,3,4,5}; int rot(char a[],int n) { int i,j,k,t; for(k=n-1;k>0;k--) if(a[k-1]{ for(i=0,j=n-1;ireturn 0; }

t=a[k-1];i=k;

for(j=k+1;jif(ta[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; }

return 1; }

int main()

{ int i,j,k,ans=0; int i1,i2,i3,i4,i5; aa[1].color=4; aa[2].drink=2; aa[0].guojia=1;

for(i1=0;i1<24;i1++){ aa[0].color=color[0]; aa[2].color=color[1];

aa[3].color=color[2]; aa[4].color=color[3]; rot(color,4);

for(i2=0;i2<24;i2++){ aa[0].drink=drink[0]; aa[1].drink=drink[1]; aa[3].drink=drink[2]; aa[4].drink=drink[3]; rot(drink,4);

for(i3=0;i3<24;i3++){ aa[1].guojia=guojia[0]; aa[2].guojia=guojia[1]; aa[3].guojia=guojia[2]; aa[4].guojia=guojia[3]; rot(guojia,4);

for(i4=0;i4<120;i4++){ for(i=0;i<5;i++)

aa[i].smoke=smoke[i]; rot(smoke,5);

for(i5=0;i5<120;i5++){ for(j=0;j<5;j++) aa[j].pads=pads[j]; rot(pads,5); if(OK()){

printf(\"---------------------第%d种解---------------------\\n\for(k=0;k<5;k++)

printf(\"第%d户: %s人 %s房子 养%s 喝%s 抽%s\\n\COL[aa[k].color],PAD[aa[k].pads],DRK[aa[k].drink],SMK[aa[k].smoke]); }}}}}} return 0; }

运行结果表明:总共有七组可能的解答,具体如下

---------------------第1种解--------------------- 第1户: 挪威人 黄房子 养猫 喝水 抽Dunhill 第2户: 丹麦人 蓝房子 养马 喝茶 抽Blends

第3户: 英国人 红房子 养鸟 喝牛奶 抽Pall Mall 第4户: 德国人 绿房子 养鱼 喝咖啡 抽Prince 第5户: 瑞典人 白房子 养狗 喝啤酒 抽BlueMaster ---------------------第2种解--------------------- 第1户: 挪威人 绿房子 养鸟 喝咖啡 抽Pall Mall 第2户: 德国人 蓝房子 养猫 喝水 抽Prince 第3户: 英国人 红房子 养马 喝牛奶 抽Blends 第4户: 丹麦人 黄房子 养鱼 喝茶 抽Dunhill

第5户: 瑞典人 白房子 养狗 喝啤酒 抽BlueMaster ---------------------第3种解--------------------- 第1户: 挪威人 绿房子 养鸟 喝咖啡 抽Pall Mall 第2户: 德国人 蓝房子 养鱼 喝水 抽Prince 第3户: 英国人 红房子 养马 喝牛奶 抽Blends 第4户: 丹麦人 黄房子 养猫 喝茶 抽Dunhill

第5户: 瑞典人 白房子 养狗 喝啤酒 抽BlueMaster ---------------------第4种解--------------------- 第1户: 挪威人 绿房子 养鱼 喝咖啡 抽Blends 第2户: 德国人 蓝房子 养猫 喝水 抽Prince 第3户: 瑞典人 黄房子 养狗 喝牛奶 抽Dunhill 第4户: 英国人 红房子 养马 喝啤酒 抽BlueMaster 第5户: 丹麦人 白房子 养鸟 喝茶 抽Pall Mall

---------------------第5种解--------------------- 第1户: 挪威人 绿房子 养鸟 喝咖啡 抽Pall Mall 第2户: 德国人 蓝房子 养猫 喝水 抽Prince 第3户: 瑞典人 白房子 养狗 喝牛奶 抽Blends 第4户: 英国人 红房子 养马 喝啤酒 抽BlueMaster 第5户: 丹麦人 黄房子 养鱼 喝茶 抽Dunhill

---------------------第6种解--------------------- 第1户: 挪威人 绿房子 养鸟 喝咖啡 抽Pall Mall 第2户: 德国人 蓝房子 养猫 喝水 抽Prince 第3户: 瑞典人 白房子 养狗 喝牛奶 抽Blends 第4户: 丹麦人 黄房子 养鱼 喝茶 抽Dunhill

第5户: 英国人 红房子 养马 喝啤酒 抽BlueMaster ---------------------第7种解--------------------- 第1户: 挪威人 绿房子 养鸟 喝咖啡 抽Pall Mall 第2户: 德国人 蓝房子 养鱼 喝水 抽Prince 第3户: 瑞典人 白房子 养狗 喝牛奶 抽Blends 第4户: 丹麦人 黄房子 养猫 喝茶 抽Dunhill

第5户: 英国人 红房子 养马 喝啤酒 抽BlueMaster

C语言开发平台的搭建

作者:admin 发布时间:2012-04-18 13:03 来源:C语言中文网 人围观 分享到:QQ空间新浪微博人人网腾讯微博豆瓣0

刚学编程的总想哪天自己写个软件出来,我也是,不过学完一本c语言教材后

发现自己还是写不出什么象样的东西来,原因就是标准c提供的函数库功能有限,要写出有实用价值的程序你必须使用对应平台下的函数库,比如你的程序在DOS中运行你可以用TC提供的库和dos系统调用,比如dos.h;你要在windows中运行,你要学会调用api这个windows系统提供的函数库;你要在linux中运行,你要学会使用qt或GTK+这些图形库和linux的api。

先跑下题说一下用tc的,你没有用tc就跳过这段看下段。你应该不会想以后去DOS环境中

工作吧,你又没有DOS系统用,你让windows给你的tc和tc编译的程序虚拟一个dos出来,你以为你的程序在调用DOS系统接口(dos.h中的函数),你以为你的程序在调用中断(int86),其实你在自欺欺人,你的程序运行在windows之上虚拟出的一个DOS中所以接触不到真实的硬件,可以理解为虚拟机吧,早点醒过来吧,在turboc2这个目录上按shift+Del键彻底删除吧。

这里先纠正一些人的错误看法,有人以为c只能写黑屏幕字符模式程序,还有人以为这个黑窗口就是DOS,其实在windows中有一类程序叫console application,你用vc或Dev-C++写的控制台程序就是这种黑窗口的console application,不过和tc的有本质区别,这里的是32位windows程序,而tc编译的是16位dos程序,需要windows虚拟一个dos才可以运行,速度慢还浪费系统资源,鄙视一下。所以在盗版windows已经普及的年代c语言初学者应该用vc或Dev-C++写简单的console application,如一个hello world程序,不要用我们的老师做学生时学的dos下的tc。此处引用我以前回帖的一段:如果有人还在用8086跑DOS或Windows3.x,使用TC编程,你应该是世界上最可怜的人了,因为你过着上世纪90年代中期以前的生活。我现在用来做玩具的凌阳的单片机都是16位的,明年就玩32位的ARM单片机了,现在的PC是AMD3200+跑WINXP SP2和Ubuntu6.06LTS,编译器是VS2003.NET和GCC4.0,和用TC的有10年以上差距了(从WIN95以前到07年)。

一、我想这里大多数人正用着windows系统吧,那就说windows下开发平台的搭建:

1、vc系列IDE

vc6.0,vs2003.net,vs2005.net是现在用的较多的windows编译环境,和windows系统一样都是微软开发的,可以说是windows下最好的IDE,没有比他们更强大的了,我就用vs2003.net写c和c++程序,还做过一些简单的网页。不过功能越强大体积也就越大,vc装好了有500m吧,我的vs2003.net带着2G多的msdn就更大的了(不装msdn也应该比vc6.0大吧)。这几个IDE中vc6.0最为小巧,使用也最简单,用来开发c程序绰绰有余了。优点是vc的调试功能是相当出色的,缺点是体积大下载和安装不方便。

2、Dev-C++

这是GCC移植到windows下的最好的IDE,最新版的安装程序也只有9M,虽然体积小,写windows下的c程序也没有问题,不得不说的是这是一个开源的软件,所以很多人为她开发了很多开发库,比如一些2D,3D的图形库,特别适合喜欢图形编程的c语言爱好者,再说现在游戏开发是软件行业的三大热门之一。另一个优点是为以后转移到linux平台打基础,这里可以学到一些linxu编程的知识。缺点就是IDE中集成的调试器没有VC的强大,命令行工具gdb很多人还是不习惯。

基于Dev-C++的开发平台搭建,我今天中午写了一个Dev-C++中allegro安装的教程,大家可以去这里看看:http://bbs.bccn.net/thread-163686-1-1.html

这里有Dev-C++的安装方法,还有allegro的安装方法,安装DirectX,OpenGL等其他的Dev-C++开发库和安装allegro一样,如果你选择了Dev-C++就一定要把这里的安装学会,工欲善其事必先利其器,自己的工作平台一定要了如指掌。

二、最后提一下linux下c开发环境的搭建:

没有Bill.Gates的东西我们依然可以生活的很好,在linux中其实装一个gcc就够了,直接用vi编辑代码,再装emacs也行,IDE可以选择kdevelop,不过有人说IDE可能会把初学者教傻。

就写这么多吧,还有些问题以后再讨论。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务