新说`HelloWorld`

基本上每门语言都是用"helloworld"做为她的第一讲,C语言也不例外。程序员

##传统HelloWorld

   传统的教材都是让你安装一种IDE集成环境,而后照例子敲入代码,按下ctrl+r之类的运行程序,感觉一下运行的结果。HelloWorld程序以下:shell

  • 在编辑器中敲入:
#include <stdlib.h>
#include <stdio.h>

void main() {
        printf("Hello World\n");
}
  • 按下Ctrl+r运行,固然我没有IDE环境,就用Linux终端代替一下了哈。以下:
[zhoukai@zhoukai-MBPR:tmp]$HelloWorld
  • 固然这里有几个问题
    1. 当时你其实不知道这个程序是怎么被编译出来的。
    2. 当时你也不知道她在什么环境下运行的。
    3. 当时你确定也不会去想,我这样写在其它计算机上能运行吗?
    4. 如今你确定也没有考虑过,我能不能换个花样玩玩呢?

##老生常谈

  • 为什么我说一个gcc程序和一个编辑器vim程序就是c的开发环境呢?而传统的书籍,特别是国内的c程序数据,以来就让你装一个大得一逼的IDE环境,之前是vb6.0几百MB,如今是vs2015之类的好几GB。而我所说的gcc、vim充其量就几个MB。由于IDE环境集成了太多功能了,编辑、编译、调试、自动补全、语法错误提示等等。我通常不用这样环境,初学者更不该该用,她会使你太依赖IDE,不了解原理,也少了不少乐趣。
  • 其实全部程序都是编译器或者解释器读取一个纯文本中的代码,而后对要么生成目标二进制文件(编译型语言),要么直接就运行(解释型语言)了。对于C语言,固然是编译后,生成有特定格式二进制文件,在计算终端中运行,终端也是个程序,她是操做系统的一部分,操做系统也是程序(一两句话说不清这个关系>_<)。像C这样的语言,有语法解释,中间文件编译,目标程序连接这三部曲构成,固然还能够细分。语法解释主要是判断语法正确与否,而后是将#include<stdlib.h>这样的语句进行预处理生成最终的代码源文件,而后编译成与特定类型的操做系统相关的目标代码,这个目标代码其实能够在只要与当时生产的操做系统类型相同的操做系统中重复使用的,而后是连接成目标文件,这个文件大多数也能够在相同的操做系统中直接使用,可是因为有的程序会依赖特定的库,因此出表现出不能运行成功的状况罢了。好比刚才的helloworld程序在类Unix系统中,使用以下操做生成可执行文件:
[zhoukai@zhoukai-MBPR:tmp]$gcc main.c

而后是运行:vim

[zhoukai@zhoukai-MBPR:tmp]$./a.out
[zhoukai@zhoukai-MBPR:tmp]$HelloWorld

能够执行环境就是开启一个终端程序,而后./a.out运行。编辑器

  • 这段代码在绝大多数类Unix操做系统中都被编译运行,甚至这个二进制文件若是在内核相同操做系统也能够直接运行,而不须要从新编译。可是你可能注意到这个程序在编译是产生了警告,由于她的main()入口函数不是标准的,即不是可移植的。

守则一:一个负责的程序员编写程序要考虑可移植性函数

  • 标准的能够移植性入口函数应该是这样的:
int main(int argc, const char *argv[]) { ... }

其中的形式参数的做用就是接收运行时传入的命令参数,后面咱们会讨论到。操作系统

  • 你真没想过怎么将这段代码换个花样玩? 这个不行,做为一个程序员,脑洞过小很差,脑洞须要大开的,有多大得开多大。

##换着花样玩

###使用全局复用调试

全局变量基本是没种语言都支持的,即为全局,便是对全部人可见code

  • 有一天你的老板说,如今咱们生意很差,咱们要改程序输出的内容,发发牢骚,而不是友好的问候。偏偏这要交给你来作,并且当时没有使用任何全局变量或着宏来代替这些输出内容,并且涉及的几十个文件多是零零散散的分布在好几百个位置中,那么恭喜你,即便使用多文件文本替换也是挺麻烦的事,并且你老是要修改好几十个文件。若是当时使用全局变量也很好用修改的。开发

    1. 找一个这些文件都会引用的头文件,好比叫着utils.h,添加以下外部变量声明:
extern char g_SayHello[];
  1. 再任何一个.c文件均可以,可是推荐仍是utils.c中,这个就是规范,下次,接手项目的人要修改哪一个文件.h有相似上面的外部引用,天然而然的就在对应的.c中寻找其定义了:
char g_SayHello[] = "Kick the bucket!";
  1. 修改helloworld.c,其实这个名字不合适,最好加一个前缀,表面她含有入口函数,main_helloworld.c:
...
#include "utils.h"
...
int main(int argc, const char *argv[]) {
        printf("%s\n", g_SayHello);
        return EXIT_SUCCESS;
}
  1. 加入新的源文件一块儿编译、运行以下:
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c
[zhoukai@zhoukai-MBPR:tmp]$ ./a.out
Kick the bucket!
[zhoukai@zhoukai-MBPR:tmp]$

###使用宏复用编译器

宏是c语言强有力的特性之一,不使用宏,你会作不少不讨喜的工做,可是滥用宏也会不讨喜,全部任何东西都有利有弊。就像没有坏人,就体现不出好人;没有细菌这样的微生物,满世界都是尸体同样。这个是一个哲学问题〜〜〜

  • 继续上面的情景。你好不容易完成了需求,结果你老板说,咱们须要在不一样操做系统上让运行程序发不一样的牢骚〜〜〜,虽然你心中有一万匹草泥马在狂奔,但为了工资忍了吧。显然上面的代码在不一样的计算机上发布时,若是在编译后须要让程序运行时显现一些不一样的东西,是须要修改源代码的,很不方便,作这样的事情,宏的优势就体现出来了,由于在编译时,能够给定参数定义一个宏让源代码相同的程序有不一样的行为:
    1. 仍是utils.h,添加以下宏:
//稍做解释,下面的宏定义是关联预编译条件宏使用,即若是没有定义宏,则定一个默认的宏
#ifndef SAY_HELLO
#define SAY_HELLO "Kick the bucket!"
#endif
  1. 修改main_helloworld.c:
int main(int argc, const char *argv[]) {
        printf("%s\n", SAY_HELLO);
        return EXIT_SUCCESS;
}
  1. 编译不一样的行为的程序:
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Drop\ dead\!\" -o a.out
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Go\ to\ hell\!\" -o ab.out
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Damn\ you\!\" -o abc.out
[zhoukai@zhoukai-MBPR:tmp]$ ./a.out
Drop dead!
[zhoukai@zhoukai-MBPR:tmp]$ ./ab.out
Go to hell!
[zhoukai@zhoukai-MBPR:tmp]$ ./abc.out
Damn you!
[zhoukai@zhoukai-MBPR:tmp]$

稍做解释,gcc最简单的使用就是gcc <源文件名>,而后就会生成默认的程序文件a.out,可是通常都但愿又一个自定义的程序文件名,因此加上选项参数-o <目标名>;在不加-c <源文件名>的状况下都是直接完成三部曲,生成可执行文件的;其它还有不少可选的参数,后面慢慢说。

  • 能够看到不一样的程序使用一样的源代码编译的,可是编译时能够重定义宏,从而改变其行为。这里使用了可选参数-Dmacro="string"(加上''只是由于shell环境须要转义双引号),这样能够将编译时宏参数带入预处理,从而替换默认的宏参数。

##结束语

为何要写这么多,其实也不是高深的代码。目的就是一个,你在编写代码的时候是否比别人多想了一步呢?是否考虑过代码的可移植性呢?是否考虑过代码的可复用性呢?是否考虑过代码的可维护性呢?这些!都是一名合格的程序员应该考虑的问题。

相关文章
相关标签/搜索