C语言入门小册子
一个简单的C程序:计算球体的体积
以下是一个简单的C语言程序,用于计算球体的体积:
/* sphere.c */
#include <stdio.h> /* 包含用于printf的头文件。 */
#define PI 3.141592654f /* 定义一个常量。 */
static float sphere(float); /* 函数原型。sphere函数被定义为内部链接的(static),
因为它仅在本翻译单元中使用。 */
int main(void) /* 主函数 */
{
float volume; /* 定义一个浮点变量。 */
float radius = 3; /* 定义并初始化变量。 */
volume = sphere(radius); /* 调用sphere函数,并将返回值赋值给volume。 */
printf( "Volume: %f\n", volume ); /* 打印结果。 */
return 0;
}
static float sphere(float rad) /* 定义用于计算体积的函数。 */
{
float result; /* 定义一个自动存储周期和块作用域的变量result。 */
result = rad * rad * rad;
result = 4 * PI * result / 3;
return result; /* 返回result给调用sphere函数的地方。 */
}
程序特性解析
-
注释:
程序中的注释以/*
和*/
括起来。注释几乎可以放在程序中的任何地方,因为编译器会忽略它们。 -
语句结束:
每条语句以分号;
结尾。漏掉分号或多加一个分号是C语言编程中的常见错误。 -
代码块:
代码通过花括号{ }
分组为块。 -
区分大小写:
C语言区分大小写,所有的C语言关键字都以小写书写。变量和函数名称可以使用任意大小写,但按惯例应使用小写。常量通常使用大写。 -
预处理指令:
并非C程序中的所有语句都是可执行的。一些语句是“预处理指令”。
C程序的编译过程
C语言的编译是一个多步过程,包括以下步骤:
-
预处理:
源文件会通过“C预处理器”处理。预处理器的任务是对源文件进行文本操作,如宏展开、常量替换、文件包含和条件编译。预处理的输出是二级源文件,用于后续编译。 -
编译:
编译器将二级源文件转化为二进制等效的目标文件(object file)。 -
链接:
目标文件不能直接运行。如果程序中使用了C库函数(如printf()
),链接器会将库函数的二进制代码与目标文件合并。此外,还需要链接一些地址信息,以便程序能在目标系统上运行。链接器将一个或多个目标文件与库文件链接,生成可执行文件。
预处理指令
预处理指令以#
开头,被称为“预处理指令”或“宏命令”。示例程序中包含两个预处理指令:
#include <stdio.h>
#define PI 3.141592654f
-
#include <stdio.h>
:
将头文件stdio.h
的内容合并到当前程序中。stdio.h
包含了标准输入/输出库的声明,例如printf()
函数的声明。 -
#define PI 3.141592654f
:
定义了一个常量PI
,表示圆周率。
头文件(如stdio.h
)仅包含声明,实际的库代码存放在单独的库文件中,并在链接时添加。自定义头文件可通过如下方式包含:
#include "mydefs.h"
需要注意,标准头文件使用尖括号< >
,而自定义头文件使用双引号" "
。
C程序的结构
C程序由一个或多个函数组成。示例程序中包含两个用户自定义函数main()
和sphere()
,以及一个库函数printf()
。
-
main()
函数:
主函数是编写自包含程序时的必备部分。它定义了程序运行时自动执行的函数,其他函数均由main()
直接或间接调用。 -
函数调用:
调用C函数时,只需指定函数名,并在后续括号中提供参数,用逗号分隔参数。例如:printf( "Volume: %f\n", volume );
这段代码传递了两个参数:
"Volume: %f\n"
:提供文本和格式信息。volume
:提供一个数值。
-
函数返回值:
函数可以返回值,也可以不返回值。例如,sphere()
函数返回一个值,因此调用方式如下:volume = sphere(radius);
sphere()
函数使用return
关键字返回计算结果,具体语句为:return result;
C语言中的变量和函数声明
变量声明
在C程序中,所有变量都必须通过指定名称和类型进行“声明”。在示例程序中,main
函数声明了两个变量:
float volume;
float radius = 3;
sphere
函数中也声明了一个变量:
float result;
以上声明的变量类型为浮点型(float
)。在声明变量时,可以同时为其赋值,例如在声明radius
时直接赋值为3
。
这三个变量的声明都定义了块作用域(block scope)。具有块作用域的变量仅在声明它们的代码块中有效。同名变量可以在不同的代码块中声明,而不会互相干扰。此外,可以声明文件作用域(file scope)的变量,使其在整个翻译单元内的所有函数中共享。这类变量在所有块外部声明,例如:
extern int a;
此声明标识a
为整型变量,其存储在其他地方定义,通常在另一个翻译单元中。例如:
/* global.c */
#include <stdio.h>
void somefunc( void );
int globalvar;
void main()
{
extern int globalvar;
globalvar = 42;
somefunc();
printf( "%d\n", globalvar );
}
void somefunc( void )
{
extern int globalvar;
printf( "%d\n", globalvar );
globalvar = 13;
}
在这段代码中,globalvar
是一个全局变量,在main
和somefunc
函数中都可以访问和修改。
函数声明(函数原型)
除了变量声明,示例程序还展示了函数声明(或称函数原型)的用法。例如:
float sphere(float);
函数原型声明了函数的返回值类型(若函数无返回值,则类型为void
)以及调用该函数时需要提供的参数类型。这使得C编译器能够检查程序中对函数的调用是否正确,若调用不符合声明,将抛出错误。
输出文本的功能
printf()
函数
C语言的printf()
函数提供了强大的文本输出功能。例如,打印一条简单消息:
printf("Hello, world!");
输出结果为:
Hello, world!
需要注意的是,printf()
不会自动在输出末尾添加换行符(\n
)。例如:
printf("Twas bryllig ");
printf("and the slithy toves");
输出为:
Twas bryllig and the slithy toves
如果需要换行,可以显式添加换行符:
printf("Hello,\nworld!");
输出结果为:
Hello,
world!
格式化输出
printf()
支持使用格式化代码打印变量的值。例如:
printf("Result = %f\n", result);
如果result
的值为0.5
,输出为:
Result = 0.5
这里%f
是格式化代码,用于打印浮点数。再看一个例子:
printf("%d times %d = %d\n", a, b, a * b);
若a = 4
,b = 10
,输出为:
4 times 10 = 40
%d
用于打印整数。参数列表中还可以包括数学表达式或字符串。
puts()
函数
对于简单的字符串输出,可以使用puts()
函数。它会自动在输出文本后添加换行符。例如:
puts("Hello, world!");
输出为:
Hello, world!
ANSI前的C语言示例
在ANSI标准出现之前,C语言中的函数声明和参数定义有所不同。例如:
/* oldspher.c */
#include <stdio.h>
#define PI 3.141592654
float sphere(); /* 函数原型中未定义参数类型 */
main()
{
float volume;
int radius = 3;
volume = sphere( radius );
printf( "Volume: %f\n", volume );
}
float sphere( rad )
int rad; /* 参数类型在函数头部未指定 */
{
float result;
result = rad * rad * rad;
result = 4 * PI * result / 3;
return result;
}
可以看到,早期的函数声明中,参数类型不需要在函数原型中指定,参数类型在函数定义的后续部分才被描述。ANSI C标准统一了这些规范,使C语言的代码更易读、易维护。
后续章节将详细阐述本节中提到的原理和概念。