以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  LCC-Win32教程《Programming with lcc-win32》整理(中文版)  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=73072)


--  作者:卷积内核
--  发布时间:3/16/2009 10:52:00 AM

--  LCC-Win32教程《Programming with lcc-win32》整理(中文版)
LCC-Win32 是运行于Windows9x/Me/NT/2000下的免费的32位C语言编译系统。它的原始发布文件只有 3M,但却拥有一个功能强大的集成开发环境(IDE)、速度极快的编译器与连接器、方便顺手的调试器、资源编辑器和版本控制系统。另外还提供图像编辑器和 C 语言源文件格式化缩进器等附加工具。通过它的应用程序向导,你可以单击几次鼠标轻松得到一个应用程序框架,丝毫不比其它庞大的同类软件逊色。 你可以用LCC-Win32开发32位的控制台程序、Windows常规程序、动态连接库(DLL) 以及静态连接库(LIB)。LCC编译器支持标准的ANSI C,同时支持 C语言的扩展。通过下载相关工具,LCC-Win32还提供对Eiffel、Fortran语言的支持。

稍后将贴出LCC-Win32的官方免费教程《Programming with lcc-win32》的中文译本。

声明:中文资料1.1-1.7来自Vitamin C[抗坏血酸]的《LCC-win32与C编程简介》,我所作的只不过是将中文资料与原版教程的格式统一。再次对Vitamin C翻译了这么好的文章表示感谢。


--  作者:卷积内核
--  发布时间:3/16/2009 10:56:00 AM

--  
第1章 C语言介绍

本教程假定你的系统已经安装了Lcc-win32编译系统。在学习本教程时你将会用到此编译程序。Lcc-win32是免费的,你可以到http://www.q-softwaresolutions.com/免费下载。

本教程不是一个完整的C语言教程,如果你从未学习过C语言编程,建议你先借助其它参考书(可参考本教程末尾列出的书本)学习C语言编程。尽管教程将从基础开始介绍,但是并未包含C语言的全部功能与特性。注意,这不是一个用来学习C语言编程的文档或参考书。在这里将对C标准库进行介绍,但并不详尽[1]。

在开始之前,先来回答这样一个问题:

1.1 为什么要学习C语言?

在刚刚出现的时候,C曾被广泛地批评,很多人迅速提出了它的问题和缺点。但随着编程语言的更替,C屹立不倒。Lcc-win32已发行了多年,有很多人,包括C语言的创作者丹尼斯·里奇(Dennis M Ritchie)[2]。对于这个问题,答案很简单:如果你想让你的软件长时间地存活,那就不要学“今天的编程语言”,而应该学习C。

C没有特别的亮点来打动你。它不是一种面向对象程序设计语言,但是如果你愿意,你可以用C进行面向对象的程序设计[3]。它不是一种功能性语言(functional language),但是如果你喜欢,你可以用C进行功能性编程(functional programming)[4]。大多数LISP解释程序和Scheme解释程序/编译程序是用C写的。你可以用C进行表单处理(list processing),当然不会像用LISP那样方便,但是你可以用C做到。它有很多诸如递归等精华编程的功能,所有的这些功能在这里将给予介绍。

-----------------------------------------------------------------------

1. 可以到"How to find more information"查看Lcc-win32的文档资料.
2. Dennis Ritchie编写了Lcc-win32的预处理程序
3. 对象C产生C,像Eiffel和其它的一些面向对象语言.C,正好因为这种程序模块的缺乏被适用于他们的表达. 甚至C++也是作为C编译程序的预处理程序开始的.
4. 查看“Illinois FP”语言在C的执行,很多其它的功能性编程语言是用C编写的.  



--  作者:卷积内核
--  发布时间:3/16/2009 10:57:00 AM

--  
很多人认为C缺乏Java的简易和C++模板的精密及其它吸引人的东西。诚然,C是一种简单的语言,没有任何装饰。但正是这些特性的缺乏使得C适合作为一种首先介绍的综合的高级语言,你可以很好的控制你的程序的运行而没有任何隐藏的东西。当你没有告诉编译程序做什么时,编译程序什么也会不做。尽管你将要用到的C编译程序整合了Java的一些特性如垃圾回收(the garbage collection),但C语言仍是透明的[5]。

各种语言不断更替,唯有C屹立不倒。在70年代它作为UNIX操作系统发展的核心[6],80年代作为微型计算机的更新的核心,而像C++、Delphi、Java及其它编程语言来了又去,但C依然保留自我本性。

1.2 程序结构

一个C语言程序由一个或多个被称为源模块的文本文件组成。每个这样的模块则由若干函数(即:一小块的代码完成一些任务[7])和数据(即:变量或表在程序开始时被初始化)组成。有一个名为main的特殊函数作为程序执行的开始[8]。在C语言中,文件里代码的结构是有语义的。主要的源码定义一个编译单位作为参数传递给编译程序[9]。

一个编译单位可以使用#include预处理指令输入通用的定义,或使用extern标识符来声明[10]。

C支持独立的编辑模块,即:你可以将你的程序分成几个独立的模块并分别对它们进行编译,然后再用连接程序将它们连接为最终程序。通常每个模块分别用不同的包含各种函数或数据声明的文本文件编写。模块之间的接口在一个用以描述程序的类型或函数对一些模块的可见性的“头文件”里。这些文件使用“.h”扩展名,它们分为两类,一种是系统文件,由Lcc-win32提供,另一种是私有文件,由用户自已建立。

------------------------------------------------------------------

5. Lisp和scheme,这两种面向表单语言早在几十年前已经使用垃圾自动回收.APL和其它解释程序也提供这种功能.同样Lcc-win32提供由Hans Boehm编写的垃圾回收器.
6. 在今天,像许多操作系统一样,Linux的核心完全是由C编写的.
7. 函数和过程在C里没有差别.一个过程是一个没有返回值的函数.
8. 实际上,程序开始的代码是main函数,当main函数返回时,程序即随之结束.更多细节请参考技术文件.
9. 任何一种程序的开始,在任何一种计算机的语言里,有两种主要的内存类型:程序的代码(code)段,即:程序将执行的机器指令序列.这个段有一个“入口” (“entry point”).上面提及的在C里面的“main”过程,或其它过程是作为入口使用的.程序的静态数据(data)段,即:程序一开始就确定的字符串,数字或表.这些数据可以被更进一步分为一个初始数据段,或仅仅是空的,当程序加载时这些保留的空间被操作系统初始化为0.
10. 你不能选择性的从其它的包含文件输入一些标识符.你只能选择输入全部或不输入.


--  作者:卷积内核
--  发布时间:3/16/2009 10:58:00 AM

--  
函数由参数表,函数体,以及可选的返回值组成[11]。函数体可以包含局部变量的声明(即:变量在程序执行到函数体时被激活)。

函数体为一系列由分号隔开的表达式。每一个语句可以是算法操作,赋值,函数调用,或是一个复合的语句(即:一个语句包含其它语句)。

1.3 Hello

我们将使用由C语言作者[12]给出的一个著名的例子来向读者介绍C的格式。我们编写一个在屏幕上打印出信息“Hello”的程序。

#include <stdio.h> (1)
int main(void) (2)
{ (3)
printf("Hello\n"); (4)
return 0; (5)
} (6)

1) 使用一个被称为‘预处理’的编译程序功能,你可以用“#include”指令原文地包含整个的C语言源文件。在这里我们包含一个编译程序标准库里的“stdio.h”头文件。你可能已经注意到头文件的名字是用一对 < > 括起来的,这表示告诉编译程序从标准包含目录里面查找该头文件,而不是在当前目录中寻找。如果你想从当前目录或其它目录里查找头文件,可以使用双引号将头文件名括起来,例如:#include "myfile.h"。

2) 我们定义了一个名为"main"的返回一个整数的函数,而且不接收参数(void)。[13]
所有使用C语言编写的程序都有一个名为"main"的函数,它是这些程序运行开始的地方。

3) 函数体由一对花括号( “{” 和 “}” )括起来的语句构成。

4) 我们调用标准函数"printf"来把字符串参数格式化的显示在屏幕上。在C语言里,函数调用是这样的:
函数名 (参数列表);
在这里所调用的函数的名称为"printf",其参数列表为一个字符串"Hello\n"[15]。
字符串是用一对引号括起来的。它们在C语言中用一个以空字符('\0')为结尾的字符数组表示。

------------------------------------------------------

11. 在C语言中,只能有一个返回值。尽管对于一个函数来说,可以通过修改它的环境来返回多个值。
12. 这是一个经典的例子,出自1974年B. W.Kernighan的C语言教程,这是在“The C programming Language”出版的4年之前。他们的例子到今天仍为人们所用,虽然编译时会带来一些警告:
main() { printf(“Hello world\n”); }
13. 这是两种用于定义"main"函数的方法之一。稍后我们将看到另一种方法。
14. 字符串可以包含一般的字符,也可以包含制表符如转行(\n)跳格符(\t),或是其它。详细请参考其它参考书。


--  作者:卷积内核
--  发布时间:3/16/2009 10:58:00 AM

--  
5) 函数运行结束后返回到调用函数的语句,可选,它可以返回一个结果,这里返回一个整数0。
6) 函数体结束花括号。

C语言程序使用扩展名为.c的文本文件保存。你可以使用任何你喜欢的文本编辑器编辑这些文件,但是在Lcc-win32里我们使用一个名为"Wedit"的特殊编辑器来完成这项工作。此程序可以很方便地输入程序文本,并以C程序的风格显示程序文本。

现在就来编写此程序,我们启动Wedit并输入上面的程序代码[15]。


1.3.1 程序输入
如果你知道集成式开发环境(IDE)是如何工作的,你可以略过此部分。

当你在lcc-win32的图标上双击时,你便打开了一个程序设计窗口,在这里你可以更容易地输入你的程序并且找到关于它的信息。当你首次打开它时,它将显示一个空白的窗口,等待你告诉它做些什么。

如图

你应该做的第一件事就是选择"文件"菜单里的"新建"-->"文件"。这将告诉IDE你想创建一个新的程序模块。你会激活一个对话框询问文件名称。这里输入"hello.c"就可以了。你将会看到打开了一个空白的窗口,在这里你可以输入程序的代码。你应该按照演示的那样输入程序,同时注意避免任何输入错误。记住: 机器不理解任何事物。如果你忘记了一个引号,或者其它的特殊符号,它将不能正常工作,并且编译程序将会被溢出的错误信息搞乱。(如果出现任何错误,请)仔细检查你输入的和你在上面所看见的是否一致。

当你完成程序代码的输入,你便可以使用"编译"菜单下的"构建"或按F9编译并连接程序[16].

要运行此程序,你可以使用"编译"菜单上的"执行"选项(Ctrl+F5),或打开一个命令行窗口键入程序的名字。让我们先做一次。

--------------------------------------------

15. 你可以双击Wedit的图标或在WINDOWS开始菜单的运行里输入完整路径运行Wedit,如:若你将Lcc-win32安装在c:\lcc那么Wedit将会在c:\lcc\bin\wedit.exe


--  作者:卷积内核
--  发布时间:3/16/2009 10:59:00 AM

--  
由于种种原因,我已经很少用lcc-win32了,这个教程的命运?现在还不知道。好了,索性把原来整理的翻译的都放上来吧。

首先,我们需要知道的是想要运行的程序的名字。这个容易:我们使用IDE(Wedit)"工具"(Utils)菜单中的"可执行文件信息"(Executable stats)选项。我们看到以下显示的信息。

我们看窗口下方面板的第一行,程序的执行调用:
h:\lcc\projects\hello.exe.[17]

我们打开命令行窗口,并键入:
C:\>h:\lcc\projects\lcc1\hello.exe(回车,DOS命令好歹地学一些)
Hello
C:\>

我们的程序按照我们想得那样显示了字符串"Hello"并开始了新的一行。如果我们抹去字符串中的\n,然后再按一次F9重新编译连接,显示的信息将会变为:
c:\>h:\lcc\projects\lcc1\hello.exe
Hello
C:\>


////////////////////////////////////////////////
编者注:
上面显示的程序运行的结果看起来是一样的,
其实懂一点语法就会知道\n表示换行,
不过这里真的看不出来,多编几个程序就会知道的。
////////////////////////////////////////////////


但我们如何知道显示一个字符串需要调用"printf"函数呢?

因为库的说明文档告诉我们如此…一个刚开始使用C的用户要做的第一件事便是浏览系统所提供的库函数,他/她就可以方便的调用常用的函数而不必浪费太多的时间去重新编写这些基本的函数。库中有千千万万个为各种任务编写的各种类型的函数(pre-built functions),printf便是这些函数中的一个。我们将在以后的内容中讨论它们。


1.3.2 什么是函数参数?

假如有一个这样的函数:

int fn(int a){ ...}

这个名为a的变量被复制到编译程序预留给函数参数的存储空间。注意函数fn用到的仅仅是一份拷贝,而不是原函数值。例如:

int fn1(int a)
{
a=a+7;
return a;
}
int fn2(void)
{
int b=7;

-------------------------------------------------------------------------
16. 如果如此不运行或收到警告,你可能在安装过程中出现问题(除非你键入错误).或程序有Bug.紧记若你用EMAIL告诉我程序的Bug,我将要解决安装程序的错误,所以详尽将你遇到的问题告诉我将对于我解决问题十分重要.

当你单击compiler 按钮Wedit会为你生成一个默认的工程.如果磁盘没有足够的空间时编译将出错,或安装时出错,Wedit找不到编译程序,又或其它原因.若当你见到错误信息时请勿惊慌,并尽量根据信息修正错误.

一个通常的错误是当你往一个不足空间安装一个旧版本的Wedit时,尽管会有醒目的警告提示你不应继续时,大多数人习惯不读警告而继续安装,这将导致Lcc-win32 无法工作.然后你便抱怨起我我.我已经在新的版本将之修正.但仍会有错误发生.

17. 下面几行输出的信息含义将在下文介绍。


--  作者:卷积内核
--  发布时间:3/16/2009 10:59:00 AM

--  
fn(b); //我认为是fn1(b),书上可能打错了
return b;
}
函数fn2总是返回7,因为函数fn1使用的是b的一份拷贝,而不是b本身。这就是参数的值传递。在标准C中,这条规则不会用于字符串。

当你看到一条这样的指令:
printf("Hello\n");

它将参数的首地址传递给"printf",而不是整个字符串的拷贝。这当然要比复制整个字符串有更高的效率,但是天下没有免费的午餐,代价是你所调用的函数有可能更改这个字符串。更多信息将在稍后给出。


1.3.3 控制台模式程序和Windows程序

Windows存在两种不同的模式:文本行模式和窗口模式。在本书的第一章我们使用控制台模式,即,程序运行在Windows的文本行模式下,只能接收文本输入并产生文本输出。它们要比构建复杂的GUI(图形用户界面)程序简单的多。

通过查看可执行程序的特定位置,Windows知道如何区分控制台模式/窗口模式的程序。如果程序是由用于控制台模式程序的编译器生成的,windows将默认打开一个黑底的窗口,并在它开始前初始化标准输入输出程序。如果程序是由用于窗口模式程序的编译器产生的,那么就什么也不做,你也不能够使用文本输入或输出的库函数。

由于历史原因,此窗口有时候被称为"DOS"窗口,尽管MSDOS已经离开有十多年了。这个运行在控制台窗口里的程序是32位的,如果它喜欢它可以打开一个窗口。它们可是使用windows所有的图形特性。唯一的缺点是,一个丑陋的黑色窗口始终是打开的,即使你打开一个新的窗口。

在lcc-win32中,你可以通过在"工程"-->"配置"的"连接器"选项卡中勾选相应的选项来改变所生成的程序类型。

在其他操作系统中情况是差不多的。Linux提供控制台模式,即便是Macintosh同样也有。在通常情况下,输入一条指令要比点击大量的菜单/选项来到达你想要的选项要快许多。除此之外,附加的好处就是控制台模式程序更容易自动化以及当作大程序的独立部件来接受命令行参数并在没有人类介入的情况下生成输出信息。

1.3.4 编译过程的概述

当你在编辑器里按下F9生成一个可执行文件时,将执行一系列复杂的操作,但所有的操作对于你是不可见的。下面将对此做一个概述,让你至少可以了解在后台发生了什么。

Wedit将调用C编译程序。这个程序在lcc的安装目录下的bin文件夹里,名为:lcc.exe。如:你将lcc安装在c:\lcc,则lcc.exe将放在c:\lcc\bin里。


--  作者:卷积内核
--  发布时间:3/16/2009 10:59:00 AM

--  
编译程序将读入你的源文件,并产生一个称为目标文件的文件,[18]其名字与你的源文件的名字相同,只是扩展名为.obj。C语言支持分开独立编辑模块,即:你可以将你的多个模块源文件分别产生多个目标文件,然后用连接程序lcclnk.exe连接产生可执行文件。

Lcclnk.exe是连接程序。它读入不同的目标文件,库文件或其它文件,并产生一个可执行文件或一个动态连接库DLL(dynamically loaded library)文件。

当你编译hello.c文件时,编译程序产生一个"hello.obj"文件,再由连接程序产生名为hello.exe的可执行文件。连接程序将使用储存在\lcc\lib目录下的多个文件来产生可执行文件,并将之与几乎所有的应用程序都用到的kernel32.dll、crtdll.dll等系统DLL文件绑定。

关于lcc编译程序如何工作的更多细节描述可在技术文档里找到,这里只介绍其主要步骤:

源文件被第一次预处理。#include指令被执行,包含文件里的代码被加入到源文件里。[19]

编译程序将预处理过的文件再次处理,产生一系列中间指令代码。[20]再由代码生成程序生成汇编程序代码。[21]

最后编译程序生成一个扩展名为.obj的目标文件。这个文件再经过连接程序的连接就产生了可执行文件。

组织并输入这些命令是让人厌烦的,为此,当你按下F9功能键时,IDE将自动为你完成全部操作。


1.3.5 术语

在上面的Wedit窗口里出现的术语的解释如下:

1 Size of code.
这个数字表示你的程序指令将要使用的字节数。包程序启动,相关函数代码连接生成的指令,如:printf,等等.
Lcc-win32使用标准windows动态连接库DRTDLL.DLL,windows自己提供的C运行库。尽管如此,但是如果你想支持最新的ANSI标准,那个库中的大多数函数是不可用的。
因此,他们被重新编写进连接程序使用的静态库文件libc.lib中。这就是为什么你在程序源代码中仅仅添一行,而编译生成的代码大小却猛地增加了几千字节。

------------------------------------------------------------
18. 当然这与面向对象程序设计毫无关系!

19. 当你调用编译程序时使用-E参数就可以见到处理的过程。例如你在命令行窗口中键入lcc –E hello.c你可以见到预处理的全过程,结果文件保存在hello.i中。

20. 同样,你可以通过lcc –Z hello.c见到生成的中间指令代码,其中间指令代码保存在文件hello.lil中。

21. 汇编程序代码可以通过lcc –S hello.c命令生成,汇编程序代码保存在文件hello.asm中。生成的文件包含C程序代码的列表及其对应的汇编程序代码。


--  作者:卷积内核
--  发布时间:3/16/2009 11:00:00 AM

--  
2 Size of initialized data.
这个数字表示表(tables)、常量(constants)以及其它初始数据使用的字节数。在这个例子中

,我们除了可以看到字符串"hello"还能看到其它从C库里面加进去的常量。

3 Uninitialized data.
这表示你的程序为变量保留的空间的大小,在程序启动时它们将由系统赋于值为0。

4 Size of image.
这表示程序加载时所用的全部内存空间,包括所有上面提及的部分。由于对列的原因这个数

字将会大于上述各值的代数和。这个大小一定是4096字节的整数倍,也就是一个page(page

指的什么,我不清楚)。


这意味着什么呢?

渐渐的,程序的大小不再那么受关注了。由于机器已经非常巨大了,所以许多人认为程序做

成2兆或20多兆都没有关系。当然,这里有一些争论,但是一般而言这是lcc-win32或C的哲学

。Wedit有多个工具来获取每一个函数的大小,并报告关于你程序的大小信息的摘要。记住,

程序越小越适合CPU的快速缓存(cache),并且执行的更快而所需的资源更少。


1.3.6 运行时环境

//这部分是今天现翻译的,因为当时的理解不够,怕翻译错了,结果翻译也就以这个借口卡

住了 leexuany//

程序运行在你的机器上。一个特殊的操作系统正在运行,一个确定的文件和硬盘结构,许多

的随机存储器碎片等等。这就是运行时环境。

由连接器lcclnk创建的文件因为一个用户行为(你双击它的图标)而启动或在命令行模式下

通过名字激活,或者由其它的程序请求操作系统来打开它。

操作系统进入硬盘的指定位置,将文件中的所有数据读入RAM中。接下来,它判断程序的起始

位置,并设置你电脑印刷电路上的程序计数器到那个内存地址。

开始的第一条指令是启动块("startup"stub,如果你不明白这是什么东西,请自行搜索PE各

式的相关信息),这个小程序完成一些初始化并调用"main"函数。它像对其他程序那样将参

数传递给"main"。

"main"函数通过调用C库中一个叫"printf"的函数开始执行。这个函数在控制台模式下输出一

些字符。这是一个相当简单的环境,对于那些不喜欢到处点来点去的人们,它很完美。

"printf"函数把字符堆放到终端仿真程序的输入缓冲中,这使得一些位(bits)使用当前的字

体改变颜色,并在准确的位置显示字形。

Windows调用你显卡的图形驱动控制这些位的改变。这些位改变的速度之快,快到在这段时间

里你的手不足以移动1毫米。今天的图形驱动非常快,他们返还控制权给printf函数几乎不需

要时间。

"printf"函数退出,并把控制权返还给"main"函数,接着在退出到启动块,并调用退出程序

,然后程序就被操作系统中止了。

Your hand is still near the return key.(不知道怎么翻译,应该是激励人继续学习的话

吧,就想钥匙就在你手中)


--  作者:zb1201
--  发布时间:3/16/2009 3:08:00 PM

--  
受教了,谢谢楼主供应

--  作者:sr030
--  发布时间:6/14/2010 12:25:00 AM

--  

W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
85.938ms