
for(int i = n; i> 1; i-)/ *循环体* /
res * = i;
返回资源; / *返回* /
}
使用递归过程求解阶乘,如代码18.14所示.
代码18.14使用递归编程来解决阶乘迭代
<----------------------------文件名: Iteration.c ------------ ----------------->
01#include <stdio.h> / *使用头文件包含在printf中* /
02#include <conio.h>
03 int f(int n)/ *用于计算阶乘的函数* /
04 {
05 printf(“解决%d \ n的阶乘”,n);
06 if(n == 1)
07返回1;
08
其他
09 return f(n-1)* n;
10}
11 void main(void)/ * main function * /
12 {
13 int n = 1,res = 1; / * n用于接收用户输入,res用于存储结果* /
14 printf(“请输入整数\ n”);
15 scanf(“%d”,&n); / *读取输入* /
16 res = f(n);
17 printf(“%d的阶乘是%d”,n,res);
18 getch(); / *等待,按任意键结束* /
19}
输出为:
请输入整数5(键盘输入)
解决5的阶乘解4的阶乘解3的阶乘解2的阶乘解1的阶乘5的阶乘为120
[代码分析]在代码18.14的函数f中,当N> 1时,它将再次调用自身以将参数修改为N-1,依此类推,直到参数变为1,返回1,然后返回layer逐层获取正确的结果. 以输入数字5为例,f(5)的值为5×f(4),找到f(4)的值为4×f(3),...,当N = 1时,递归停止并逐渐返回第一次调用时,返回结果为5×4×3×2×1 = 120.
递归函数的执行始终是尚未执行的函数主体,因此它获取该执行的结果并进入函数主体的另一轮执行. 重复执行并加深该操作,直到某个执行达到递归终止条件为止. 不再深入,执行函数主体的其余部分,返回上一个调用的函数主体,执行剩余部分,然后逐层返回,最后返回到起始位置,结束整个递归过程,得到相应的结果.
递归的核心是“逐渐加深,然后逐渐返回”.
18.5.2递归的两个要素
递归离不开两点,一是递归递进机制,二是递归终止条件. 渐进机制应确保每个呼叫距离呼叫终止都近一步. 在代码18.14中,f(5)调用f(4),f(4)调用f(3),...,逐步接近f(1)的终止条件. 这样可以确保递归调用正常结束,而不会引起无限循环,从而导致系统内存不足并崩溃. 通常,这些参数用于判断调用过程,以便在适当时切断调用链,例如代码18.14中的“ if(n == 1){...}”结构.
18.5.3递归调用的效率和可读性,每个函数调用都有一定的堆栈操作,相比于循环模式,时空开销更大,因此递归函数的效率始终比同一个函数小循环结构低,任何递归编写的函数都可以用循环代替,以提高效率.
但是,递归的好处也很明显: 一种是程序更易读,更易于修改和维护,另一种是在连续的函数调用中,函数的大小在减小. 在解决阶乘问题的示例中,继续描述复杂度为n-1的问题和复杂度为n-1的问题,直到可以直接解决该问题为止(参数1).
如果loop语句实现的算法比使用递归复杂得多,则有必要考虑复杂性和效率之间的折衷. 在这种情况下,建议先使用递归方法解决该问题.
18.6具有参数的主要功能上述主要功能没有参数. 实际上,main函数也可以接受参数. 谁传递了main函数的参数?答案是操作系统. C语言规定只能有两个主要的函数参数: argc和argv. 具有参数的主要函数的形式为:
int main(int argc,char * argv [])
{
......
}

第一个参数argc必须是一个整数变量,称为参数计数器,其值是包括命令名称在内的参数数量;第二个参数argv必须是一个指向字符的指针数组,用于存储命令名称和参数字符串的地址.
要使用参数调用主函数必须在操作系统环境中执行,让我们看一下示例代码18.15.
代码18.15通过参数MainWithAugment使用主函数
<-------------------------文件名: MainWithAugment.c --------------- ----------->
01#include <stdio.h> / *使用头文件包含在printf中* /
02#include <conio.h>
03 void main(int argc,char * argv [])/ *两个参数* / 04 {
05 for(int i = argc-1; i> = 0; i-)/ *向后输出,0到argc-1,以防止越界* /
06
{
07 printf(“%s \ n”,argv [i]);
08}
09 getch();
10}
编译并连接上面的代码以生成一个可执行文件(假设其名称为example.exe),将其复制到C驱动器的根目录,然后在DOS环境中输入以下命令行:
C: \>世界打招呼的例子
输出为:
你好
世界
示例
[代码分析]文件名示例本身也是一个参数. 因此,以上命令行调用example.exe共享3个参数. DOS系统传递给主函数的参数argc为3,字符指针数组argv元素argv [0],argv [1]和argv [2]分别存储字符串示例,世界,和你好. 根据主功能的定义,三个字符串以相反的顺序输出. 18.7总结本章讨论有关功能的深入内容. 首先,从函数参数传递和返回值的角度来看,使用了大量示例来帮助读者理解“复制机制”. 使用C语言传递函数参数的方法有两种: 值传递和地址传递. 无论使用哪种方法,该函数都会在堆栈中创建参数的副本. 所有操作都在副本上执行,因此值传输不会修改实际参数,但是在通过地址调用时,由于指针的间接操作,它将影响实际参数所在的存储区. 该函数返回相同的值和地址,但是您应注意不要返回指向局部变量(即堆栈存储器)的指针. 函数是C语言的核心. 只有充分掌握功能机制,才能理解高质量的代码并编写自己的高质量代码.
作为组织数据的两种有效方法,结构和数组广泛用于函数中. 结构变量本质上可以用作普通变量,并且在将数组名称作为参数传递给函数时,它会退化为一个Pointer,需要将数组的大小等显式传递给函数.
最后,解释了一种通用的编程机制-递归. 使用递归时,应注意终止条件的编写,以防止无限调用循环和程序崩溃.
18.8动手练习
1. 以下程序的运行结果是什么?
void main()
{
结构节点
{
int数据;
结构节点*接下来;
} s [10];
int i;
对于(i = 0; i <10; i ++)
s [i] .data = 2 * i + 1;
对于(i = 0; i <10; i ++)
printf(“%d \ n”,s [i] .data);
}
[提示]第一个for循环为结构分配值,第二个循环输出结构的值. 2.编写以下代码的运行结果.
#include <stdio.h>
结构节点
{
int数据;

字符n [10];
};
没有乐趣(结构节点* a)
{
a->数据= 10;
}
void main()
{
结构节点s = {20,“ zhang”};
printf(“%d,%s”,s.data,s.n);
乐趣(&s);
printf(“%d,%s”,s.data,s.n);
}
[提示]在上述程序中,在地址处调用了fun函数,并且s.data的值更改为10. 因此,结构变量s的值将更改.
第19章生存期,范围和可见字段有时,您会非常感兴趣地编写大型页面程序,但是在编译和链接时,会收到许多奇怪的错误消息,例如未定义的变量和函数. 您可能在窃窃私语,此功能显然在这里,这不是某个变量吗?编译器有问题吗?阅读本章后,您会发现编译器没有问题. 它是函数和变量的范围,生存期和可见域.
可能在生存期,范围和可见字段中涉及的程序元素是: 变量(公用类型,结构变量和公用变量的通用名称),常量,函数,结构和公用对象的定义
本章包括的知识点是:
C语言中的内存分配问题
自动,注册,外部,静态变量
功能范围和可见域结构范围和可见域19.1内存分配
变量名称,函数名称等都对应于内存中的一个区域,那么这些实体如何存储在内存中?程序如何使用这些变量?在本节中,我们首先从C程序的内存分配开始,并逐步回答这些问题.
19.1.1内存分区
由C编译的程序所占用的内存大致分为以下几部分.
堆栈区域(stack): 由编译器自动分配和释放,存储函数参数值和局部变量值.
堆: 通常由程序员分配和释放(动态内存应用和释放). 如果程序员不释放它,则操作系统可能会在程序结束时对其进行回收.
全局区域(静态区域)(静态): 全局变量和静态变量的存储放在一起,已初始化的全局变量和静态变量在同一区域中,未初始化的全局变量和未初始化的静态变量在另一个相邻的区域中程序结束后,操作系统将释放该区域.
常量区: 字符串常量和其他常量的存储位置,在程序结束后由操作系统释放.
程序代码区: 存储函数体的二进制代码.
看下面的程序: int a = 0; / *全局初始化区* /
char * p1; / *全局未初始化区域* /
主要()
{
int b; / *堆栈* /
char s [] =“ abc”; / *堆栈* /
char * p2; / *堆栈* /
char * p3 =“ 123456”; / * 123456 \ 0在常量区域中,p3在堆栈上,与char s [] =“ abc”;
static int c = 0; / *全局(静态)初始化区域* /
p1 =(char *)malloc(10); / *堆区域* / p2 =(char *)malloc(20); / *堆区域* // * 123456 \ 0放置在常量区域中,但是编译器可能会将其优化为p3指向带有“ 123456”的位置* /
strcpy(p1,“ 123456”);
}
19.1.2变量的存储类别在C语言中,变量的存储类别大致分为4种类型: 自动(自动),寄存器(寄存器),静态(静态)和和可见域是不同的.
根据作用域,变量可以分为局部变量和全局变量.
所谓的局部变量是指在函数内部定义的变量. 局部变量只能在定义它的函数中有效使用. 它的范围仅限于函数,即从定义变量的位置到函数主体的末尾. 通常,系统不会为局部变量分配存储单元,但是在程序运行期间,当调用局部变量所在的函数时,系统会根据需要临时为其分配内存. 函数执行结束后,将撤消局部变量并恢复占用的内存.
在函数更广. 全局变量不属于任何函数,并且理论上可以被其范围内的所有函数访问. 因此,全局变量提供了一种在不同函数之间进行通信的方式,从而使函数之间的数据通信不仅限于参数传递和返回语句. 一旦定义了全局变量,编译器将为其分配一个固定的存储单元. 该存储单元在程序执行期间始终有效,并且在程序执行完成之前,操作系统将无法恢复该内存.
19.1.3生存时间
用外行人的术语来说,生存期是指程序运行时从创建到撤销变量的时间段. 寿命的长短取决于上述存储方法. 对于自动分配(堆栈分配),该变量与它所在的代码块共存. 对于静态分配(由编译器进行预分配),该变量在程序开始执行时与程序共存. 也就是说,直到程序完成运行并退出后,该变量才存在. 对于动态存储的内存块(请注意: 不是指向内存块的指针),程序员可以确定其寿命.
对于程序代码区域中的函数定义,常量区域中的字符串常量和其他常量,结构和联合,讨论寿命是没有意义的,因为它们都与程序共存并消亡.
19.1.4范围和可见字段在程序代码中,变量的有效范围(源程序区域)称为范围,而可以合法访问变量和标识符的范围(源程序区域)称为变量. 可见场. . 可以说,范围是变量在理论上有效的区域c 文件 读取0,可见区域是变量实际有效的区域,可见域是范围的子集.
C语言的范围可以分为以下几类.
1. 封锁范围
自动变量(自动,寄存器)和内部静态变量(静态)具有块范围. 对于在块内声明的变量,作用域始于声明点,并在块末尾结束. 在函数定义中声明的形式参数的范围仅限于函数的主体. 它与其他函数中声明的同名变量不同. 允许在不同的函数中使用相同的变量名. 编译器将为这些变量分配不同的变量. 存储单元,不要混淆.
请注意,函数中给定的标签(无论位于何处)都可以使用goto语句将程序流转移到标签,但是C语言不允许使用goto语句将流转移到另一个功能.
2. 文件范围
,从声明点到文件末尾. 这里所指的文件是编译的基本单元-.c文件.
3. 全局(程序)范围全局变量(. 只要在使用前声明它们,全局变量就可以在程序中的任何位置(由多个.c文件组成)使用.
19.2自动变量
函数的形式参数和代码块中定义的变量均为自动变量. 这是C语言中使用最广泛的变量. 这种类型的变量由堆栈分配,并动态分配存储空间. 以函数参数为例,当调用函数时,为该参数分配存储空间,当函数调用结束时,存储空间自动释放. 对于代码块中定义的变量(包括函数中定义的变量),在执行变量声明语句时,系统会为这些自动变量分配空间. 当程序流离开代码块时,这些变量将自动撤销,并释放它们所占用的内存空间.
19.2.1定义格式自动变量的定义格式为
[auto]数据类型变量1 [=初始化表达式],变量2 [=初始化表达式]……;
方括号可以省略. 这里的变量不仅指普通内置类型的变量,还包括诸如数组,结构和指针之类的复合结构.
默认情况下,用C语言定义的变量是auto变量. 在前面的示例中,函数和代码块中的局部变量未使用关键字auto. 实际上,这符合C语言的默认规定. 例如,在一个函数中,其定义如下:
int a;
float b:
C编译器自动将其解释为:
auto int a;
自动浮动b;
请注意,在函数定义和声明中,不能使用auto修改形式参数,并且形式参数只能在堆栈上分配而无需修改.
19.2.2范围和生存时间
自动变量的范围和生存期仅限于定义它的代码块. 所谓的代码块是指用两个大括号括起来的代码行. 该功能只是一种代码块. 通用代码块还有结构,结构等,即使它们只是两对花括号,它们也可以形成一个独立的代码块.
此和生存期与从定义到代码块末尾的时空区域相对应.
查看以下功能:
1个int函数(int m,int n)
2 {
3 int x; / *#1,等效于auto int x; * /
4 {/ *#4 * /
5个整数a,b,c; / *#2,等效于auto int a,b,c; * /
6 ...
7} / *#5 * /
8 int y,z; / *#3,相当于auto int y,z; * /
9 ...
10返回0;
11}
[代码分析]形式参数m和n是默认的自动变量. 生存期(时间)从func的开始到执行的结束返回. 范围(空格)覆盖了整个功能代码.
对于自动变量x,y,z和a,b和c,其寿命和范围由定义它们的代码块确定. x,y和z在函数中定义. 因此,x的范围和生存期从#1返回到函数执行的结尾; y和z的范围和生存期从#3返回到函数执行结束. a,b和c是在由#4和#5组成的代码块中定义的,a,b和c的范围和生存期是从#2到#5(代码块的末尾). 因此,如果在#3及更高版本的代码中使用a,b和c,编译器将给出错误消息-使用的变量未定义.
19.2.3可以将受屏蔽的代码块嵌套并应用于形成一定的层次结构,那么可以在内部和外部代码块中定义相同名称的变量吗?如果是这样,这些同名变量之间的关系是什么?首先看一下示例代码19.1.
代码19.1内部自动变量屏蔽外部自动变量AutoVariable
<------------------------------文件名: AutoVariable.c ------------- -------------->
01#include <stdio.h> / *使用头文件包含在printf中* /
02#include <conio.h>
03 void main(void)/ * main function * /
04 {
05 / *这是A行,声明3个自动变量,作用域和生存期从A行到主函数执行结束* /
06 int x = 1,y = 2,z = 3;
07 printf(“ x是%d,y是%d,z是%d \ n”,x,y,z);
08 {/ *#1 * /
09 / *这里标记为B行,声明的y,范围和生存期从B行到#4,屏蔽了A行中定义的y * /
10 int y = 6;
11 printf(“ x是%d,y是%d,z是%d \ n”,x,y,z);
12 {/ *#2 * /
13 / *这是C行,声明的y和z,范围和生存期从C行到#3 * /
14 / *在行B中定义y,在行A中定义z,* /
15 int y = 8,z = 9;
16 printf(“ x是%d,y是%d,z是%d \ n”,x,y,z);
17} / *#3 * /
18 printf(“ x是%d,y是%d,z是%d \ n”,x,y,z);
19} / *#4 * /
20 printf(“ x是%d,y是%d,z是%d \ n”c 文件 读取0,x,y,z);
21 getch(); / *等待,按任意键结束* /
22}
输出为:
x为1,y为2,z为3
x为1,y为6,z为3
x为1,y为8,z为9
x为1,y为6,z为3
x为1,y为2,z为3
[代码分析]代码19.1中有三级代码块.
第一层: 主功能块.
第二层: 由#1和#4组成的代码块.
第三层: 由#2和#3组成的代码块.
在内层中定义的自动变量在其范围内屏蔽了外部自动变量. 在代码19.1中,第二层中定义的y屏蔽了第一层中定义的y,第三层中定义的y屏蔽了第二层中定义的y,而第三层中定义的z则屏蔽了定义的z在第一层.
x在第二层和第三层中未定义,因此,第一层中的x不被屏蔽,即x的范围(理论有效面积)和可见区(实际有效面积)相等,两者它是从x的定义到主要函数执行的结尾,但是对于在第一层中定义的y和z,由于存在屏蔽现象,因此其范围和可见域不相同. 对于在第一层中定义的y,其范围是从定义到主函数执行的结尾,但是其可见域是第一层减去第二层,从y到#4;对于在第一层中定义的z,其范围是从定义到主函数执行的结尾,但其可见字段是第一层减去从z到#3的第三层.
类似地,对于第二层中定义的y,范围是从B线到#4,但是可见场不是那么大,因此应扣除从y定义到#3的第三层.
19.2.4重复定义
不能重复定义自动变量. 所谓重复是指两个具有相同名称的变量出现在同一代码块中. 这里所指的相同代码块不包括屏蔽.
以下代码重复定义错误:
如果(……)
{
int x,y;
双倍x,
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-207413-1.html
为了一己之利而冒天下之大不韪
表示农民老了只能喝水了