函数与程序结构

函数与程序结构


1 基础知识

函数定义形式如下:

//函数定义
返回值类型 函数名(参数声明表)
{
    声明和语句
}
//return语句
return exp;

程序:可以看作变量定义和函数定义的集合。函数之间的通信可以通过参数、函数返回值以及外部变量进行。

被调用函数通过return语句向调用者返回值。return语句后的表达式也可以省略,此时函数不向调用者返回值。调用函数也可以忽略返回值。

2 外部变量

外部变量定义在函数之外,可以在许多函数中使用。C语言不允许在一个函数中定义其他函数,因此函数本身是外部的。

外部变量可以在全局范围内访问,这为函数之间的数据交换提供了一种可以代替函数参数和返回值的方式。

外部变量的用途还表现在它们与内部变量相比具有更大的作用域和更长的生存期。自动变量只能在函数内部使用,从其所在函数被调用是变量开始存在,在函数退出时变量消失。

3 作用域规则

作用域:名字的作用域是指程序中可以使用改名字的部分。

3.1 自动变量

自动变量的作用域从声明该变量的函数中的位置开始到函数结束。不同函数中声明的具有相同名字的各个局部变量间没有任何关系。

3.2 外部变量

外部变量或函数的作用域从声明它的地方开始到其所在文件的末尾结束。

要在外部变量定义前使用该变量或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性使用关键字extern。在一个程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它。

外部变量的初始化只能出现在其定义中。

声明与定义:变量声明用于说明变量的属性。变量定义除此之外还将引起存储器的分配。

3.3 静态变量
  • static声明外部变量:可将变量的作用域限定为变量所在的源文件,达到隐藏外部变量的目的。
  • static声明内部变量:不改变变量的作用域,但static类型内部变量与自动变量不同,不管其所在函数是否被调用,它一直存在,即一种只能在某个函数中使用但一直占据存储空间的变量。
  • static声明函数:通常情况下函数名是全局可访问的,static类型函数只对该函数所在文件可见,其他文件无法访问。
3.4 寄存器变量

register声明的变量在程序中使用频率较高,此声明告诉编译器将变量放在机器的寄存器中,可以使程序更小、执行速度更快。无论寄存器变量实际是否存放在寄存器中,它的地址都是不可访问的。

4 头文件

5 初始化

在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义。

对于外部变量和静态变量来说,初始化表达式必须是常量表达式,且只初始化一次。对于自动变量和寄存器变量,则在每次进入函数或程序块时都将被初始化。自动变量和寄存器变量的初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值。

数组的初始化表达式用花括号括起来,各初始化表达式之间通过逗号隔开。

6 C预处理器

预处理是编译过程中单独执行的第一个步骤。两个最常用的预处理指令是:#include指令(用于在编译期间把指定文件包含进当前文件中)和#define指令(用任意字符序列代替一个标记)。

6.1 文件包含

文件包含指令使得处理大量的#define指令以及声明更加方便。文件包含指令将被替换为文件名指定的文件的内容。形式:

/* 在源文件所在位置查找该文件 */
#include "filename"
/* 根据相应规则查找该文件 */
#include <filename>

在大型程序中,#include指令是将所有声明捆绑在一起的较好的方法。

6.2 宏替换

宏定义的形式为:#define 名字 替换文本

这是最简单的宏替换,后续所有出现名字记号的地方都将被替换为替换文本。可以将一个较长的宏定义分为若干行,此时需要在行末加上一个反斜杠符 \ 。#define指令的作用域从其定义点开始,到被编译的源文件的末尾结束。替换只对记号进行,对括在引号中的字符串不起作用。

//替换文本可以是任意的
#define forever for(;;)
//宏定义也可以带参数
#define max(A, B) ((A) > (B)) ? (A) : (B))
//参数名以#作为前缀则结果中实际参数替换为该参数的带双引号的字符串
#define dprint(expr) printf(#expr " = %g\n", expr)
printf("x/y" " = %g\n", x/y);
/* 连接两个参数 */
#define paste(front, back) front ## back
//调用paste(name, 1)的结果为name1

宏max看起来向函数调用,但宏调用只是将替换文本插入到代码中而已。而且它有一些缺陷,比如作为参数的表达式要重复计算两次,若表达式含有自增(减)运算符,则会出现不正确的结果。

#undef指令:用于取消名字的宏定义。

运算符**##**:提供了一种连接实际参数的手段,##与前后的空白符将被删除。

6.3 条件包含

使用条件语句对预处理本身进行控制,为编译过程中根据计算的条件值选择性地包含不同代码提供了一种手段。

#if语句:对其中的常量整型表达式进行求值,若该表达式的值不等于0,则包含其后的各行。直到遇到#endif、#elif或#else语句为止。在#if语句中可以使用表达式defined(name),其含义为若该name已经定义时,其值为1,否则其值为0。

/* 保证hdr.h文件内容只被包含一次 */
#if !defined(HDR)
#define HDR
#endif
//第一次包含头文件hdr.h时,将定义名字HDR,此后再次包含该文件时,会直接跳转到#endif处
#ifndef HDR
#define HDR
#endif

#ifdef、#ifndef语句:用来测试某个名字是否已经定义。

//第一次包含头文件hdr.h时,将定义名字HDR,此后再次包含该文件时,会直接跳转到#endif处
#ifndef HDR
#define HDR
#endif


#ifdef、#ifndef语句:用来测试某个名字是否已经定义。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值