LCC-win32与C编程简介

从1972年在DEC PDP -11计算机上实现了最初的C语言至今,C语言已经历了数十年的风雨,而C语言正是以其如下独有的特点风靡全世界:
1)语言简洁,紧凑,使用方便,灵活.C语言只有32个关键字,程序书写形式自由.
2)丰富的运算符和数据类型.
3)C语言可直接访问内存地址,能进行位操作,使其能够胜任开发操作系统的工作.
4)生成的目标代码质量高,程序运行效率高.
5)可移植性好.
  尽管C语言有其局限和不足,但这些并不能否认C语言是一种优秀的计算机语言.
  正是如此C语言经历了数十年在各种语言的不断更替中, C语言屹立不倒.正如本书作者所言: As languages come and go, C remains.
  然而在于我们,学习C语言往往是使用TC2.0.而TC是运行在DOS环境下的,其集成的编辑器并不支持复制,粘贴等操作,对于我们对C 语言的学习诸多不便.再者,在计算机软件发展飞速发展的今天Windows98都只能算是一个过时的操作系统了,而学习C语言还要在DOS的命令行操作下学习,并且是16位的DOS编程,这未免有点不可思议了.当然你也可以在VC++的环境下学习C,但终究VC++是C++语言的集成环境.于是在学习C语言的过程中,我发现了Lcc-win32这个Windows C语言集成开发软件,它是32位的Windows编程,可以用它开发Windows应用软件.其功能强大,集成了Windows的资源编辑器,方便用户设计窗口,菜单,按钮等资源.它甚至集成软件开发的版本控制等等.要知道Lcc-win32 是免费软件!你可以随时到其官方网站下载最新版本(其最新版本还在不断更新)!
由于各种原因,我没能找到介绍Lcc-win32的中文文档,而在其官方网站上找到了这个教程<< Programming with lcc-win32>>,这是一本不错的电子书不但对传统C语言编程作了介绍,更重要的是介绍了C语言的 Windows编程!在此之前我还没有在国内见到过有这方面内容的书籍(也许是我的孤陋寡闻)!当然它也是针对Lcc-win32进行介绍的.但无论如何我们可以用C语言进行Windows编程了.
鉴于此,我决定利用我的空闲时间尽量把它翻译出来.但由于各种原因,最重要是我第一次翻译英文书籍,且本人各方面的知识和水平有限,翻译出来的字句不免会有不当或错误的.(可以到http://www.cs.virginia.edu/~lcc-win32http://q-software-solutions.comftp://ftp.cs.virginia.edu/pub/lcc-win32/下载原文以对照学习)且由于时间关系,也许我将无法完成全文的翻译任务.因此我希望能将我翻译出来的章节进行连载,且有后来人帮我将之完成.各种因素导致的不便之处敬请读者谅解.也希望读者能将译文的错误之处作出指正.
 作者:Vitamin C[抗坏血酸]

原文出处:http://www.c-sea.net/lcc_show.asp?id=1

1.3 标准库概述
  在Lcc-win32文档资料里,尤其是用户手册,有全部系统提供的库函数的描述.而在线帮助系统里也有各库函数的详细描述.在这里将是浪费地重复提及,因此,这里只介绍一些重要的库函数:[23]

头文件                           用途
_______________________________________________________________
stdio.h                     最著名的printf函数也在这定义.同样如sprintf,fprintf等
                          其它相关的函数也在这定义.
_______________________________________________________________
math.h                   数学函数.如:sin,cos,atan,log,exp,等等.
                        三角函数(sin,cos,tan.atan,等等).
                        舍入(rounding)函数(ceil,floor)
                        对数函数(log,exp,log10,等等)
                        平方与立方(sqrt,cbrt)
                        常用的常数如:pi,e,等等
________________________________________________________________
stdlib.h                 标准库函数:
                      abort(非正常中止程序)
                      exit(正常中止程序)
                      atoi,itoa(数据类型转换)
                      malloc,calloc,free(动态分配内存)
                      rand,srand(随机数)
                      putenv,getenv(环境管理)
                      qsort(排序)
                      strtod,strtol(数据类型转换)
                      sleep(挂起程序一定时间)
_________________________________________________________________
stddef.h               定义在程序里常用的宏和类型,如:
                      NULL,offsetof,ptrdiff_t,size_t等等
_________________________________________________________________
string.h                 字符串处理函数.这里定义C语言处理字符串处理的全部函数,
                      详细见106页“Traditional string representation in C”
_________________________________________________________________
ctype.h                 字符分类(isalpha,islower,isdigit)
__________________________________________________________________
stdarg.h               定义带有变量参数的函数
                      (functions with variable number of arguments)
__________________________________________________________________
complex.h               复数处理函数

time.h                        时间相关函数

 

1.4 与Windows相关的头文件
windows.h             所有窗口函数的定义.如建立一个窗口,打开一个窗口等,
                      这是一个很大的头文件,有约0.5MB大小的定义在里面.
                      注:在Lcc-win32里,很多像winbase.h这样的头文件被集中
                      在一个单一的文件

winsock.h             网络(TCP/IP).


shellapi.h      Windows外壳相关.






1.5 给程序传递参数

  我们不能使用参数干涉上面那个hello程序的操作.我们没有办法通过参使程序显示别的字符串作为例子,我们只能通过修改代码: “hello/n”来达到目的.我们甚至于不能让程序不输入回车.
通常程序从它们的环境里获得参数.一个非常古老但行而有效的方法是通过命令行给程序传递参数,即:一系列程序可用的字符串.
让我们看看参数是怎样传递给程序的.[24]

#include <stdio.h>             (1)
int main(int argc,char *argv[])     (2)
{
int count;                   (3)
for (count=0;count < argc;count++) {   (4)
printf(                      
"Argument %d = %s/n",
count,
argv[count]);
}                           (5)
return 0;
}

  1)     我们再次包含stdio.h.
  2)     我们仍和上次一样使用“main”函数.这次使用的和上次一样是一个标准的“main”函数,只是这次我们传递了两个参数给程序:int argc 这是一个整型数据,在C里用“int”标识.它用于储存传递给程序的参数的个数.char *argv[] 这是一个指向字符的指针数组[25]用于指向用户输入的参数.如:我们从命令行调用我们的程序并使用参数“foo”和“bar”,则在argv[]数组里有:argv[0] 运行的程序的名称.
argv[1] 第一个参数,即: “foo”.
argv[2] 第二个参数,即: “bar”.
  我们使用一整型变量(argc)在内存里将用户输入的参数定位,用于控制我们当前要打印的参数.这是一个局部变量,即:一个只能用于一定范围的变量,在这个例子,这个变量只能在函数“main”中使用.[26]
  3)     我们使用“for”语句,即:一个循环控制结构. “for”语句有以下结构:
  ? 初始化.在循环开始前执行的语句.在这个例子,我们设置计数器为0.我们使用C语言的赋值操作符: “=”.这个语句的结构是:变量“=”值;
  ? 判断.每次循环前都要进行判断,决定循环是否中止.在这个例子我们判断count是否小于传给程序参数的个数,即:整数argc.
  ? 步长.每次循环后都要执行.这个例子我们使用自增表达式:counter++将计数器加1.这只是表达式counter=counter+1的简写.
  ? 注意,我们从0开始,当计数器等于循环次数的上限时停止.在C里,数组用于确定元素个数的下标n总是从0开始,直到n-1.[27]
  4)     我们再次使用printf在屏幕上打印字符.这次,我们传给printf下面的参数:
"Argument %d = ‘%s’/n"
count
argv[count]
  printf将扫描它的第一个参数.它从原样输出的文本里找出格式说明(格式说明用“%”和格式字符组成).我们在字符串里使用了两个格式说明:%d和%s.
  第一个, %d意为第一个由参数传入的输出项将在这个位置按10进制整数形式输出.这里即是字符串后的整型数据“count”,它的值将在这个位置输出.
  第二个, %s意为第二个由参数传入的输出项在这个位置按字符串形式输出.这里则是在数组argv[ ]里“count”位置的元素按字符串形式在相应位置输出.

5)     我们用一对花括号把语句括起来.这意味着迭代在这里结束.

现在我们准备运行这个程序.假定我们的程序源代码在文件“args.c”.按如下操作:
h:/lcc/projects/args> lcc args.c
h:/lcc/projects/args> lcclnk args.obj
我们先用Lcc编译程序将文本文件编译生成一个目标文件,然后用连接程序lcclnk将目标文件包含在一个可执行文件里.现在可以键入程序的名称运行程序了:[28]
h:/lcc/projects/args> args
Argument 0 = args
  我们没有使用参数,故只显示argv[0],即程序的名称,这里的例子是“args”.如果这样键入:
h:/lcc/projects/args> args.exe
Argument 0 = args.exe
或这样:
h:/lcc/projects/args> h:/lcc/projects/args.exe
Argument 0 = h:/lcc/projects/args.exe
  但没有程序的目标.这样就有趣了:
h:/lcc/projects/args> args foo bar zzz
Argument 0 = args
Argument 1 = foo
Argument 2 = bar
Argument 3 = zzz
  程序接受到3个参数,因此argc的值是4.则我们的变量计数将从0运行到argc-1,显示 4个参数:第0个,每1个,每2个等等.
1.5.1 循环控制结构
  上面介绍了“for”结构,但有必要在这里介绍和理解下面的几个循环控制语句.在C语言里有3个循环控制结构: “for”, “do”, 和 “while”.

1.5.1.1 for
  “for”结构有:
  ? 初始化部分,即:在循环开始前就执行的语句.
  ? 判断部分,即:每次循环开始前都要执行判断循环是否终止的语句.
  ? 步长部分,即:每次循环结束时都要执行的语句.通常循环计数器在这里增加或减少.
  表达方式为:
for(初始化表达式;判断表达式;步长) {
    语句块
  }
  在一个for语句里,你可以为for循环定义一个局部变量.这个变量的作用域在for语句结束时消失.如:

#include <stdio.h>
int main(void)
{
  for (int i = 0; i< 2;i++) {
  printf("outer i is %d/n",i);
  for (int i = 0;i<2;i++) {
  printf("i=%d/n",i);
}
}
  return 0;
}
The output of this program is:
outer i is 0
i=0
i=1
outer i is 1
i=0
i=1
  注意,在for里声明的‘i’标识也在for语句结束时其作用域消失,而下一个for语句里的‘i’是一个新的‘i’.把上面的例子作如下修改:
#include <stdio.h>
int main(void)
{
  for (int i = 0; i< 2;i++) {     1
  printf("outer i is %d/n",i); 2
  int i = 87;
  for (int i = 0;i<2;i++) {     4
  printf("i=%d/n",i);           5
}                     6
}                     7
return 0;               8
}

  在循环的最里面将有三个标识名称为: ‘i’.
  ? 第一个‘i’是外面的‘i’.其作用域为第1行至第7行.
  ? 第二个‘i’(87)是一个复合语句里的一个局部标识,作用域从第1行至第7行. 在复合语句里常常定义局部变量.
  ? 第三个‘i’是在第二个for语句里声明的.其作用域为第4行至第6行.
  注意,相同名称的标识在重叠的作用域里,外面的标识将被里面的标识屏蔽.这也是你在C语言里所期望的.

1.5.1.2 while
“while”结构则相对比较简单.它由单一的判断决定循环体是否结束循环.没有初始化部分,也没有步长部分.

表现形式:
while (判断表达式) {
    语句块
  }
  任何一个“for”循环可以转换成一个“while”循环:
初始化表达式
  while (判断表达式) {
    语句块
    步长
  }

1.5.1.3 do
  一个“do”结构只是while结构的另一种表现方式.但循环体在判断前执行一次,也就是说其循环体至少执行一次.它的表现形式是:
do {
    语句块
  } while (判断表达式);
  “continue”关键字用在循环语句中,作用为结束本次循环,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判断.循环正常的继续进行,只是夹在关键字与循环体末之间的语句被忽略.

1.5.2 基本数据类型
    Lcc-win32内部定义如下的C语言数据类型[29]:所有类型都是ANSI C语言的一部分.只有类型_Complex则例外,它只出现在大部分的C语言编译程序中,但出现在全部的Windows编译程序里.[30]
类型      大小      描述
_Bool*      1      逻辑型,不是0就是1
char      1      字符型. 有两个修饰符: signed(有符号) 和 unsigned(无符号).
short      2      16位的短整型. signed 和 unsigned.
int      4      32位整型. signed 和 unsigned.
long      4      和int相同
long long      8      64位整型. signed 和 unsigned.
float      4      单精度浮点型. (约 7 位有效位)
double      8      双精度浮点型. (约15位有效位)
long double      12      扩展精度浮点型. (约20位有效位)
flat_Complex
double_Complex
long
double_Complex
     8
16
24      复数.每一个_Complex由两部分组成:实数与虚部分.每个部分是一个浮点数.使用它们前需包含<complex.h>.

*实际上布尔类型(Boolean type)应该是: “bool”,但为了与已经使用了的代码相兼容,现在还没有这种类型的命名标准.如果你想使用bool,你要包含头文件“stdbool.h”.

      这些都是Lcc-win32的基本数据类型.还存在其它的一些数据类型.要使用它们要包含相应的头文件,它们没有“内建”在编译程序里.它们是使用编译程序的一个允许定义属你自已的数据类型这一选项的功能来构造的.
类型      头文件      大小      描述
qfloat      qfloat.h      56      350位浮点数
bignum      bignum.h      可变      扩展精度数字

1.5.3 小结
  1)     通常函数接受参数,并返回结果.函数的类型可以在声明或定义函数时在函数名称前面声明.
  2)     “main”函数可以从它的调用环境获得参数.
  3)     在我们使用标识符前都在向编译程序声明类型.

[24] 这里我们只描述Lcc-win32里两种传递参数方法中的ANSI C标准的方式.在Windows系统,有另一个可选择的入口,名为:WinMain,但它的参数与这里描述的有所不同.详情可参考本教程的Windows编程相关章节.
[25] 这就意味着你接受的是一个保存参数字符串的数组的起始地址.在该指针数组的第一个元素我们可以找到一个整数,其为程序名字字符串在内存中的起始地址.关于其中的细节我们将在与指针处理相关的章节描述.
[26] 局部函数如此声明(和其它变量的声明一样):
<type> 标识符;
如:
int a;
double b;
char c;
数组的声明使用同样的方式,只是多了一个用方括号括起来的代表数组大小的数字:
int a[23];
double b[45];
char c[890];
[27] 一个初学者常犯的错误是循环计数从1开始到它的值与上限相等.当然,如果你不用数组的下标作为循环控制这没有什么不妥,但在若是使用下标访问数组(通常是这种情况)时则会让程序在循环里访问无效的内存空间.
[28] 当编译时会发生操作在Lcc-win32的技术文档里有详细描述.在新的Lcc-win32版本里,你可以用编译程序‘lc.exe’,它将自动调用连接程序.
[29] 在大多数的编译器里char/short/int/long类型的大小可以在不同的机器间相适应.一些嵌入式系统的编译器不支持浮点.很多编译器不支持新类型_Bool,long long和long double.据我所知,在所有的Windows环境的32位编译程序,类型 char/short/int/long/float/double是与之相同的.
[30] Microsoft Visual C把“long double”作为double来处理,并将类型long long称为: “_int64”.为与Visual C兼容,Lcc-win32认为_int64与类型long long同意义.

 

1.6 声明与定义

  在C语言里准确地理解声明与定义的区别是十分重要的.
  一个声明(declaration)为编译程序介绍标识符.其本质是:这个标识符是一个xxx且它的定义将在下面出现.一个声明的例子:

extern double sqrt(double);

  在这个声明里,向编译程序介绍标识符sqrt,并告诉编译程序它是一个接受一个双精度浮点型数据参数及返回一个双精度浮点型数据结果的函数.仅此而已.没有为这个声明分配存贮空间,包括在编译程序内部的表.[31]
  一个定义(definition)告诉编译程序为标识符分配存贮空间.例如,当定义一个上面提及的函数,则用于存贮编译程序产生的代码的空间将被建立,且在程序符号表里产生一个入口.同样的,当我们写下:

double balance;

  就算编译程序从未见过这个标识符,在这个定义之后将知道这是一个双精度浮点型数据.[32]

1.6.1 变量的声明

  一个变量如下声明:
<类型> <标识符> ;
如:

int a;
double d;
long long h;

  所有的这些都是定义变量.如果因为这些变量在别处被定义,你在这里只想声明一个变量,而不为它分配任何存贮空间,你可以加上关键字extern:

extern int a;
extern double d;
extern long long d;

  随意地,你可以定义一个标识符,并给赋予它某个计算的结果作为它的值:

double fn(double f) {
double d = sqrt(f);
  // 更多的语句…
}

  注意:只能在函数里将变量初始化为一个在编译时不确定的值.在函数的外面你可以这样写:

int a = 7;

int a = (1024*1024)/16;

  但你所赋的值在编译时必须是不变的,即:当使用它时编译程序可以将值描述出来.
指针使用一个星号声明:

int *a;
 你可以在同一个声明语句里声明几个相同类型的标识符,如下:

int a,b=7,*c,h;
  注意:c是一个指向一个整型数据的指针,因为它有一个星号在其左边.这个符号有点让人迷惑,常常会忘记星号.当所有要声明的标识符是同一种数据类型可以使用这种多重声明并把指针放在独立的一行.
  C声明的语法被批评是十分模糊的.这是个事实;且没有办法拒绝这个明显的弱点.在Peter van der Linden的“Deep C secrets”[34]这本书里写了一个简单的口诀来记住这些语法.口诀如下(在此书的第三章):
The Precedence Rule for Understanding C Declarations.

规则1:从名字开始读声明,然后按优先级读.
规则2:优先级从高到低是:
        2.A:声明圆括号里的部分
        2.B:操作的后缀
              2.B.1: 圆括号()标志一个函数原型,和
                  2.B.2:括号[]标志一个数组
                  2.B.3:操作后缀:星号的结局为"指向".
规则3:如果一个const和/或可变的关键字是一个类型指定(如:int,long等等)的下一个.)应用于类型指定. 另外const和/或可变的关键字应用于指针星号的左边.
原文:
Rule 1: Declarations are read by starting with the name and then reading in precedence order.
Rule 2: The precedence, from high to low, is:
  2.A : Parentheses grouping together parts of a declaration
  2.B: The postfix operators:
    2.B.1: Parentheses ( ) indicating a function prototype, and
    2.B.2: Square brackets [ ] indicating an array.
    2.B.3: The prefix operator: the asterisk denoting "pointer to".
Rule 3: If a const and/or volatile keyword is next to a type specifier e.g. int, long, etc.) it
applies to the type specifier. Otherwise the const and/or volatile keyword applies to the pointer
asterisk on its immediate left.

  使用这条口诀我们甚至可以理解这个声明:
char * const *(*next)(int a, int b);

  我们从变量名开始,这个例子是“next”.这是被声明的名称.我们看在圆括号里的表达式有一个星号,因此我们可以断定“next是一个指向…的指针”.又看圆括号外面有一个星号在左边,和一个函数原型在右.使用规则2.B.1我们以原型继续. “next是一个指向有两个参数的函数的指针”.再看星号:"next是一个指向有两个参数的返回一个指向…指针的函数的指针",最后我们加上char *const,就是:"next是一个指向有两个参数的返回一个指向一个指向一个字符型常量的指针的指针的函数的指针"或next是一个指向有两个参数的返回一个指向一个常量指针的函数的指针,常数指针指向一个字符型常量".(译者:哪一种容易理解?都不太好理解,读者你试试看!英文是这样的:"next" is a pointer
to a function with two arguments returning a pointer to a constant pointer to char.).
  下面看这个例子:

char (*j)[20];
 Again,从“j是一个指向…的指针”开始.在右边是一个在方括号的表达式,因此适用2.B.2得到“j是一个指向一个有20个元素的数组的指针”.再加上“char”是: “j是一个指向一个有20个字符型元素的数组的指针”.注意:当产生一个映射时,没有标识符也可使用同样形式的声明:

j = (char (*)[20]) malloc(sizeof(*j));

  我们看在圆括号里的粗体字(一个映射)同样是一个声明,只是没有标识符j.

1.6.2 函数的声明

  函数的声明的详细说明:
? 函数返回值的数据类型
? 名称
? 每个参数的数据类型

  通常的形式是:

<类型> <名称>(<参数的数据类型 1>, … <参数的数据类型 N> ) ;

double sqrt(double) ;

注意 :一个标识符可以加到声明里,但它是可要可不要的.我们可以这样写 :

double sqrt(double x);

  只要你喜欢,但“x”不是所要求的将被编译程序忽略.
  函数的参数的数量是可变的.函数“printf”就是一个取得多个参数的例子.这些函数如此声明:

int printf(char *, ...);

  省略符号意为“更多的参数”.[35]
  为何函数的声明如此重要?
  当我们在C里开始编程时,函数的原型是不存在的.因此你可以如下定义一个函数:

int fn(int a)
{
  return a+8;
}

  而在另一个模块里这样写:

fn(7,9);
 没有任何问题.
  Well,在编译时当然没有问题.只是程序运行时崩溃或返回一个乱七八糟的结果.如果你编写一个由很多模块很多人参与的大系统, 这种错误存在的可能性几乎为100%.避免这种错误的发生是十分重要的.你可以在大部分的时间里避免,可你不可能完全地避免这种错误的发生.
  函数原型提出编译时检查所有的函数调用.因此我们不用担心在程序调试时花费大量的时间去排除这样的错误.在C++语言里,如果遇到一个没有原型的函数编译程序会中止编译.我曾经多次想把这种机制引入到Lcc-win32里,因为忽略函数原型常常导致错误,但出于兼容性的原因,我没有这样做.[36]

1.6.3 函数的定义

  函数的定义看起来和函数的声明很相像,不同之处是用一个在花括号里的语句块代代替了分号,就像我们在上面看到的main函数.另一个不同点就是我们要给每个参数指定名称,这些标识符不再是可选项: 需要在函数体内使用它们.下面是一个微不足道的例子:

int addOne(int input)
{
  return input+1;
}

1.6.4 变量的定义

  一个变量的定义将告诉编译程序为其分配内存空间.例如当编译程序见到如下语句将为变量分配内存空间:

int a;
或:
int a = 67;
 在第一个例子,编译程序在程序没有初始化的变量段里为变量分配sizeof(int)个字节的内存空间.而第二个例子则在已初始化的变量段里为赋值为67的变量分配同样的内存空间.

1.6.5 语句的语法

  在C语言里,那些如if或while之类的封闭的控制语句表达式,必须在一个圆括号里.在很多语言是没有必要的.如这样写:

if a < b run(); // 在C里不能这样写...

  在C语言里,if语句需要一个圆括号:

if (a<b) run();

  在C语言里, 赋值是一个表达式,即:它可以出现在一个很复杂的表达式里:

if ( (x =z) > 13) z = 0;

  其意义是在编译程序里产生将z的值赋予x的代码,再将x的值与13比较,如果关系成立,则程序将z的值设为0.

 

 



[31] 注意:如果一个函数只是被定义而没有使用,将完全没有为此函数分配存贮空间.在一个已编译的程序里没有使用任何空间,除非被有效地使用.若是被有效使用,编译程序将发出一条记录告诉连接程序这个对象在某处被定义.

[32] 注意:当你不想规定一个声明,可以使用这样的一个功能:定义就是一个声明;在它被定义后你只可以使用被定义的对象.一个声明位于程序模块的开始或在一个头文件里,这视乎你自己的选择.你可以在程序一开始就立即使用标识符,不管它的定义在哪里,也不管是否在另一个模块里.
[33] 内存地址当然是一个整数.例如,如果你有一台有128MB内存的机器,你有134 217 728个内存位置.它们从0开始计起,但Windows使用一个更复杂的计数机制称为“虚拟内存”(Virtual memory).
[34] Deep C secrets. Peter van der Linden ISBN 0-13-177429-8
[35] 使用一个可变参数数量的函数的接口在标准头文件“stdarg.h”里描述.更多的可以查看可变参数数量的函数章节

[36] 从一个编译程序作者的角度来看,去维护已经完成的代码是一个沉重的义务,同时要避免破坏正在工作的程序.当标准委员会向原型提出建议,所有的C代码将不能再使用旧形式的声明,因此需要一个过渡期.编译程序除了发出一个警告外将不接受任何没有原型的声明.一些人说这个过滤期现在该结束了(我们已经使用原型超过10年了),但是,像Lcc-win32
这样新的编译程序仍然使用旧形式的声明.

1.7 错误与警告(errors and warnings)

  在我们第一次编写一个程序时往往它是不能工作的,例如,如果我们在main函数里忘记用一个花括号`}'将它括起来时, 会发生什么?我们将前面的程序去掉一个花括号,并试运行它:
h:/lcc/examples>lcc args.c
Error args.c: 15 syntax error; found `end of input' expecting `}'
1 errors, 0 warnings
  Well,这至少是一个明显的错误信息.如果忘记在第三行count的声明的末尾加上分号`;'则会复杂得多:
D:/lcc/examples>lcc args.c
Error args.c: 6 syntax error; found `for' expecting `;'
Error args.c: 6 skipping `for'
Error args.c: 6 syntax error; found `;' expecting `)'
Warning args.c: 6 Statement has no effect
Error args.c: 6 syntax error; found `)' expecting `;'
Error args.c: 6 illegal statement termination
Error args.c: 6 skipping `)'
6 errors, 1 warnings
D:/lcc/examples>
  在这里可以看到一连串由第一个错误引起的错误.编译程序试着跳过错误去把程序排列起来,可是因为不能理解“for”的结构而产生了更多的错误.错误修正是一个十分困难的义务,且Lcc-win32对此并不在行.因此最好是查看第一个错误,通常因为第一个错误的修正剩下的错误信息将自动消失.[37]
  当我们忘记包含相应的头文件时可以出现另一种错误.如果我们忘记在程序里写下#include <stdio.h>时,将看到如下信息:
D:/lcc/examples>lcc args.c
Warning args.c: 7 missing prototype for printf
0 errors, 1 warnings
  这是一个警告.函数printf将被假定返回一个整数,只是在这个例子里是个十分好的假定.我们可以连接程序,且程序也正常运行.当然这不是一个好的习惯, 无论如何,当未知的函数没有对所有的参数进行核对; 没有发觉一个错误的参数被传递将引起另一个更棘手的错误:一个运行错误.
  通常,最好在碰到错误时立即尽可能的修正.因为越迟发现错误,错误将越困难被找到.故因该养成使用相应的头文件检查所使用函数调用的习惯.
  编译程序给出两种错误,根据错误的严重程度分类:警告,当错误不严重,并不至于中止编译程序的任务时给出警告.错误,当编译程序不能产生一个可执行文件时将给调用环境返回一个错误代码.
  我们应当视警告为错误,并修正它们.
  编译程序使用一个两个等级的“warning level”变量.在默认的情况下,很多警告为避免中断输出而不给于显示.如果你使用选项 -A提高警告的等级它们将被显示出来.这个编译程序选项将使编译程序发出所有的警告.可以在命令行里调用lcc –A <文件名>,或选择在 IDE的编译程序设置标签里相应的按钮来实现.
  当然错误可以出现在编译后面的过程.连接程序可以发现你在程序使用了一个没有给出任何定义的过程,并以这个错误停止连接.或它可以发现你给出两个不同的定义,因相同的标识符而发生矛盾.这些也都会引起一个连接时的错误.
  但更多的错误是在运行时发生的,即:在程序运行时.大多数的这些错误是难以发现的(它们可以通过编译程序和连接过程而没有任何警告)且将引起软件的运行故障.
  当一个程序员涉及到错误时C语言不是很“慈悲的”.大多数程序将因为错误而立即终止,或返回一个乱七八糟的结果.这时你需要一个特殊的工具,一个调试程序,去发现错误.Lcc-win32为你提供了这个工具,你可以在IDE里按F5来调试你的程序.
  小结:
语法错误(没有花括号或分号)是在所有错误里最容易修正的.
编译程序有两种诊断信息:警告与错误.
你可以使用选项-A来提高编译程序的错误报告.
当一个标识符被定义了两次或没有定义连接程序可以报告这种错误.
大多数的错误是在程序运行时发生的,引起程序终止或得到一个错误的结果.

[37] 如果你不是使用更新版本的Lcc-win32也许将看到不同的错误信息.当我写这个教程时我正在改良对错误的处理.

==========================

Vitamin C[抗坏血酸]那天把这分译文寄给我,我赶忙看了,我对lcc-win32 并不熟悉,一直都没用过,
但经常听人家提起。听说这个软件对在程序不是很好看,但很使用,而且免费 我一直用着盗版的vc,实在汗颜。我用了一下这个软件,感觉非常的好,唯一有一点不支持c++,另我有点费解。

不过,在用c语言编程工具中,我看我可能永远都会用他了。

  Vitamin C[抗坏血酸]翻译的这本书,翻译的很详尽,很容易理解。这本书不仅对
传统c语言做了介绍,更重要的是介绍了用c编写windows的程序,很多程序员一直都用
MFC编写windows的程序,往往是对windows的编程就云里雾里了,理解就不那么透彻
,只知道只要那样做就可以出一个程序了,不能从根本上了解。看看这本书,绝对让
你有所收获。
                                                          --张Q

===========================

作者:Vitamin C   更新日期:2004-12-23

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值