内容简介

1、课程大纲

2、第二部分第一课: 模块化编程

3、第二部分第二课预告: 进击的指针,C语言王牌


课程大纲

我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案。还会带大家用C语言编写三个游戏。

C语言编程基础知识

  • 什么是编程?

  • 工欲善其事,必先利其器

  • 你的第一个程序

  • 变量的世界

  • 运算那点事

  • 条件表达式

  • 循环语句

  • 实战:第一个C语言小游戏

  • 函数

  • 练习题

  • 习作:完善第一个C语言小游戏

C语言高级技术

  • 模块化编程

  • 进击的指针,C语言王牌

  • 数组

  • 字符串

  • 预处理

  • 创建你自己的变量类型

  • 文件读写

  • 动态分配

  • 实战:“悬挂小人”游戏

  • 安全的文本输入

  • 练习题

  • 习作:用自己的语言解释指针

用基于C语言的SDL库开发2D游戏

  • 安装SDL

  • 创建窗口和画布

  • 显示图像

  • 事件处理

  • 实战:“超级玛丽推箱子”游戏

  • 掌握时间的使用

  • 用SDL_ttf编辑文字

  • 用FMOD控制声音

  • 实战:可视化的声音谱线

  • 练习题

数据结构

  • 链表

  • 堆,栈和队列

  • 哈希表

  • 练习题


第二部分第一课:模块化编程

不好意思,小编生病了几天(发烧了),这一课内容极为多。所以现在才发布 :)

前些天微信上有朋友提议说,可以建一个讨论的群。凡是对编程感兴趣的朋友可以加,大家可以交流,互动,分享写的程序的源代码,等。

这个群已经建了,目前70多人了。

还有刚建立的QQ群:《程序员联盟》,群号:413981577

这段时间有不少朋友加我好友,但我不清楚谁是看了我的《程序员联盟》的文章之后加的,所以能否烦请大家给我发一个私信(“加程序员联盟”之类的),我会把发我私信的朋友加到这个微信的群里(程序员联盟)。

我的微信号可以在文章最后看到,发我邮件也行,邮箱也在最后。

目前也创建了《程序员联盟》的微社区,大家可以关注一下。

微社区地址和二维码如下:

http://m.wsq.qq.com/264152148

谢谢!


大家肯定注意到了,今天的主题图片我用了老爷子:Dennis Ritchie(C语言之父)的壁纸,略表敬仰和怀念之情。

还有一个原因:因为这一部分要开始难起来了,开始学习C语言的高级技术。

图片上的The Legend,是英语“传奇”的意思。我再说一次,2011年,乔布斯乔帮主也离开了我们,不过不要忘了这个“乔布斯站在其肩头”的伟大的人,Dennis Ritchie:C语言之父,Unix操作系统之父(与Ken Thompson一起),还有其他丰功伟业。

威尔.史密斯也在拍《我是传奇》3了。

以后应该拍一系列早期计算机黑客的传奇的电影,特别是丹尼斯.里奇,肯定得拍一部纪录片。

网上有一篇图文介绍,叫《Unix英烈传:图文细数十五位计算先驱》

http://www.linuxidc.com/Linux/2013-08/89395p2.htm

还有最后一个原因:老爷子长得帅啊,给我们程序员长脸啊。

《灌篮高手》里樱木花道称呼安西教练为老爷子,愿我们的Dennis Ritchie教练也“安息”。

好了,话休絮烦(已经很烦了你,发烧发傻了。。。),我们回归正题。


话说上一课是第一部分最后一课,现在开始第二部分的探索之旅!

在这一部分中,我们会学习C语言的高级技术。这一部分内容将是一座高峰,会挺难的。但是我们一起翻越。

一口是吃不成一个胖子的,但是小口小口,慢慢吃,还是能吃成胖子的嘛。所以要细水长流,肥油慢积。一路上有你(油腻)...

一旦你跟我们的课程一直到这一部分的结束,你将会掌握C语言的核心技术,也可以理解大部分C语言写的程序了。

之后在第三部分,我们就会一起来学习C语言图形编程,写C语言的2D游戏等等。

到目前为止我们的程序都只是在一个main.c文件里捣腾,因为我们的程序还很短小,这也足够。但是之后你的程序如果有了十多个函数,甚至上百个函数,那么你就会感到全部放在main.c一个文件里是多么拥挤和混乱。

正因为如此,计算机科学家才想出了模块化编程。原则很简单:与其把所有源代码都放在一个main.c当中,我们将把它们合理地分割,放到不同的文件里面。


函数原型

到目前为止,写自定义的函数的时候,我们都要求大家暂时把函数写在main函数的前面。

这是为什么呢?(唉呀妈呀,想到了蔡明和郭达那个小品。。。)

因为这里的顺序是一个重要的问题。如果你将自己定义的函数放置在main函数之前,电脑会读到它,会知道这个函数,然后当你在main函数中调用这个函数时,电脑知道这个函数,也知道到哪里去执行它。

但是假如你把这个函数写在main函数后面,那你在main函数里调用这个函数的时候,电脑就不认识它了,你可以自己写个程序测试一下。是的,很奇怪吧?这绝对有点任性的。

那你会说:“C语言岂不是设计得不好么?”

我完全同意(别让上面的老爷子听到了...)。但是请相信,这样设计应该也是有理由的,计算机先驱们早就想到了,也提出了解决之道。

下面我们就来学一个新的知识点,借着这个,你可以把你的自定义函数放在程序的任意位置。不必操心总是好事。

用来声明一个函数的“函数原型”

我们会声明我们的函数,用我们所说的术语:函数原型(prototype)。就好比你对电脑发出一个通知:“看,我的函数的原型在这里,你给我记住啦”。

我们来看一下我们上一课举的一个函数的例子(计算矩形面积):

double rectangleArea(double length, double width)

{

return length * width;

}

怎么来声明我们上面这个函数的原型呢?

  1. 复制,黏贴第一行

  2. 在最后放上一个分号;

  3. 把这一整行放置在main函数前面

简单吗?现在你就可以把你的函数的定义放在main函数后面啦,电脑也会认识它,因为你在main函数前面已经声明过这个函数了。

你的程序会变成这样:

#include <stdio.h>

#include <stdlib.h>

// 下面这一行是rectangleArea函数的函数原型 :

double rectangleArea(double length, double width);

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

{

printf("长为10,宽为5的矩形面积 = %f\n", rectangleArea(10, 5));

printf("长为3.5,宽为2.5的矩形面积 = %f\n", rectangleArea(3.5, 2.5));

printf("长为9.7,宽为4.2的矩形面积 = %f\n", rectangleArea(9.7, 4.2));

return 0;

}

// 现在我们的rectangleArea函数就可以放置在程序的任意位置啦 :

double rectangleArea(double length, double width)

{

return length * width;

}

与原先的程序相比有什么改变呢,其实就是在程序的开头加了函数的原型而已(记得不要忘了那个分号)。

函数的原型,其实是给电脑的一个提示或指示。比如上面的程序中,函数原型

double rectangleArea(double length, double width);

就是对电脑说:“老兄,存在一个函数,它的输入是哪几个参数,输出是什么类型”,这样就能让电脑更好地管理。

多亏了这一行代码,现在你的rectangleArea函数可以置于程序的任何位置啦。

记得:最好养成习惯,对于C语言程序,总是定义了函数,再写一下函数的原型。不写函数原型行不行?也行。只要你把每个函数的定义都放在main函数之前,但是你的程序慢慢会越来越大,等你有几十或者几百个函数的时候,你还顾得过来么?

所以养成好习惯,不吃亏的。

你也许注意到了,main函数没有函数原型。因为不需要,main函数是每个C程序必须的入口函数。(人家有钱,跟编译器关系好,编译器对main函数很熟悉,是经常打交道的哥们,所以不需要函数原型来“介绍”main函数。)。

还有一点,在写函数原型的时候,对于圆括号里的函数参数,名字是不一定要写的,可以只写类型,因为函数原型只是给电脑做个介绍,所以电脑只需要知道输入的参数是什么类型就够了,不需要知道名字。所以我们以上的函数原型也可以简写如下:

double rectangleArea(double, double);

看到了吗,我们可以省略length和width这两个变量名,只保留double(双精度浮点型)这个类型名字。

注意:千万不要忘了函数原型末尾的分号,因为这是编译器区分函数原型和函数定义开头的重要指标。如果没有分号,编译时会出现比较难理解的错误提示。


头文件

每次看到这个术语,我都想到刚刚结婚的“我们的青春”:周杰伦 的《头文字D》。在此祝福一下杰伦幸福美满。

到目前为止,我们的程序只有一个.c文件(称之为“源文件”),比如我们之前把这个.c文件命名为main.c,当然名字是无所谓的。你叫hello.c,hehe.c都行。

一个项目多个文件

在实际编写程序的时候,你的项目一般肯定不会把代码都写在一个main.c文件中。当然,可行是可行的。但是,试想一下,如果你把所有代码都塞到这一个main.c文件中,那代码量可能10000多行,你要在里面找一个东西太难了。也正是因为这样,通常我们每一个项目都会创建多个文件。

那以上说到的项目是指什么呢?

之前用Code::Blocks这个IDE创建第一个C语言项目的时候,其实有接触过。但是我们会再解释一下。

一个项目(英语是 project),简单来说是指你的程序的所有源代码(还有一些其他的文件),项目里面的文件有多种类型。

目前我们的项目还只有一个源文件:main.c

看一下你的IDE,一般来说项目是列在左边。

如上图,你可以看到,这个项目(在Projects一栏里)只有一个文件:main.c

现在我们再来展示一个包含好多个文件的项目:

上图中,我们可以看到在这个项目里有好几个文件。实际中的项目大多是这样的,你看到那个main.c文件了吗?通常来说在我们的程序中,会把main函数只定义在main.c当中。当然不是一定非要这样,每个人都有自己的编程风格。不过希望跟着这个课程学习的读者,可以和我们保持一致的风格,方便理解。

那你又要问了:“为什么创建多个文件呢?我怎么知道为我的项目创建几个文件合适呢?”

答案是:这是你的选择。通常来说,我们把同一主题的函数放在一个文件里。

.h文件和.c文件

在上图中,我们可以看到有两种类型的文件:一种是以.h结尾的,一种是以.c结尾的。

.h文件:称为“头文件”,这些文件包含了函数的原型

.c文件:称为“源文件”,包含了函数本身(定义)

所以,通常来说我们不常把函数原型放在.c文件中,而是放在.h文件中,除非你的程序很小。

对每个.c文件,都有同名的.h文件。上面的项目那个图中,你可以看到.h和.c文件一一对应。

files.h和files.c

editor.h和editor.c

game.h和game.c

但我们的电脑怎么知道函数原型是在.c文件之外的另一种文件里呢?

需要用到我们之前介绍过的预处理指令 #include来将其引入到.c文件中。

请做好准备,下面要有一波“密集”的知识点“来袭”。

怎么引入一个头文件呢?其实你已经知道怎么做了,之前的课程我们已经写过了。

比如我们来看我们上面的game.c文件的开头

#include <stdlib.h>

#include <stdio.h>

#include "game.h"



void player(SDL_Surface* ecran)

{

// ...

}

看到了吗,其实你早就熟悉了,要引入头文件,只需要用#include这个预处理指令

因此我们在game.c源文件中一共引入了三个头文件: stdlib.h, stdio.h,game.h

注意到一个不同点了吗?

在标准库的头文件(stdlib.h, stdio.h)和你自己定义的头文件(game.h)的引入方式是有点区别的:

<>用于引入标准库的头文件,在IDE中一般位于安装目录的include文件夹中,在linux中则一般位于系统的include文件夹里

""用于引入自定义的头文件,位于你自己的项目的目录中

我们再来看一下我们的对应的game.h这个头文件的内容:

看到了吗,.h文件中存放的是函数原型.

你已经对一个项目有大致概念了

那你又会问了:“为什么要这样安排呢?把函数原型放在.h头文件中,在.c源文件中用#include引入,为什么不把函数原型写在.c文件中呢?”

答案是:方便管理,条理清晰,不容易出错,省心

因为如前所述,你的电脑在调用一个函数前必须先“知道”这个函数,我们需要函数原型来让使用这个函数的其他函数预先知道

如果用了.h头文件的管理方法,在每一个.c文件开头只要用#include这个指令来引入头文件的所有内容,那么头文件中声明的所有函数原型都被当前.c文件所知道了,你就不用再操心那些函数的定义顺序或者有没有被其他函数知道

例如我的main.c函数要使用functions.c文件中的函数,那我只要在main.c的开头写 #include "functions.h",之后我在main.c函数中就可以调用function.c中定义的函数了

你可能又要问了:“那我怎么在项目中加入新的.h和.c文件呢?”

很简单,在codeblocks里,鼠标右键点击项目列表的主菜单处,选择Add Files

或者

在菜单栏上依次单击

Fiel->New->File...

就可以选择添加文件的类型了

引入标准库

你脑海里肯定出现一个问题:如果我们用#include来引入stdio.h和stdlib.h这样的标准库的头文件,而这些文件又不是我自己写的,那么它们肯定存在于电脑里的某个地方,我们可以去找到,对吧?

是的,完全正确!

如果你使用的是IDE(集成开发环境),那么它们一般就在你的IDE的安装目录里。如果是在纯linux环境下,那就要到系统文件夹里去找,这里不讨论了,以后开了Linux课会讲到,感兴趣的读者可以去网上搜索。

在我的情况,因为安装的是codeblocks这个IDE,所以在windows下,我的头文件们“隐藏”在这个路径下:

E:\Program Files\CodeBlocks\MinGW\include

一般来说,都在一个叫做“include”的文件夹里。在里面,你会找到很多文件,都是.h文件,也就是C语言系统定义的标准头文件,也就是系统库的头文件(对windows,mac,linux都是通用的,C语言本来就是可移植的嘛)。在这众多的头文件当中,你可以找到我们的老朋友:stdio.h和stdlib.h。

你可以双击打开这些文件或者选择你喜欢的文本编辑器来打开,不过也许你会吓一跳,因为这些文件里的内容很多,而且好些是我们还没学到的用法,比如除了#include以外的其他的预处理指令。

你可以看到这些头文件中充满了函数原型,比如你可以在stdio.h中找到printf函数的原型。

你要问了:“ok,现在我已经知道标准库的头文件在哪里了,那与之对应的标准库的源文件(.c文件)在哪里呢?”

不好意思,你见不到它们啦。因为.c文件已经被事先编译好,转换成计算机能理解的二进制码了。

“伊人已去,年华不复,吾将何去何从?”

既然见不到原先的它们了,至少让我见一下“美图秀秀”之后的它们吧…

可以啊,你在一个叫lib的文件夹下面就可以找到,在我的windows下的路经为:

E:\Program Files\CodeBlocks\MinGW\lib

被编译成二进制码的.c文件,有了一个新的后缀名:.a(在codeblocks的情况,因为编译器是mingw)或者.lib(在visual c++的情况,因为编译器是Visual),当然以后我们还会学到在linux下还有.so这个后缀名,等。暂时不深究。

这些被编译之后的文件被叫做库文件或library文件(library,英语:库),不要试着去阅读这些文件的内容,完全不是人看得懂的乱码…

学到这里可能有点晕,不过继续看下去就会渐渐明朗起来,下面的章节会有示意图帮助理解。

小结一下:

在我们的.c源文件中,我们可以用#include这个预处理指令来引入标准库的.h头文件或自己定义的头文件,这样我们就能使用标准库所定义的printf这样的函数,这样电脑就认识了这些函数(借着.h文件中的函数原型),就可以检验你调用这些函数时有没有用对,比如函数的参数个数,返回值类型等。


分开编译

现在我们知道了一个项目是由若干文件组成的,那我们就可以来了解一下编译器的工作原理; 之前的课里面展示的编译示例图是比较简化的,下图是一幅编译原理的略微详细的图,希望大家用心理解并记住:

上图将编译时所发生的事情基本详细展示了,我们来仔细分析:

1. 预处理器(preprocessor):顾名思义,预处理器为编译做一些预备工作,所以预处理器是在编译之前启动的。它的任务是执行特殊的指令,这些指令是通过预处理命令给出的,预处理命令以#开头,很容易辨认。

预处理指令有好多种,目前我们学过的只有#include,它使我们可以在一个文件中引入另一个文件的内容。#include这个预处理指令也是最常用的。

预处理器会把#include所在的那一句话替换为它所引入的头文件的内容,比如

#include <stdio.h>

预处理器在执行时会把上面这句指令替换为stdio.h文件的内容。所以到了编译的时候,你的.c文件的内容会变多,包含了所有引入的头文件的内容,显得比较臃肿。

2. 编译(compilation):这是核心的步骤,以前的课我们说过,正是编译把我们人写的代码转换成计算机能理解的二进制码(0和1组成)。编译器编译一个个.c文件。对于codeblocks这样的IDE来说,就是你放在项目列表中的所有.c文件;如果你是用gcc来编译,那么你要指定编译哪几个.c文件。

编译器会把.c文件先转换成.o文件(有的编译器会生成.obj文件),.o文件一般叫做“目标文件”(o就是英语object:目标 的首字母),是临时的二进制文件,会用于之后生成最终的可执行二进制文件。

.o文件一般会在编译完成后被删除(根据你的IDE的设置)。从某种程度上来说.o文件虽然是临时中间文件,好像没什么大用,但保留着不删除也是有好处:假如项目有10个.c文件,编译后生成了10个.o文件。之后你只修改了其中的一个.c文件,如果重新编译,那么编译器不会为其他9个.c文件重新生成.o文件了,只会重新生成你更改的那个。节省资源。

3. 链接器(linker):顾名思义,链接器的作用是链接,链接什么呢?就是编译器生成的.o文件,链接器把所有.o文件链接器来,“制作成”一个大块头:最终的可执行文件(windows下是.exe文件,linux下有不少种形式)。

现在你知道代码生成一个可执行程序的内部原理了吧,下面我们要展示给大家的这张图,很重要,希望大家理解并记住。

大部分的错误都会在编译阶段被显示,但也有一些是在链接的时候显示,有可能是少了.o文件之类。

上面那幅图其实还不够完整,你可能想到了:我们用.h文件引入了标准库的头文件的内容(里面主要是函数原型),函数的具体实现的代码我们还没引入呢,怎么办呢?对了,就是之前提到过的.a或.lib这样的库文件(由标准库的.c源文件编译而成)。

所以我们的链接器(linker)的活还没完呢,它还需要负责链接标准库文件,把你自己的.c文件编译生成的.o目标文件和标准库文件整合在一起,然后链接成最终的可执行文件。如下图所示:

这下我们的示意图终于完整了

这样我们才有了一个完整的可执行文件,里面有它需要的所有指令的定义,比如printf的定义

之后在第三部分我们会用到系统的图形库,也是在.a库文件中定义的,包含了一系列指令的定义,比如告诉电脑怎么创建一个窗口,绘制图形,等等,好戏在后头


变量和函数的作用范围

为了结束我们这一课,我们还必须来学习最后一个知识点:

变量和函数的作用范围(有效范围)。

我们将学习它们什么时候是可以被调用的。

函数的私有变量(局部变量)

当你在一个函数里定义了一个变量之后,这个变量会在函数结尾时从内存中被删除。

int multipleTwo(int number)

{

   int result = 0;  // 变量result在内存中被创建



   result = 2 * number;

   return result;

} // 函数结束,变量result从内存中被删除

在一个函数里定义的变量,只在函数运行期间存在。这意味着什么呢?意味着你不能从另一个函数中调用它。

#include <stdio.h>

int multipleTwo(int number);



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

{

   printf("15的两倍是 %d\n", multipleTwo(15));

  

   printf("15的两倍是 %d", result);  // 错误!



   return 0;

}



int multipleTwo(int number)

{

   int result = 0;



   result = 2 * number;

   return result;

}

可以看到,在main函数中,我们试着调用result这个变量,但是因为这个变量是在multipleTwo函数中定义的,在main函数中就不能调用,会出错。

记住:在函数里定义的变量只能在函数内部使用,我们称之为 局部变量。

全局变量:避免使用

能被所有文件使用的全局变量

我们可以定义能被项目的所有文件的所有函数调用的变量。我们会展示给大家怎么做,为了告知大家这方法存在,但是一般来说,要避免使用能被所有文件使用的全局变量。可能这样做一开始会让你的代码简单一些,但是不久你就会为之烦恼了。

为了创建能被所有函数调用的全局变量,我们须要在函数之外定义。通常我们把这样的变量放在程序的开头,#include预处理指令的后面。

#include <stdio.h>



int result = 0; // 定义全局变量result



void multipleTwo(int number); // 函数原型



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

{

   multipleTwo(15);  // 调用multipleTwo函数,使全局变量result的值变为原来的两倍

printf("15的两倍是 %d\n", result);  // 我们可以调用变量 result 

   return 0;

}



void multipleTwo(int number)

{

   result = 2 * number;

}

上面的程序中,我们的函数multipleTwo不再有返回值了,而是用于将result这个全局变量的值变成2倍。之后main函数可以再使用result这个变量。

由于这里的result变量是一个完全开放的全局变量,所以它可以被项目的所有文件调用,也就能被所有文件的任何函数调用。

:这种类型的变量是很不推荐使用的,因为不安全。一般用函数里的return语句来返回一个变量的值。

只能在一个文件里被访问的全局变量

刚才我们学习的完全开放的全局变量可以被项目的所有文件访问。我们也可以使一个全局变量只能被它所在的那个文件调用。就是说它可以被自己所在的那个文件的所有函数调用,但不能被项目的其他文件的函数调用。

怎么做呢?

只需要在变量前面加上 static 这个关键字。如下所示:

static int result = 0;

static在英语里是“静止的,不变的”之意。

函数的static(静态)变量

注意:如果你在声明一个函数内部的变量时,在前面加上static这个关键字,它的涵义和加在上面我们演示的全局变量之前是不同的。函数内部的变量如果加了static,那么在函数结束后,这个变量也不会销毁,它的值会保持。下一次我们再调用这个函数时,此变量会延用上一次的值。

例如:

int multipleTwo(int number)

{

   static int result = 0;   // 静态变量result在函数第一次被调用时创建

result = 2 * number;

   return result;

} // 变量result在函数结束时不会被销毁

这到底意味着什么呢?

就是说:result这个变量的值,在下次我们调用这个函数时,会延用上一次结束调用时的值。

有点晕是吗?不要紧。来看一个小程序,以便加深理解:

#include <stdio.h>

int increment();



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

{

   printf("%d\n", increment());

   printf("%d\n", increment());

   printf("%d\n", increment());

   printf("%d\n", increment());



   return 0;

}



int increment()

{

   static int number = 0;

   

   number++;

   return number;

}

上述程序中,在我们第一次调用increment函数时,number变量被创建,初始值为0,然后对其做自增操作(++运算符),所以number的值变为1,函数结束后,number变量被没有从内存中被删除,而是保存着1这个值。

之后,当我们第二次调用increment函数时,变量number的声明语句(static int number = 0;)会被跳过不执行(因为变量number还在内存里呢。你想一个皇帝还没驾崩,太子怎么能继位呢)。我们继续使用上一次创建的number变量,这时候变量的值沿用第一次increment函数调用结束后的值:1,再对它做++操作(自加1),number的值就变为2了。依此类推,第三次调用increment函数后number的值为3,第四次number的值为4

所以运行程序,输出如下:

1

2

3

4



一个文件中的局部函数(本地函数或静态函数)

我们用函数的作用域来结束我们关于变量和函数的作用域的学习。

正常来说,当你在一个.c源文件中创建了一个函数,那它就是全局的,可以被项目中所有其他.c文件调用。

但是有时我们需要创建只能被本文件调用的函数,怎么做呢?

聪明如你肯定想到了:对了,就是使用static关键字,与变量类似。把它放在函数前面。如下:

static int multipleTwo(int number)

{

   // 指令

}

现在,你的函数就只能被同一个文件中的其他函数调用了,项目中的其他文件中的函数就只可远观而不可亵玩焉…



总结一下变量的所有可能的作用范围:

  1. 在函数体内定义的变量,如果前面没加static关键字,则是局部变量,在函数结束时被删除,只能在本函数内被使用

  2. 在函数体内定义,但是前面加了static关键字,则为静态变量,在函数结束时不被删除,其值也会保留。

  3. 在函数外面定义的变量被称为全局变量,如果前面没有static关键字,则其作用范围是整个项目的所有文件,就是说它可以被项目的所有文件的函数调用。

  4. 函数外面定义的变量,如果前面加了static关键字,那就只能被本文件的所有函数调用,而不能被项目其他的文件的函数调用。

同样地,总结一下函数的所有可能的作用范围:

  1. 一个函数在默认情况下是可以被项目的所有文件的函数调用的。

  2. 如果我们想要一个函数只能被本文件的函数所调用,只需要在函数前加上static关键字。


总结

  1. 一个程序包含一个或多个.c文件(一般称为 源文件,source。当然我们一般也把所有的高级语言代码叫做源代码)。通常来说,每个.c文件都有一个和它同名但不同扩展名的.h文件(有点像同父异母的兄弟)。.c文件里面包含了函数的实际定义,而.h文件里包含函数的原型声明。

  2. .h文件的内容被一个叫做预处理器(preprocessor)的程序引入到.c文件的开头。

  3. .c文件被一个叫做编译器(compiler)的程序转换成.o的二进制目标文件(o是英语object的首字母,表示“对象、目标”)。

  4. .o文件又被一个叫做链接器(linker)的程序连接成一个最终的.exe可执行文件(exe是英语executable的前三个字母,表示“可执行的”。在windows操作系统里可执行程序的扩展名是.exe,在linux系统里,可执行程序有不少扩展名(.elf,等),也可以没有扩展名)

  5. 变量和函数都有“有效范围”,某些时候是访问不到的。


第二部分第二课预告:

今天的课就到这里,一起加油咯。

下一次我们学习第二部分第二课,咳咳,我必须正襟危坐,假装严肃地来宣布一下:

来认识一下C语言的精华和王牌:指针 吧!


程序员联盟
微信公众号*您若觉得本文不错,请点击画面右上角《···》按钮“分享到朋友圈”或“发送给朋友”

*新朋友请关注「程序员联盟」微信搜公众号
 ProgrammerLeague

小编微信号:frogoscar

小编QQ号:  379641629

小编邮箱:    enmingx@gmail.com

(微信和邮箱最常用)

“程序员联盟”公众号专为程序员,App设计师,各位喜爱编程和热爱分享的小伙伴们推送各样编程相关知识,优秀软件推荐,业界动态等。搜索ProgrammerLeague
加关注~

持续关注 程序员联盟 微信公众号,更多有趣,有料,有亮点的内容等着你哦!

【C语言探索之旅】 第二部分第一课:模块化编程的更多相关文章

  1. 【C语言探索之旅】 第三课:你的第一个程序

    内容简介 1.课程大纲 2.第一部分第三课:你的第一个程序 3.第一部分第四课预告:变量的世界 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个 ...

  2. 【C语言探索之旅】 第一部分第九课:函数

    内容简介 1.课程大纲 2.第一部分第九课:函数 3.第一部分第十课预告: 练习题+习作 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. ...

  3. 【C++探索之旅】开宗明义+第一部分第一课:什么是C++?

    内容简介 1.课程大纲 2.第一部分第一课:什么是C++? 3.第一部分第二课预告:C++编程的必要软件 开宗明义 亲爱的读者,您是否对C++感兴趣,但是C++看起来很难,或者别人对你说C++挺难的, ...

  4. 【Linux探索之旅】开宗明义+第一部分第一课:什么是Linux?

    内容简介 1.课程大纲 2.第一部分第一课:什么是Linux? 3.第一部分第二课预告:下载Linux,免费的噢!   开宗明义 我们总听到别人说:Linux挺复杂的,是给那些追求逼格的程序员用的.咱 ...

  5. 【Web探索之旅】第二部分第一课:客户端语言

    内容简介 1.第二部分第一课:客户端语言 2.第二部分第二课预告:服务器语言 第二部分:Web编程语言和工具 大家好.上一个部分我们学习了Web的一些基本概念: 什么是Web? Internet和We ...

  6. 【C语言探索之旅】 第三部分第二课:SDL开发游戏之创建窗口和画布

    内容简介 1.第三部分第二课: SDL开发游戏之创建窗口和画布 2.第三部分第三课预告: SDL开发游戏之显示图像 第三部分第二课:SDL开发游戏之创建窗口和画布 在上一课中,我们对SDL这个开源库做 ...

  7. 【C语言探索之旅】 第二部分第二课:进击的指针,C语言的王牌!

    内容简介 1.课程大纲 2.第二部分第二课: 进击的指针,C语言的王牌 3.第二部分第三课预告: 数组 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言 ...

  8. 【C语言探索之旅】 第一部分第十课:练习题+习作

    内容简介 1.课程大纲 2.第一部分第十课: 练习题+习作 3.第二部分第一课预告: 模块化编程 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三 ...

  9. 【C++探索之旅】第二部分第一课:面向对象初探,string的惊天内幕

    内容简单介绍 1.第二部分第一课:面向对象初探.string的惊天内幕 2.第二部分第二课预告:掀起了"类"的盖头来(一) 面向对象初探,string的惊天内幕 上一课<[C ...

随机推荐

  1. 别样JAVA学习(五)继承上(1.0)Object类equals()

    上一节继承下(一)我们进行抽象类.接口以及多态的学习. 接下来大家我们讲点特殊的东西就是object类, 我们一直在说继承,子继承了父,父还有没有父类呢, 为什么这么思考,大家想构造函数的第一行是不是 ...

  2. Android开发系列(二十二):AdapterViewFlipper的功能和使用方法

    AdapterViewFlipper继承了AdapterViewAnimator,它会显示一个View组件,能够通过showPrevious()和showNext()方法控制组件显示上一个.下一个组件 ...

  3. The method getDispatcherType() is undefined for the type HttpServletRequest 升级到tomcat8(转)

    配置项目,从tomcat低版本,放到tomcat8时,正常的项目居然报错了: The method getDispatcherType() is undefined for the type Http ...

  4. Linux档案种类与扩展名(2013.09.03)

    档案种类: 正规档案(regular file ):    第一个字符为 [ -],例如 [-rwxrwxrwx ].另外,依照档案的内容,又大略可以分为:     纯文本档(ASCII)     二 ...

  5. 【前端攻略】:玩转图片Base64编码(转)

    引言 图片处理在前端工作中可谓占据了很重要的一壁江山.而图片的Base64编码可能相对一些人而言比较陌生,本文不是从纯技术的角度去讨论图片的base64编码.标题略大,不过只是希望通过一些浅显的论述, ...

  6. Flipping Game(枚举)

    Flipping Game time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  7. UVa 442 Matrix Chain Multiplication(矩阵链,模拟栈)

    意甲冠军  由于矩阵乘法计算链表达的数量,需要的计算  后的电流等于行的矩阵的矩阵的列数  他们乘足够的人才  非法输出error 输入是严格合法的  即使仅仅有两个相乘也会用括号括起来  并且括号中 ...

  8. 宏碁宣布Liquid Jade智能机和Leap袖口

    据科技网站Android Community 4月29日覆盖,宏碁29公布的新智能机Liquid Jade而随着智能手镯部署Liquid Leap.尽管宏碁已经宣布了一项新的外部基本信息.但价格格和商 ...

  9. ASP.NET MVC源码分析

    MVC4 源码分析(Visual studio 2012/2013) HttpModule中重要的UrlRoutingModule 9:this.OnApplicationPostResolveReq ...

  10. ASP.NET回车提交事务

    浅析ASP.NET回车提交事件[转] ASP.NET回车提交事件其实说到底并不是ASP.NET 的编程问题,却是关于html form 中的submit 按钮就是如何规划的具体讨论. 也可归于ASP. ...