第4章 函数与程序结构

4.1 函数的基本知识
程序可以看成是变量定义和函数定义的集合。函数之间的通信可以通过参数、函数返回值以及外部变量进行。函数在源文件中出现的次序可以是任意的,只要保证每一个函数不被分离到多个文件中,源程序就可以分成多个文件。
4.2 返回非整型值的函数
4.3 外部变量
internal用于描述定义在函数内部的函数参数及变量。
4.4 作用域规则
名字的作用域指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数参数也是这样的,实际上可以将它看作是局部变量。
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。
另一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。
外部变量的初始化只能出现在其定义中。
4.5 头文件
对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。较大的程序需要使用更多的头文件,我们需要精心地组织它们。
4.6 静态变量
用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。通过static限定外部对象,可以达到隐藏外部对象的目的。
通常情况下,函数名字是全局可访问的,对整个程序的各个部分而言都可见。但是,如果把函数声明为static类型,则该函数名除了对该函数声明所在的文件可见外,其它文件都无法访问。
static也可以用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定函数的局部变量,只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存在空间的变量。
4.7 寄存器变量
register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以是程序更小、执行速度更快,但编译器可以忽略此选项。
register声明只适用于自动变量以及函数的形式参数。
无论寄存器变量实际上是不是房子寄存器中,它的地址都是不能访问的。在不同的机器中,对寄存器变量的数目和类型的具体限制也是不同的。
4.8 程序块结构
C语言不允许在函数中定义函数,但是,在函数中可以以程序块的形式定义变量。变量的声明(包括初始化)除了可以紧跟在函数开始的花括号之后,还可以紧跟在任何其他标识复合语句开始的左花括号之后,以这种方式声明的变量可以隐藏程序块外与之同名的变量,它们之间没有任何关系,并在与左花括号匹配的右花括号出现之前一直存在。
4.9 初始化
在不进行显示初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)。
对于外部变量和静态变量来说,初始化表达式必须是常量表达式,且只初始化一次。对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。
如果初始化表达式的个数比数组元素少,则对外部变量、静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0,如果初始化表达式的个数比数组元素多,则是错误的。不能一次将一个初始化表达式指定给多个数组元素,也不能跳过前面的数组元素而直接初始化后面的数组元素。
字符数组的初始化比较特殊:可以用一个字符串来代替用花括号括起来并用逗号分隔的初始化表达式序列。例如:
char pattern[]="ould";
它同下面的声明是等价的:
char pattern[]={'o','u','l','d'};
这种情况下,数组的长度是5(4个字符加上一个字符串结束符'\0');
4.10 递归
4.11 C预处理器
C语言通过预处理器提供了一些语言功能。从概念上讲,预处理器是编译过程中单独执行的第一个步骤。最常用的预处理指令时:#include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)。
4.11.1 文件包含
文件包含指令(即#include指令)使得处理大量的#define指令以及声明更加方便。在源文件中,任何形如:
#include "文件名"

#include<文件名>
的行都将被替换为由文件名指定的文件的文件的内容,如果文件名用引号引起来,则在源文件所在的位置查找该文件;如果在该位置没有找到文件,或者如果文件名使用尖括号<与>括起来的,则将根据相应的规则查找该文件,这个规则与具体的实现有关。被包含的文件本身也可以包含#include指令。
4.11.2 宏替换
宏的定义形式如下:
#define 名字 替换文本
这是一种最简单的宏替换——后续所有出现名字记号的地方都将被替换为替换文本。#define指令中的名字与变量名的命名方式相同,替换文本可以是任意字符串,通常情况下,#definae指令占一行,替换文本时#define指令行尾部的所有剩余部分内容,但也可以把一个较长的宏定义分成若干行,这时需要在待续行的末尾加上一个反斜杠符\。#define指令定义的名字作用域从其定义点开始,到被编译源文件的末尾处结束。
可以通过#undet指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不是宏调用:
#undef getChar
int getchar(void){...}
形式参数不能用带引号的字符串替换。但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。
预处理运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被市级参数替换,##与见后的空白符将被删除,并对替换后的结果重新扫描。
4.11.3 条件包含
还可以使用条件语句对预处理本身进行控制,这种条件语句的值实在预处理执行的过程中进行计算。这种方式为在编译过程中根据计算所得的条件值选择性地包含不同代码提供了一种手段。
#if语句对其中的常量整型表达式(其中不能包含sizeof、类型转换运算符或enum常量)进行求值,若该表达式的值不等于0,则包含其后的各行,直到遇到#endif、#elif或#else语句位置(预处理语句#elif类似于else if)。在#if语句中可以使用表达式defined(名字),该表达式的值遵循下列规则:当名字已经定义时,其值为1;否则其值为0。
C语言专门定义了两个预处理语句#ifdef与#ifndef,它们用来测试某个名字是否已经定义。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值