A Simple Example of C
#include <stdio.h>
int main(void) /* a simple program */
{
int num; /* define a variable called num */
num = 1; /* assign a value to num */
printf("I am a simple "); /* use the printf() function */
printf("computer.\n");
printf("My favorite number is %d because it is first.\n",num);
return 0;
}
#include Directives and Header Files
#include <stdio.h>
#include 是C预处理命令的一个例子.
通常来说,C编译器在编译源码之前,会进行一些准备工作,这些工作被称为预处理.
stdio.h
文件是所有C编译器的一部分. 是standard input/output header的缩写. 人们把位于文件头部的信息的集合称为头文件,并且C的实现通常有很多头文件.
总的来说,头文件帮助编译器将你的程序结合起来.
Why Input and Output Are Not Built In
- 不是所有的程序使用这个I/O包. 而C哲学的一部分就是为了避免携带不必要的负担. 这种经济使用资源的原理让的C被嵌入式编程所欢迎.
- #include 甚至不是C语言的语句. 第一列的#让这行代码在编译器编译之前,先被C处理器处理.
The main() Function
int main(void)
C程序总是从main函数开始执行. 你可以随意选择其他函数名,但是main函数必须作为开始函数存在. 括号的作用是将main视为一个函数.
函数是C程序最基本的模块.
int
是main函数返回值的类型. 这意味着,main函数可以范围的值的类型是一个整数. 而这个值返回到操作系统中.
函数名后面的括号通常都包含着发送给函数的信息. 以这个程序为例, 没有什么东西被传输给main函数,因此括号里面包含着void.
虽然void main()
和main()
有一些编译器是允许这种写法的,但是他们都不算标准的. 坚持最标准的形式,只有这样,你在将程序从一个编译器移植到另一个编译器的时候,才不会遇到问题.
Comments
/* a simple program */
程序中被/* */
符号包围的部分叫注释. 使用注释是为了让人(包括你自己)跟容易理解你的程序. 注释的一个特性是它可以放在程序的任何位置, 即使是和它所解释的部分在同一行. 一个更长的注释写成一行,也可以写成多行. 在/* */
之间的所有东西都会被编译器忽略. 下面是一些有效和无效的注释形式:
/* This is a C comment. */
/* This comment, being somewhat wordy, is spread over
two lines. */
/*
You can do this, too.
*/
/* But this is invalid because there is no end marker.
C99标准添加了第二种注释的方式,一种被C++和Java所欢迎的方式. 新的方式使用//
来创建一行注释.
// Here is a comment confined to one line.
int rigue; // Such comments can go here, too.
由于一行的结尾意味着注释的结束,因此这种方式需要注释符在一行的开头.
这种新的形式是为了避免旧形式可能出现的问题. 假设你有以下代码:
/*
I hope this works.
*/
x = 100;
y = 200;
/* Now for something else. */
下面你想要删除第四行,并且不小心也删除了第三行. 那么代码就变成了:
/*
I hope this works.
y = 200;
/* Now for something else. */
编译器现在就会将第一行的/*
和第四行的*/
匹配起来,将这四行都变成注释.
由于//
形式的注释不会扩展超过一行,它不会导致这种代码消失的问题.
有些编译器可能不会支持这种性质,另一些可能需要将编译器的设置更改成允许C99或C11的性质.
Braces,Bodies,and Blocks
{
...
}
在上面的例程中,大括号限定这函数的界限. 通常来说,所有的C函数都使用大括号标示着函数的开始和结尾. 他们是强制存在的,不能省略.
并且只有大括号能作为这个目的,小括号和中括号都不行.
大括号也用来作为在函数中结合一个单元或块的语句.
Declarations
int num
程序中的这一行被程序,声明语句. 声明语句是C最重要的性质. 这个例子声明了两个事情. 第一,在函数中,你有一个名为num的变量. 第二,int表明num是一个整数. 编译器根据这个信息,在内存中位num变量开辟一个适当的存储空间. 在声明语句结尾的分号,将这行代码作为C语句或指令. 这个分号是语句的一部分,并不只是语句之间的划分符号.
int
是C用来指示一种基本数据类型的关键字. 关键字是用来表示一种的词,并且你不能使用他们作为其他用途. 例如,你不能用int
作为函数名或者变量名.
num
是一个标识符,即你选择的变量,函数或其他实例的名字. 因此, 声明将一个特定的标识符和计算机内存中特定的位置联系起来,并且在那个地址上也表明了数据类型.
在C语言中,所有的变量在使用前必须被声明. 这意味着,你必须列出所有你在程序中使用的变量,以及每个变量的数据类型.
声明变量被视作一种编程好技巧.并且在C语言中,这是必须的.
传统地,C语言要求变量必须在其他所有语句之前被声明. 那意味着main函数主体可能这样:
int main()
{
int doors;
int dogs;
doors = 5;
dogs = 3;
// other statement
}
C99和C11,允许你将声明放在任何地方. 但是你仍热需要在变量使用前,将其声明. 所以,如果你的编译器允许这种性质,有的代码像这样:
int main()
{
int doors;
doors = 5;
int dogs;
dogs = 3;
}
为了和旧系统更好的兼容,这本书会坚持传统的方式.
-
数据类型
C语言处理多种数据类型,像整数,字符和浮点数. 声明一个变量为整数或字符类型使得它能够被计算机正确地存储,获取和编译.
-
名字选择
你需要为变量选择有意义的名字. 如果名字不够,就使用注释来解释这个变量代表什么. 通过这种方式记录程序,是良好编程的基本技巧之一.
C99和C11标准使你能够用任意长度的标识名,但是编译器只会考虑前63个字符. 对于外部标识符,只有31个字符需要被考虑.
实际上,你可以使用超过最大数的字符,但是编译器没有被要求注意到多余的字符. 这意味着,如果你有两个标识符,每个都63个字符长度,他们除了一个字符,其他都相同,则编译器会将这两个视为不同的. 如果你有两个64字符的标识符,并且除了最后一个字符,其他都相同,编译器可能会将这两个标识符认为是不同的,也可能认为是相同的. C99和C11标准并没有定义那种情况下会发生什么
你可以使用的字符包括小写字符,大写字符,数字和下划线. 第一个字符必须是字母或者下划线.
操作系统和C库经常使用带有一个或两个下划线的标识符,所以你最好避免使用这种. 标准将像库标识符这样开头带有一个或两个下划线的字符标为reserved. 这意味着,尽管使用他们不会造成语法错误,但是会造成名字冲突.
C的名字是区分大小写的.
- 四个声明变量的好处
- 将所有变量放在一个地方,方便读者了解程序是做什么的. 这尤其是当你给你的变量有意义的名字的时候,更加正确.
- 思考哪些变量需要声明使得你需要在写代码之前做一些计划.
- 声明变量帮助防止一种编程最难找的bug–变量名的错误拼写.
- 你的程序不会编译如果你没有声明你的变量.
Assignment
num = 1;
程序的下一行是一个赋值语句,C中最基本的操作之一. 声明语句给num在计算机内存的地址中开辟了一片空间,而赋值语句则是在那个地址上存储了一个值.
赋值语句,是将等号右边的值赋值给左边. 并且这个语句也是以分号结尾的.
The printf() Function
printf("I am a simple");
printf("computer.\n");
printf("My favorite number is %d because it is first.\n", num);
这些行都使用了名为printf()的标准C函数.
C使用形参和实参来区分传输给函数的实际值和函数中用来获取值的变量.
\n 是换行符. 换行符是转义序列(escape sequence)的一个例子.
%d
是一个占位符,用来展示num的值应该在哪里被打印出来. % 告诉程序有个变量在那个位置要被打印,而d则是告诉它这个将这个变量打印成十进制整数.
printf函数允许为打印出来的变量选择多种形式, 包括十六进制和浮点数.
Return Statement
return 0;
这个return语句是程序最后的语句. int main(void)
函数要求返回一个整数. C函数通过return关键字来返回值.如果你忽略了main函数的return语句,程序会在你遇到}
的时候返回0.所以你可以省略main函数的return语句. 但是你不能省略其他函数的return语句.
The Structure of a Simple Program
一个程序由一个或者多个函数组成,其中一个函数,必须是main函数. 头部和主体构成了函数的介绍. 函数的头部包括函数名,传给函数的类型以及返回值的类型. 函数主体由大括号包围,并且由一系列语句构成. 每个语句都以分号结尾. 这章的例程中有一个声明语句,声明了变量使用的类型和变量名. 然后它用一个赋值语句给变量一个值. 然后有三个print语句,每个语句都调用了printf函数. 这个print语句是函数调用的例子. 最终,main函数以return语句结尾.
Tips on Making Your Programs Readable
让你的程序具有可读性是一个好的编程练习. 一个可读性的程序更容易被理解,也更容易被纠正和修改.
使程序可读的行为,也帮助你清楚程序功能的概念.
你已经了解两个提高可读性的方法:选择有意义的变量名和使用注释. 需要注意的是,这两个技巧是互补的.
另一个技术是用空行将一个函数的一个概念部分与另一个分开. C不要求空行,但是这可以提高程序可读性.
第四个技术是一个语句一行. 这个是提高可读性的方法,并不是C要求的. C具有自由形式的特点.
Taking Another Step in Using C
// fathm_ft.c -- converts 2 fathoms to feet
#include <stdio.h>
int main(void)
{
int feet, fathoms;
fathoms = 2;
feet = 6 * fathoms;
printf("There are %d feet in %d fathoms!\n", feet, fathoms);
printf("Yes, I said %d feet!\n", 6 * fathoms);
return 0;
}
Documentation
程序以注释开头,定义了文件名和程序的目的. 这种程序记录只耗费一点时间,但是对你后续浏览许多文件或者打印他们都十分有帮助
Multiple Declarations
程序声明两个变量在一个声明语句中. 为了这么做,用逗号在声明语句中将两个变量分开.
int feet, fathoms;
和
int feet;
int fathoms;
是等效的.
Multiplication
程序做出了一个乘法. 在C中,和许多语言一样,*是代表着乘号. 因此,语句
feet = 6 * fathoms
意味着,将变量fathoms的值,乘以6,然后将这个结果赋值给变量feet.
Printing Multiple Values
最后,程序更好的使用了printf函数. 如果你编译然后运行例程,你会看到这样的输出:
There are 12 feet in 2 fathoms!
Yes, I said 12 feet!
这次,在第一个printf中,做了两个替换. 双引号中的第一个%d被第一个变量(feet)所替换,第二个%d被第二个变量(fathoms)替换. 注意,这一系列变量都在双引号语句后面被列出,并且用逗号分隔.
第二个printf展示了被打印的值不一定是变量,它只需要是某个值就行,就像``6 *fathoms`
While You’re at It — Multiple Functions
到目前为止,这些程序都是使用了标准printf函数,下面这个例子要展示如何在程序中,使用除了main函数之外的,你自己的函数.
//* two_func.c -- a program using two functions in one file */
#include <stdio.h>
void butler(void); /* ANSI/ISO C function prototyping */
int main(void)
{
printf("I will summon the butler function.\n");
butler();
printf("Yes. Bring me some tea and writeable DVDs.\n");
return 0;
}
void butler(void) /* start of function definition */
{
printf("You rang, sir?\n");
}
程序的输出如下:
I will summon the butler function.
You rang, sir?
Yes. Bring me some tea and writeable DVDs.
butler( ) 函数在这个程序中出现了三次. 第一次出现是prototype, 用来告诉编译器这个程序是怎么用的. 第二次是以函数调用的方式在main函数中出现. 最后,程序展示了函数定义,也就是函数本身的源码. 让我们轮流看这三次出现.
C90标准增加了prototype. 一个prototype向编译器声明了你正在使用一个特定的函数,所以也叫函数声明. 他也指定了函数的性质. 比方说,第一个void就说明这个函数不返回任何值. 第二个void则意味着这个函数不需要参数. 因此,当编译器在mian函数中到达butler被使用的地方,就可以检查butler函数是否被正确使用. 注意, void被用来表示空的,并不是无效的.
然后,你在main函数中调用butler函数,只需要使用函数的名字,并加上括号. 当butler函数完成他的任务后,程序会进入main函数中的下一个语句.
最后,butler函数用和main函数一样的方式定义: 函数头和用大括号包围的函数主体. 函数头重复着函数声明中的信息: 没有参数也没有返回值…
需要注意的一点是,决定butler函数什么时候运行的,是butler函数在main函数中的位置,而不是butler函数定义的位置. 不管你是将函数定义放在main函数前面还是后面,函数运行都是一样的. 但是通常情况下,都是先列出main函数,因为这通常会提供程序的基本框架.
C标准建议提供所有你使用的函数的prototype. 标准的include文件帮忙实现标准库函数部分的任务.
Introducing Debugging
下面展示了一些bugs.
/* nogood.c -- a program with errors */
#include <stdio.h>
int main(void)
(
int n, int n2, int n3;
/* this program has several errors
n = 5;
n2 = n * n;
n3 = n2 * n2;
printf("n = %d, n squared = %d, n cubed = %d\n", n, n2, n3)
return 0;
)
Syntax Errors
例程中含有很多语法错误. 当你不遵守C的规则的时候,你就会造成一个语法错误. C的语法错误就是用有效的C标识在错误的地方.
所以例程中犯了哪些语法错误呢?
-
使用小括号而非大括号来标示函数主体.
-
声明应该是这样:
int n,n2,n3;
或者是: -
int n; int n2; int n3;
-
例程忽略了
*/
-
printf结尾忽略了分号
Semantic Errors
Semantic Errors是语义错误. 在C中,当你遵守C的规则但是造成了错误的结果,就会造成语义错误.
Program State
program state是在程序运行过程中某一时刻所有变量的值的集合.
我们只讨论了一种追踪程序状态的方法: 自己一步一步地运行程序. 在那种有上万次循环的程序上,你可能没有办法做到.但是你也可以运行几次循环来看看你的程序是否符合你想要的. 但是,总是有可能你运行的步骤是你希望这些步骤运行的,而不是你实际上写的代码. 因此要充分尊重实际代码.
另一种查找语义错误的方法是增加额外的printf语句在程序关键节点来获取选择的变量的值. 看到变量值的变化,可以推测发生了什么. 当程序运行的符合你的期望的时候,你就可以把这些额外的语句去掉然后再编译一次.
第三个检查程序状态的方式就是使用debugger. debugger是一个能够让你一步一步运行程序并且检查程序变量值的另一个程序. 高级一些的debugger能够显示哪一行的源码正在被执行. 这对有很多执行路径的程序十分友好,因为你能够很轻松的知道程序在执行哪条路径.
Keywords and Reserved Identifiers
关键字是C的词汇. 因为他们对于C很特殊,因此你不能将他们作为变量名. 许多这些关键字指定了许多像int这样的类型. 而另一些则像if这样被用来控制程序语句是怎样被运行的.
auto | extern | short | while | break | float | signed | _Alignas | case | for | sizeof |
---|---|---|---|---|---|---|---|---|---|---|
_Alignof | char | goto | static | _Bool | const | if | struct | _Complex | continue | inline |
switch | __Generic | default | int | typedef | _Imaginary | do | long | union | _Noreturn | double |
register | unsigned | _Static_assert | else | restrict | void | #_Thread_local | enum | return | volatile |
如果你尝试将关键字作为变量名,编译器会将这个作为语法错误. 还有一些你不能使用的称为保留标识符的标识符. 他们不会造成语法错误,因为他们是有效的名字. 但是,语言以及使用他们或者保留了使用它们的权利, 所以如果你用这些标识符做其他事情,就会造成问题. 保留标识符包括那些以下划线开头的字符和标准库函数的名字.
Key Concepts
计算机编程是一个充满挑战的活动. 他需要抽象,有概念的思考结合上对细节的关注. 你会发现编译器强制你关注细节. 对于编译器来说,基本正确仍然是错误.
编译器不会帮助你像这些概念上的事情,因此本书会通过列举每章节的关键信息来弥补这些差距.
对于这章节来说,你的目标就是理解什么是C程序. 你可以将程序认为是一个你希望计算机如何表现得介绍. 编译器处理着贴别细节得事情来将你的描述转换为机器语言. 由于编译器没有真正的智力,你必须表达你的描述通过编译器得语句. 并且这些语句必须是用C语言标准规定得正确方式.
标签:语句,main,函数,int,程序,Chapter2,编译器,plus,primer From: https://blog.csdn.net/shaun2001/article/details/140268940