一个简单的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函数的地方。 */
}

程序特性解析

  1. 注释:
    程序中的注释以/**/括起来。注释几乎可以放在程序中的任何地方,因为编译器会忽略它们。

  2. 语句结束:
    每条语句以分号;结尾。漏掉分号或多加一个分号是C语言编程中的常见错误。

  3. 代码块:
    代码通过花括号{ }分组为块。

  4. 区分大小写:
    C语言区分大小写,所有的C语言关键字都以小写书写。变量和函数名称可以使用任意大小写,但按惯例应使用小写。常量通常使用大写。

  5. 预处理指令:
    并非C程序中的所有语句都是可执行的。一些语句是“预处理指令”。


C程序的编译过程

C语言的编译是一个多步过程,包括以下步骤:

  1. 预处理:
    源文件会通过“C预处理器”处理。预处理器的任务是对源文件进行文本操作,如宏展开、常量替换、文件包含和条件编译。预处理的输出是二级源文件,用于后续编译。

  2. 编译:
    编译器将二级源文件转化为二进制等效的目标文件(object file)。

  3. 链接:
    目标文件不能直接运行。如果程序中使用了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()

  1. main()函数:
    主函数是编写自包含程序时的必备部分。它定义了程序运行时自动执行的函数,其他函数均由main()直接或间接调用。

  2. 函数调用:
    调用C函数时,只需指定函数名,并在后续括号中提供参数,用逗号分隔参数。例如:

    printf( "Volume: %f\n", volume );
    

    这段代码传递了两个参数:

    • "Volume: %f\n":提供文本和格式信息。
    • volume:提供一个数值。
  3. 函数返回值:
    函数可以返回值,也可以不返回值。例如,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是一个全局变量,在mainsomefunc函数中都可以访问和修改。

函数声明(函数原型)

除了变量声明,示例程序还展示了函数声明(或称函数原型)的用法。例如:

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 = 4b = 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语言的代码更易读、易维护。


后续章节将详细阐述本节中提到的原理和概念。

最后修改: 2025年01月27日 星期一 22:48