一个求1到1000数的阶乘和数 c!

tianyuuuu 2012-03-01 12:28:58
阶乘和数就是比如: 145为1!+4!+5!,那么145就是阶乘和数。编程题目就是求1到1000的阶乘和数,我的代码如下:
#include<stdio.h>
long jc(int x)
{
long s=1; int i,p;
for(i=1;i<=x;i++)
p*=i;
return(p);
}
main()
{
int i,j,v[2]={0},s=0;

for(j=1;j<1000;j++)
{
i=0;
while(j!=0)
{
v[i]=j%10;
j/=10;
i++;
}
s=jc(v[0])+jc(v[1])+jc(v[2]);
if(j==s) printf("%d ",j);
for(i=0;i<3;i++)
v[i]=0;
}

}
请指教!
...全文
788 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
tianyuuuu 2012-03-01
  • 打赏
  • 举报
回复
3楼的高手 具体怎么设计呀?
七擒关羽 2012-03-01
  • 打赏
  • 举报
回复

int ten_factorial()//求10以内数的所有的阶乘和
{
int i,j;
int temp_i = 0;
int temp_j = 1;
for(i = 1;i>10;i++)
{
for(j=1;j<i;j++)
temp_j = temp_j*j;
temp_i = temp_i + temp_j;
}
return temp_i;
}

int main()
{
int num = 1000;
int total = (1000/10)*ten_factorial();
return 0;
}
nice_cxf 2012-03-01
  • 打赏
  • 举报
回复
那有那么麻烦,分别算1!,2!直到n!大于1000为止大概也就1-66个数,分别取一个两个三个算一下是否满足就ok了,6因为6!=720也可以直接去掉,实际上就只有5个
tianyuuuu 2012-03-01
  • 打赏
  • 举报
回复
在VC6.0中 没反应
cbzjzsb123 2012-03-01
  • 打赏
  • 举报
回复
不错,上面高手不少的,呵呵
zmkkobe 2012-03-01
  • 打赏
  • 举报
回复
不错,上面高手不少的,呵呵
CatSmoking 2012-03-01
  • 打赏
  • 举报
回复
[Quote=引用 35 楼 tianyuuuu 的回复:]
一天只花2个小时的话?
[/Quote]

楼主出来解释下,是不是百位的阶乘+十位的阶乘+个位的阶乘这个意思?比如

145=1!+4!+5!合格
那么121=1! + 5!不合格?

否则没任何讨论的意义了。
qixing1115 2012-03-01
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 catsmoking 的回复:]
引用 25 楼 qixing1115 的回复:
1:1
2:2
145:145
5760:5760
5761:5761
5762:5762
请按任意键继续. . .
1000以内的没几个,10000以内的还多两个

5!=120
6!=720
7!=5040

5!+6!+7!=5880

5760不管0的阶乘是多少,5760就已经不是了(除非0!=-120).
……
[/Quote]
忘了程序用的是三位数了,多了一位,不过思路是没错的
tianyuuuu 2012-03-01
  • 打赏
  • 举报
回复
一天只花2个小时的话?
tianyuuuu 2012-03-01
  • 打赏
  • 举报
回复
你们一天像这样难度的代码 能打几个呀?
tianyuuuu 2012-03-01
  • 打赏
  • 举报
回复
谢32楼的MM或兄弟!
没有蜡 2012-03-01
  • 打赏
  • 举报
回复
LZ 你的代码存在一下错误:
1.没有考虑到0的阶乘。
2.你在循环中修改了你的j值,并且还用修改了的j与s比较,并且导致死循环。
3.你的Jc函数也有问题
下面是我该写的,通过了编译 希望对你有帮助
#include<stdio.h>
long jc(int x)
{
int s=1; int i;
for(i=1;i<=x;i++)
s*=i;
return(s);
}
void main()
{
int i,j,v[3]={0},s=0;

for(j=1;j<1000;j++)
{
i=0;
int m=j;

while(m!=0)
{
v[i]=m%10;
m/=10;
i++;
}
if(v[2]!=0)
s=jc(v[2])+jc(v[1])+jc(v[0]);
else if(v[1]!=0)
s=jc(v[1])+jc(v[0]);
else
s=jc(v[0]);

if(j==s)
printf("%d\n ",j);

for(i=0;i<3;i++)
v[i]=0;
}
}
funywork 2012-03-01
  • 打赏
  • 举报
回复
求解释下3楼的思路,没看懂额
CatSmoking 2012-03-01
  • 打赏
  • 举报
回复
1,2,145,40585
CatSmoking 2012-03-01
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 qixing1115 的回复:]
1:1
2:2
145:145
5760:5760
5761:5761
5762:5762
请按任意键继续. . .
1000以内的没几个,10000以内的还多两个
[/Quote]
5!=120
6!=720
7!=5040

5!+6!+7!=5880

5760不管0的阶乘是多少,5760就已经不是了(除非0!=-120).
结果就1,2,145。
  • 打赏
  • 举报
回复
继续非主流写法

int a[]={0,1,2,6,24,120,720},i=1,z=0;
for(;z=a[i&1]+a[i&2]+a[((i&4)>>2)*3]+a[((i&8)>>3)*4]+a[((i&16)>>4)*5]+a[((i&32)>>5)*6],printf("%5d\t",z),z=0,i++,!(i-1%8)?printf("\n"):0,i<63;);

CatSmoking 2012-03-01
  • 打赏
  • 举报
回复
没一个好好看题目的,怎么可能溢出呢。
最大的阶乘和数就是999 1088640
qixing1115 2012-03-01
  • 打赏
  • 举报
回复
1:1
2:2
145:145
5760:5760
5761:5761
5762:5762
请按任意键继续. . .
1000以内的没几个,10000以内的还多两个
qixing1115 2012-03-01
  • 打赏
  • 举报
回复
long jc(int x)
{
long s=1; int i,p;
if(x==0)
return 0;
p = 1;
for(i=1;i<=x;i++)
p*=i;
return(p);
}
int main()
{
int i,j,v[3]={0},s=0;

for(j=1;j<20;j++)
{
int k = j;
i=0;
while(k!=0)
{
v[i]=k%10;
k/=10;
i++;
}
s=jc(v[0])+jc(v[1])+jc(v[2]);
printf("%d:%d \n",j,s);
for(i=0;i<3;i++)
v[i]=0;
}

}

你的错误很多,自己对照着改吧,要不是你非要知道你的哪不对我真想给你重新写个
qixing1115 2012-03-01
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 studycbc 的回复:]
C/C++ code

int ten_factorial()//求10以内数的所有的阶乘和
{
int i,j;
int temp_i = 0;
int temp_j = 1;
for(i = 1;i>10;i++)
{
for(j=1;j<i;j++)
temp_j = temp_j*j;
temp……
[/Quote]
三楼的思路就挺不错的
加载更多回复(18)
适于初学者 第五章:函 概述   在第一章中已经介绍过,C源程序是由函组成的。 虽然在前面各章的程序中都只有一个主函main(), 但实用程序往往由多个函组成。函是C源程序的基本模块, 通过对函模块的调用实现特定的功能。C语言中的函相当于其它高级语言的子程序。 C语言不仅提供了极为丰富的库函(如Turbo C,MS C 都提供了三百多个库函),还允许用户建立自己定义的函。用户可把自己的算法编成一个个相对独立的函模块,然后用调用的方法来使用函。   可以说C程序的全部工作都是由各式各样的函完成的, 所以也把C语言称为函式语言。 由于采用了函模块式的结构, C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。   在C语言中可从不同的角度对函分类。 1. 从函定义的角度看,函可分为库函和用户定义函两种。 (1)库函   由C系统提供,用户无须定义, 也不必在程序中作类型说明,只需在程序前包含有该函原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf 、 scanf 、 getchar 、putchar、gets、puts、strcat等函均属此类。 (2)用户定义函   由用户按需要写的函。对于用户自定义函, 不仅要在程序中定义函本身, 而且在主调函模块中还必须对该被调函进行类型说明,然后才能使用。 2. C语言的函兼有其它语言中的函和过程两种功能,从这个角度看,又可把函分为有返回值函和无返回值函两种。 (1)有返回值函   此类函被调用执行完后将向调用者返回一个执行结果, 称为函返回值。如学函即属于此类函。 由用户定义的这种要返回函值的函,必须在函定义和函说明中明确返回值的类型。 (2)无返回值函   此类函用于完成某项特定的处理任务, 执行完成后不向调用者返回函值。这类函类似于其它语言的过程。 由于函无须返回值,用户在定义此类函时可指定它的返回为“空类型”, 空类型的说明符为“void”。 3. 从主调函和被调函之间据传送的角度看又可分为无参函和有参函两种。 (1)无参函   函定义、函说明及函调用中均不带参。 主调函和被调函之间不进行参传送。 此类函通常用来完成一组指定的功能,可以返回或不返回函值。 (2)有参函   也称为带参函。在函定义及函说明时都有参, 称为形式参(简称为形参)。在函调用时也必须给出参, 称为实际参(简称为实参)。 进行函调用时,主调函将把实参的值传送给形参,供被调函使用。 4. C语言提供了极为丰富的库函, 这些库函又可从功能角度作以下分类。 (1)字符类型分类函   用于对字符按ASCII码分类:字母,字,控制字符,分隔符,大小写字母等。 (2)转换函   用于字符或字符串的转换;在字符量和各类字量 (整型, 实型等)之间进行转换;在大、小写之间进行转换。 (3)目录路径函   用于文件目录和路径操作。 (4)诊断函   用于内部错误检测。 (5)图形函   用于屏幕管理和各种图形功能。 (6)输入输出函   用于完成输入输出功能。 (7)接口函   用于与DOS,BIOS和硬件的接口。 (8)字符串函   用于字符串操作和处理。 (9)内存管理函   用于内存管理。 (10)学函   用于学函计算。 (11)日期和时间函   用于日期,时间转换操作。 (12)进程控制函   用于进程管理和控制。 (13)其它函   用于其它各种功能。      以上各类函不仅量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需要一个较长的学习过程。 应首先掌握一些最基本、 最常用的函,再逐步深入。由于篇幅关系,本书只介绍了很少一部分库函, 其余部分读者可根据需要查阅有关手册。   还应该指出的是,在C语言中,所有的函定义,包括主函main在内,都是平行的。也就是说,在一个的函体内, 不能再定义另一个, 即不能嵌套定义。但是函之间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函。 函还可以自己调用自己,称为递归调用。main 函是主函,它可以调用其它函,而不允许被其它函调用。 因此,C程序的执行总是从main函开始, 完成对其它函的调用后再返回到main函,最后由main函结束整个程序。一个C源程序必须有,也只能有一个主函main。    函定义的一般形式 1.无参函的一般形式 类型说明符 函名() { 类型说明 语句 }   其中类型说明符和函名称为函头。 类型说明符指明了本函的类型,函的类型实际上是函返回值的类型。 该类型说明符与第二章介绍的各种说明符相同。 函名是由用户定义的标识符,函名后有一个空括号,其中无参,但括号不可少。{} 中的内容称为函体。在函体中也有类型说明, 这是对函体内部所用到的变量的类型说明。在很多情况下都不要无参函有返回值, 此时函类型符可以写为void。 我们可以改为一个定义: void Hello() { printf ("Hello,world \n"); }  这里,只把main改为Hello作为函名,其余不变。Hello 函一个无参函,当被其它函调用时,输出Hello world字符串。 2.有参函的一般形式 类型说明符 函名(形式参表) 型式参类型说明 { 类型说明 语句 }   有参函比无参函多了两个内容,其一是形式参表, 其二是形式参类型说明。在形参表中给出的参称为形式参, 它们可以是各种类型的变量, 各参之间用逗号间隔。在进行函调用时,主调函将赋予这些形式参实际的值。 形参既然是变量,当然必须给以类型说明。例如,定义一个, 用于两个中的大,可写为: int max(a,b) int a,b; { if (a>b) return a; else return b; }   第一行说明max函一个整型函,其返回的函值是一个。形参为a,b。第二行说明a,b均为整型量。 a,b 的具体值是由主调函在调用时传送过来的。在{}中的函体内, 除形参外没有使用其它变量,因此只有语句而没有变量类型说明。 上边这种定义方法称为“传统格式”。 这种格式不易于编译系统检查,从而会引起一些非常细微而且难于跟踪的错误。ANSI C 的新标准中把对形参的类型说明合并到形参表中,称为“现代格式”。   例如max函用现代格式可定义为: int max(int a,int b) { if(a>b) return a; else return b; }   现代格式在函定义和函说明(后面将要介绍)时, 给出了形式参及其类型,在编译时易于对它们进行查错, 从而保证了函说明和定义的一致性。例1.3即采用了这种现代格式。 在max函体中的return语句是把a(或b)的值作为函的值返回给主调函。有返回值函中至少应有一个return语句。 在C程序中,一个的定义可以放在任意位置, 既可放在主函main之前,也可放在main之后。例如例1.3中定义了一个max 函,其位置在main之后, 也可以把它放在main之前。 修改后的程序如下所示。 int max(int a,int b) { if(a>b)return a; else return b; } void main() { int max(int a,int b); int x,y,z; printf("input two numbers:\n"); scanf("%d%d",&x,&y); z=max(x,y); printf("maxmum=%d",z); }   现在我们可以从函定义、 函说明及函调用的角度来分析整个程序,从中进一步了解函的各种特点。程序的第1行至第5行为max函定义。进入主函后,因为准备调用max函,故先对max函进行说明(程序第8行)。函定义和函说明并不是一回事,在后面还要专门讨论。 可以看出函说明与函定义中的函头部分相同,但是末尾要加分号。程序第12 行为调用max函,并把x,y中的值传送给max的形参a,b。max函执行的 结果 (a或b)将返回给变量z。最后由主函输出z的值。   函调用的一般形式前面已经说过,在程序中是通过对函的调用来执行函体的,其过程与其它语言的子程序调用相似。C语言中, 函调用的一般形式为:   函名(实际参表) 对无参函调用时则无实际参表。 实际参表中的参可以是常,变量或其它构造类型据及表达式。 各实参之间用逗号分隔。'Next of Page在C语言中,可以用以下几种方式调用函: 1.函表达式   函作表达式中的一项出现在表达式中,以函返回值参与表达式的运算。这种方式要是有返回值的。例如: z=max(x,y)是一个赋值表达式,把max的返回值赋予变量z。'Next of Page 2.函语句   函调用的一般形式加上分号即构成函语句。例如: printf ("%D",a);scanf ("%d",&b);都是以函语句的方式调用函。 3.函实参   函作为另一个调用的实际参出现。 这种情况是把该函的返回值作为实参进行传送,因此要该函必须是有返回值的。例如: printf("%d",max(x,y)); 即是把max调用的返回值又作为printf函的实参来使用的。在函调用中还应该注意的一个问题是值顺序的问题。 所谓值顺序是指对实参表中各量是自左至右使用呢,还是自右至左使用。 对此, 各系统的规定不一定相同。在3.1.3节介绍printf 函时已提 到过,这里从函调用的角度再强调一下。 看例5.2程序。 void main() { int i=8; printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--); } 如按照从右至左的顺序值。例5.2的运行结果应为: 8 7 7 8 如对printf语句中的++i,--i,i++,i--从左至右值,结果应为: 9 8 8 9   应特别注意的是,无论是从左至右值, 还是自右至左值,其输出顺序都是不变的, 即输出顺序总是和实参表中实参的顺序相同。由于Turbo C现定是自右至左值,所以结果为8,7,7,8。上述问题如还不理解,上机一试就明白了。函的参和函的值 一、函的参   前面已经介绍过,函的参分为形参和实参两种。 在本小节中,进一步介绍形参、实参的特点和两者的关系。 形参出现在函定义中,在整个函体内都可以使用, 离开该函则不能使用。实参出现在主调函中,进入被调函后,实参变量也不能使用。 形参和实参的功能是作据传送。发生函调用时, 主调函把实参的值传送给被调函的形参从而实现主调函向被调函据传送。   函的形参和实参具有以下特点: 1.形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函内部有效。 函调用结束返回主调函后则不能再使用该形参变量。 2.实参可以是常量、变量、表达式、函等, 无论实参是何种类型的量,在进行函调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。 3.实参和形参在量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。 4.函调用中发生的据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函调用过程中,形参的值发生改变,而实参中的值不会变化。例5.3可以说明这个问题。 void main() { int n; printf("input number\n"); scanf("%d",&n); s(n); printf("n=%d\n",n); } int s(int n) { int i; for(i=n-1;i>=1;i--) n=n+i; printf("n=%d\n",n); } 本程序中定义了一个s,该函的功能是∑ni=1i 的值。在主函中输入n值,并作为实参,在调用时传送给s 函的形参量n( 注意,本例的形参变量和实参变量的标识符都为n, 但这是两个不同的量,各自的作用域不同)。 在主函中用printf 语句输出一次n值,这个n值是实参n的值。在函s中也用printf 语句输出了一次n值,这个n值是形参最后取得的n值0。从运行情况看,输入n值为100。即实参n的值为100。把此值传给函s时,形参 n 的初值也为100,在执行函过程中,形参n的值变为5050。 返回主函之后,输出实参n的值仍为100。可见实参的值不随形参的变化而变化。 二、函的值   函的值是指函被调用之后, 执行函体中的程序段所取得的并返回给主调函的值。如调用正弦函取得正弦值,调用例5.1的max函取得的最大等。对函的值(或称函返回值)有以下一些说明: 1. 函的值只能通过return语句返回主调函。return 语句的一般形式为: return 表达式; 或者为: return (表达式); 该语句的功能是计算表达式的值,并返回给主调函。 在函中允许有多个return语句,但每次调用只能有一个return 语句被执行, 因此只能返回一个值。 2. 函值的类型和函定义中函的类型应保持一致。 如果两者不一致,则以函类型为准,自动进行类型转换。 3. 如函值为整型,在函定义时可以省去类型说明。 4. 不返回函值的函,可以明确定义为“空类型”, 类型说明符为“void”。如例5.3中函s并不向主函返函值,因此可定义为: void s(int n) { …… }   一旦函被定义为空类型后, 就不能在主调函中使用被调函的函值了。例如,在定义s为空类型后,在主函中写下述语句 sum=s(n); 就是错误的。为了使程序有良好的可读性并减少出错, 凡不要返回值的函都应定义为空类型。函说明在主调函中调用某函之前应对该被调函进行说明, 这与使用变量之前要先进行变量说明是一样的。 在主调函中对被调函作说明的目的是使编译系统知道被调函返回值的类型, 以便在主调函中按此种类型对返回值作相应的处理。 对被调函的说明也有两种格式,一种为传统格式,其一般格式为: 类型说明符 被调函名(); 这种格式只给出函返回值的类型,被调函名及一个空括号。   这种格式由于在括号中没有任何参信息, 因此不便于编译系统进行错误检查,易于发生错误。另一种为现代格式,其一般形式为: 类型说明符 被调函名(类型 形参,类型 形参…); 或为: 类型说明符 被调函名(类型,类型…);   现代格式的括号内给出了形参的类型和形参名, 或只给出形参类型。这便于编译系统进行检错,以防止可能出现的错误。例5.1 main函中对max函的说明若 用传统格式可写为: int max(); 用现代格式可写为: int max(int a,int b); 或写为: int max(int,int);   C语言中又规定在以下几种情况时可以省去主调函中对被调函的函说明。 1. 如果被调函的返回值是整型或字符型时, 可以不对被调函作说明,而直接调用。这时系统将自动对被调函返回值按整型处理。例5.3的主函中未对函s作说明而直接调用即属此种情形。 2. 当被调函的函定义出现在主调函之前时, 在主调函中也可以不对被调函再作说明而直接调用。例如例5.1中, 函max的定义放在main 函之前,因此可在main函中省去对 max函的函说明int max(int a,int b)。 3. 如在所有函定义之前, 在函外预先说明了各个函的类型,则在以后的各主调函中,可不再对被调函作说明。例如: char str(int a); float f(float b); main() { …… } char str(int a) { …… } float f(float b) { …… } 其中第一,二行对str函和f函预先作了说明。 因此在以后各函中无须对str和f函再作说明就可直接调用。 4. 对库函的调用不需要再作说明, 但必须把该函的头文件用include命令包含在源文件前部。组作为函组可以作为函的参使用,进行据传送。 组用作函有两种形式,一种是把组元素(下标变量)作为实参使用; 另一种是把组名作为函的形参和实参使用。一、组元素作函实参组元素就是下标变量,它与普通变量并无区别。 因此它作为函实参使用与普通变量是完全相同的,在发生函调用时, 把作为实参的组元素的值传送给形参,实现单向的值传送。例5.4说明了这种情况。[例5.4]判别一个组中各元素的值,若大于0 则输出该值,若小于等于0则输出0值。编程如下: void nzp(int v) { if(v>0) printf("%d ",v); else printf("%d ",0); } main() { int a[5],i; printf("input 5 numbers\n"); for(i=0;i<5;i++) { scanf("%d",&a[i]); nzp(a[i]); } }void nzp(int v) { …… } main() { int a[5],i; printf("input 5 numbers\n"); for(i=0;i<5;i++) { scanf("%d",&a[i]); nzp(a[i]); } }   本程序中首先定义一个无返回值函nzp,并说明其形参v 为整型变量。在函体中根据v值输出相应的结果。在main函中用一个for 语句输入组各元素, 每输入一个就以该元素作实参调用一次nzp函,即把a[i]的值传送给形参v,供nzp函使用。 二、组名作为函   用组名作函与用组元素作实参有几点不同: 1. 用组元素作实参时,只要组类型和函的形参变量的类型一致,那么作为下标变量的组元素的类型也和函形参变量的类型是一致的。因此, 并不要的形参也是下标变量。 换句话说,对组元素的处理是按普通变量对待的。用组名作函时, 则要形参和相对应的实参都必须是类型相同的组,都必须有明确的组说明。当形参和实参二者不一致时,即会发生错误。 2. 在普通变量或下标变量作函时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函调用时发生的值传送是把实参变量的值赋予形参变量。在用组名作函时,不是进行值的传送,即不是把实参组的每一个元素的值都赋予形参组的各个元素。因为实际上形参组并不存在,编译系统不为形参组分配内存。那么,据的传送是如何实现的呢? 在第四章中我们曾介绍过,组名就是组的首地址。因此在组名作函时所进行的传送只是地址的传送, 也就是说把实参组的首地址赋予形参组名。形参组名取得该首地址之后,也就等于有了实在的组。实际上是形参组和实参组为同一组,共同拥有一段内存空间。图5.1说明了这种情形。图中设a为实参组,类型为整型。a占有以2000 为首地址的一块内存区。b为形参组名。当发生函调用时,进行地址传送, 把实参 组a的首地址传送给形参组名b,于是b也取得该地址2000。 于是a,b两组共同占有以2000 为首地址的一段连续内存单元。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内 存单元(整型组每个元素占二字节)。例如a[0]和b[0]都占用2000和2001单元,当然a[0]等于b[0]。类推则有a[i]等于b[i]。 [例5.5]组a中存放了一个学生5门课程的成绩,平均成绩。 float aver(float a[5]) { int i; float av,s=a[0]; for(i=1;i<5;i++) s=s+a[i]; av=s/5; return av; } void main() { float sco[5],av; int i; printf("\ninput 5 scores:\n"); for(i=0;i<5;i++) scanf("%f",&sco[i]); av=aver(sco); printf("average score is %5.2f",av); } float aver(float a[5]) { …… } void main() { …… for(i=0;i<5;i++) scanf("%f",&sco[i]); av=aver(sco); …… }   本程序首先定义了一个实型函aver,有一个形参为实型组a,长度为5。在函aver中,把各元素值相加出平均值,返回给主函。主函main 中首先完成组sco的输入,然后以sco作为实参调用aver函,函返回值送av,最后输出av值。 从运行情况可以看出,程序实现了所要的功能 3. 前面已经讨论过,在变量作函时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同, 而形参的值发生改变后,实参并不变化, 两者的终值是不同的。例5.3证实了这个结论。 而当用组名作函时,情况则不同。 由于实际上形参和实参为同一组, 因此当形参组发生变化时,实参组也随之变化。 当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函之后实参组的值将由于形参组值的变化而变化。为了说明这种情况,把例5.4改为例5.6的形式。[例5.6]题目同5.4例。改用组名作函。 void nzp(int a[5]) { int i; printf("\nvalues of array a are:\n"); for(i=0;i<5;i++) { if(a[i]<0) a[i]=0; printf("%d ",a[i]); } } main() { int b[5],i; printf("\ninput 5 numbers:\n"); for(i=0;i<5;i++) scanf("%d",&b[i]); printf("initial values of array b are:\n"); for(i=0;i<5;i++) printf("%d ",b[i]); nzp(b); printf("\nlast values of array b are:\n"); for(i=0;i<5;i++) printf("%d ",b[i]); } void nzp(int a[5]) { …… } main() { int b[5],i; …… nzp(b); …… }   本程序中函nzp的形参为整组a,长度为 5。 主函中实参组b也为整型,长度也为5。在主函中首先输入组b的值,然后输出组b的初始值。 然后以组名b为实参调用nzp函。在nzp中,按要把负值单元清0,并输出形参组a的值。 返回主函之后,再次输出组b的值。从运行结果可以看出,组b 的初值和终值是不同的,组b 的终值和组a是相同的。这说明实参形参为同一组,它们的值同时得以改变。 用组名作为函时还应注意以下几点: a. 形参组和实参组的类型必须一致,否则将引起错误。 b. 形参组和实参组的长度可以不相同,因为在调用时,只传送首地址而不检查形参组的长度。当形参组的长度与实参组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。如把例5.6修改如下: void nzp(int a[8]) { int i; printf("\nvalues of array aare:\n"); for(i=0;i<8;i++) { if(a[i]<0)a[i]=0; printf("%d",a[i]); } } main() { int b[5],i; printf("\ninput 5 numbers:\n"); for(i=0;i<5;i++) scanf("%d",&b[i]); printf("initial values of array b are:\n"); for(i=0;i<5;i++) printf("%d",b[i]); nzp(b); printf("\nlast values of array b are:\n"); for(i=0;i<5;i++) printf("%d",b[i]); }   本程序与例5.6程序比,nzp函的形参组长度改为8,函体中,for语句的循环条件也改为i<8。因此,形参组 a和实参组b的长度不一致。编译能够通过,但从结果看,组a的元素a[5],a[6],a[7]显然是无意义的。c. 在函形参表中,允许不给出形参组的长度,或用一个变量来表示组元素的个。 例如:可以写为: void nzp(int a[]) 或写为 void nzp(int a[],int n)   其中形参组a没有给出长度,而由n值动态地表示组的长度。n的值由主调函的实参进行传送。 由此,例5.6又可改为例5.7的形式。 [例5.7] void nzp(int a[],int n) { int i; printf("\nvalues of array a are:\n"); for(i=0;i数形参组a没有给出长度,由n 动态确定该长度。在main函中,函调用语句为nzp(b,5),其中实参5将赋予形参n作为形参组的长度。 d. 多维组也可以作为函的参。 在函定义时对形参组可以指定每一维的长度,也可省去第一维的长度。因此,以下写法都是合法的。 int MA(int a[3][10]) 或 int MA(int a[][10]) 函的嵌套调用   C语言中不允许作嵌套的函定义。因此各函之间是平行的,不存在上一级函和下一级函的问题。 但是C语言允许在一个的定义中出现对另一个的调用。 这样就出现了函的嵌套调用。即在被调函中又调用其它函。 这与其它语言的子程序嵌套的情形是类似的。其关系可表示如图5.2。   图5.2表示了两层嵌套的情形。其执行过程是:执行main函中调用a函的语句时,即转去执行a函,在a函中调用b 函时,又转去执行b函,b函执行完毕返回a函的断点继续执行,a 函执行完毕返回main函的断点继续执行。 [例5.8]计算s=22!+32! 本题可编写两个函一个是用来计算平方值的函f1, 另一个是用来计算阶乘值的函f2。主函先调f1计算出平方值, 再在f1中以平方值为实参,调用 f2计算其阶乘值,然后返回f1,再返回主函,在循环程序中计算累加和。 long f1(int p) { int k; long r; long f2(int); k=p*p; r=f2(k); return r; } long f2(int q) { long c=1; int i; for(i=1;i<=q;i++) c=c*i; return c; } main() { int i; long s=0; for (i=2;i<=3;i++) s=s+f1(i); printf("\ns=%ld\n",s); } long f1(int p) { …… long f2(int); r=f2(k); …… } long f2(int q) { …… } main() { …… s=s+f1(i); …… }   在程序中,函f1和f2均为长整型,都在主函之前定义, 故不必再在主函中对f1和f2加以说明。在主程序中, 执行循环程序依次把i值作为实参调用函f1i2值。在f1中又发生对函f2的调用,这时是把i2的值作为实参去调f2,在f2 中完成i2! 的计算。f2执行完毕把C值(即i2!)返回给f1,再由f1 返回主函实现累加。至此,由函的嵌套调用实现了题目的要。 由于值很大, 所以函和一些变量的类型都说明为长整型,否则会造成计算错误。 函的递归调用   一个在它的函体内调用它自身称为递归调用。 这种函称为递归函。C语言允许函的递归调用。在递归调用中, 主调函又是被调函。执行递归函将反复调用其自身。 每调用一次就进入新的一层。例如有函f如下: int f (int x) { int y; z=f(y); return z; }   这个函一个递归函。 但是运行该函将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行, 必须在函内有终止递归调用的手段。常用的办法是加条件判断, 满足某种条件后就不再作递归调用,然后逐层返回。 下面举例说明递归调用的执行过程。 [例5.9]用递归法计算n!用递归法计算n!可用下述公式表示: n!=1 (n=0,1) n×(n-1)! (n>1) 按公式可编程如下: long ff(int n) { long f; if(n<0) printf("n<0,input error"); else if(n==0||n==1) f=1; else f=ff(n-1)*n; return(f); } main() { int n; long y; printf("\ninput a inteager number:\n"); scanf("%d",&n); y=ff(n); printf("%d!=%ld",n,y); } long ff(int n) { …… else f=ff(n-1)*n; …… } main() { …… y=ff(n); …… }   程序中给出的函ff是一个递归函。主函调用ff 后即进入函ff执行,如果n<0,n==0或n=1时都将结束函的执行,否则就递归调用ff函自身。由于每次递归调用的实参为n-1,即把n-1 的值赋予形参n,最后当n-1的值为1时再作递归调用,形参n的值也为1,将使递归终止。然后可逐层退回。下面我们再举例说明该过程。 设执行本程序时输入为5, 即 5!。在主函中的调用语句即为y=ff(5),进入ff函后,由于n=5,不等于0或1,故应执行f=ff(n-1)*n,即f=ff(5-1)*5。该语句对ff作递归调用即ff(4)。 逐次递归展开如图5.3所示。进行四次递归调用后,ff函形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函。ff(1)的函返回值为1,ff(2)的返回值为1*2=2,ff(3)的返回值为2*3=6,ff(4) 的返 回值为6*4=24,最后返回值ff(5)为24*5=120。   例5. 9也可以不用递归的方法来完成。如可以用递推法,即从1开始乘以2,再乘以3…直到n。递推法比递归法更容易理解和实现。但是有些问题则只能用递归算法才能实现。典型的问题是Hanoi塔问题。      [例5.10]Hanoi塔问题 一块板上有三根针,A,B,C。A针上套有64个大小不等的圆盘, 大的在下,小的在上。如图5.4所示。要把这64个圆盘从A针移动C针上,每次只能移动一个圆盘,移动可以借助B针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。移动的步骤。 本题算法分析如下,设A上有n个盘子。 如果n=1,则将圆盘从A直接移动到C。 如果n=2,则: 1.将A上的n-1(等于1)个圆盘移到B上; 2.再将A上的一个圆盘移到C上; 3.最后将B上的n-1(等于1)个圆盘移到C上。 如果n=3,则: A. 将A上的n-1(等于2,令其为n`)个圆盘移到B(借助于C), 步骤如下: (1)将A上的n`-1(等于1)个圆盘移到C上,见图5.5(b)。 (2)将A上的一个圆盘移到B,见图5.5(c) (3)将C上的n`-1(等于1)个圆盘移到B,见图5.5(d) B. 将A上的一个圆盘移到C,见图5.5(e) C. 将B上的n-1(等于2,令其为n`)个圆盘移到C(借助A), 步骤如下: (1)将B上的n`-1(等于1)个圆盘移到A,见图5.5(f) (2)将B上的一个盘子移到C,见图5.5(g) (3)将A上的n`-1(等于1)个圆盘移到C,见图5.5(h)。 到此,完成了三个圆盘的移动过程。 从上面分析可以看出,当n大于等于2时, 移动的过程可分解为 三个步骤: 第一步 把A上的n-1个圆盘移到B上; 第二步 把A上的一个圆盘移到C上; 第三步 把B上的n-1个圆盘移到C上;其中第一步和第三步是类同的。 当n=3时,第一步和第三步又分解为类同的三步,即把n`-1个圆盘从一个针移到另一个针上,这里的n`=n-1。 显然这是一个递归过 程,据此算法可编程如下: move(int n,int x,int y,int z) { if(n==1) printf("%c-->%c\n",x,z); else { move(n-1,x,z,y); printf("%c-->%c\n",x,z); move(n-1,y,x,z); } } main() { int h; printf("\ninput number:\n"); scanf("%d",&h); printf("the step to moving %2d diskes:\n",h); move(h,'a','b','c'); } move(int n,int x,int y,int z) { if(n==1) printf("%-->%c\n",x,z); else { move(n-1,x,z,y); printf("%c-->%c\n",x,z); move(n-1,y,x,z); } } main() { …… move(h,'a','b','c'); }   从程序中可以看出,move函一个递归函,它有四个形参n,x,y,z。n表示圆盘,x,y,z分别表示三根针。move 函的功能是把x上的n个圆盘移动到z 上。当n==1时,直接把x上的圆盘移至z上,输出x→z。如n!=1则分为三步:递归调用move函,把n-1个圆盘从x移到y;输出x→z;递归调用move函,把n-1个圆盘从y移到z。在递归调用过程中n=n-1,故n的值逐次递减,最后n=1时,终止递归,逐层返回。当n=4 时程序运行的结果为 input number: 4 the step to moving 4 diskes: a→b a→c b→c a→b c→a c→b a→b a→c b→c b→a c→a b→c a→b a→c b→c 变量的作用域   在讨论函的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函内才是有效的, 离开该函就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。 一、局部变量   局部变量也称为内部变量。局部变量是在函内作定义说明的。其作用域仅限于函内, 离开该函后再使用这种变量是非法的。 例如: int f1(int a) /*函f1*/ { int b,c; …… }a,b,c作用域 int f2(int x) /*函f2*/ { int y,z; }x,y,z作用域 main() { int m,n; } m,n作用域 在函f1内定义了三个变量,a为形参,b,c为一般变量。在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。同理,x,y,z的作用域限于f2内。 m,n的作用域限于main函内。关于局部变量的作用域还要说明以下几点: 1. 主函中定义的变量也只能在主函中使用,不能在其它函中使用。同时,主函中也不能使用其它函中定义的变量。因为主函也是一个,它与其它函是平行关系。这一点是与其它语言不同的,应予以注意。 2. 形参变量是属于被调函的局部变量,实参变量是属于主调函的局部变量。 3. 允许在不同的函中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在例5.3 中,形参和实参的变量名都为n,是完全允许的。4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。例如: main() { int s,a; …… { int b; s=a+b; ……b作用域 } ……s,a作用域 }[例5.11]main() { int i=2,j=3,k; k=i+j; { int k=8; if(i==3) printf("%d\n",k); } printf("%d\n%d\n",i,k); } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i=3) printf("%d\n",k); } printf("%d\n%d\n",i,k); }   本程序在main中定义了i,j,k三个变量,其中k未赋初值。 而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第7行对i赋值为3,故以输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第4 行已获得为5,故输出也为5。 二、全局变量 全局变量也称为外部变量,它是在函外部定义的变量。 它不属于哪一个,它属于一个源程序文件。其作用域是整个源程序。在函中使用全局变量,一般应作全局变量说明。 只有在函内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个之前定义的全局变量,在该函内使用可不再加以说明。 例如: int a,b; /*外部变量*/ void f1() /*函f1*/ { …… } float x,y; /*外部变量*/ int fz() /*函fz*/ { …… } main() /*主函*/ { …… }/*全局变量x,y作用域 全局变量a,b作用域*/   从上例可以看出a、b、x、y 都是在函外部定义的外部变量,都是全局变量。但x,y 定义在函f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。 [例5.12]输入正方体的长宽高l,w,h。体积及三个面x*y,x*z,y*z的面积。 int s1,s2,s3; int vs( int a,int b,int c) { int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } main() { int v,l,w,h; printf("\ninput length,width and height\n"); scanf("%d%d%d",&l,&w,&h); v=vs(l,w,h); printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3); }   本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积,其作用域为整个程序。函vs用来正方体体积和三个面积, 函的返回值为体积v。由主函完成长宽高的输入及结果输出。由于C语言规定函返回值只有一个, 当需要增加函的返回据时,用外部变量是一种很好的方式。本例中,如不使用外部变量, 在主函中就不可能取得v,s1,s2,s3四个值。而采用了外部变量, 在函vs中得的s1,s2,s3值在main 中仍然有效。因此外部变量是实现函之间据通讯的有效手段。对于全局变量还有以下几点说明: 1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函之外,且只能定义一次。其一般形式为: [extern] 类型说明符 变量名,变量名… 其中方括号内的extern可以省去不写。 例如: int a,b; 等效于: extern int a,b;   而外部变量说明出现在要使用该外部变量的各个函内, 在整个程序内,可能出现多次,外部变量说明的一般形式为: extern 类型说明符 变量名,变量名,…; 外部变量在定义时就已分配了内存单元, 外部变量定义可作初始赋值,外部变量说明不能再赋初始值, 只是表明在函内要使用某外部变量。 2. 外部变量可加强函模块之间的据联系, 但是又使函要依赖这些变量,因而使得函的独立性降低。从模块化程序设计的观点来看这是不利的, 因此在不必要时尽量不要使用全局变量。 3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。 [例5.13]int vs(int l,int w) { extern int h; int v; v=l*w*h; return v; } main() { extern int w,h; int l=5; printf("v=%d",vs(l,w)); } int l=3,w=4,h=5;   本例程序中,外部变量在最后定义, 因此在前面函中对要用的外部变量必须进行说明。外部变量l,w和vs函的形参l,w同名。外部变量都作了初始赋值,mian函中也对l作了初始化赋值。执行程序时,在printf语句中调用vs函,实参l的值应为main中定义的l值,等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函中使用的h 为外部变量,其值为5,因此v的计算结果为100,返回主函后输出。变量的存储类型各种变量的作用域不同, 就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式, 也称为存储方式。 变量的存储方式可分为“静态存储”和“动态存储”两种。   静态存储变量通常是在变量定义时就分定存储单元并一直保持不变, 直至整个程序结束。5.5.1节中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中,使用它时才分配存储单元, 使用完毕立即释放。 典型的例子是函的形式参,在函定义时并不给形参分配存储单元,只是在函被调用时,才予以分配, 调用函完毕立即释放。如果一个被多次调用,则反复地分配、 释放形参变量的存储单元。从以上分析可知, 静态存储变量是一直存在的, 而动态存储变量则时而存在时而消失。我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的时间。 生存期和作用域是从时间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。 一个变量究竟属于哪一种存储方式, 并不能仅从其作用域来判断,还应有明确的存储类型说明。   在C语言中,对变量的存储类型说明有以下四种: auto     自动变量 register   寄存器变量 extern    外部变量 static    静态变量   自动变量和寄存器变量属于动态存储方式, 外部变量和静态变量属于静态存储方式。在介绍了变量的存储类型之后, 可以知道对一个变量的说明不仅应说明其据类型,还应说明其存储类型。 因此变量说明的完整形式应为: 存储类型说明符 据类型说明符 变量名,变量名…; 例如: static int a,b;           说明a,b为静态类型变量 auto char c1,c2;          说明c1,c2为自动字符变量 static int a[5]={1,2,3,4,5};    说明a为静整型组 extern int x,y;           说明x,y为外部整型变量 下面分别介绍以上四种存储类型: 一、自动变量的类型说明符为auto。   这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定, 函内凡未加存储类型说明的变量均视为自动变量, 也就是说自动变量可省去说明符auto。 在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。例如: { int i,j,k; char c; …… }等价于: { auto int i,j,k; auto char c; …… }   自动变量具有以下特点: 1. 自动变量的作用域仅限于定义该变量的个体内。在函中定义的自动变量,只在该函内有效。在复合语句中定义的自动变量只在该复合语句中有效。 例如: int kv(int a) { auto int x,y; { auto char c; } /*c的作用域*/ …… } /*a,x,y的作用域*/ 2. 自动变量属于动态存储方式,只有在使用它,即定义该变量的函被调用时才给它分配存储单元,开始它的生存期。函调用结束,释放存储单元,结束生存期。因此函调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。例如以下程序: main() { auto int a,s,p; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0){ s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); } s,p是在复合语句内定义的自动变量,只能在该复合语句内有效。而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误。 3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函或复合语句内), 因此不同的个体中允许使用同名的变量而不会混淆。 即使在函内定义的自动变量也可与该函内部的复合语句中定义的自动变量同名。例5.14表明了这种情况。 [例5.14] main() { auto int a,s=100,p=100; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0) { auto int s,p; s=a+a; p=a*a; printf("s=%d p=%d\n",s,p); } printf("s=%d p=%d\n",s,p); }   本程序在main函中和复合语句内两次定义了变量s,p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s,p起作用,故s的值应为a+ a,p的值为a*a。退出复合语句后的s,p 应为main所定义的s,p,其值在初始化时给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同, 但却是两个不同的变量。 4. 对构造类型的自动变量如组等,不可作初始化赋值。 二、外部变量外部变量的类型说明符为extern。 在前面介绍全局变量时已介绍过外部变量。这里再补充说明外部变量的几个特点: 1. 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变是是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。 2. 当一个源程序由若干个源文件组成时, 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C组成: F1.C int a,b; /*外部变量定义*/ char c; /*外部变量定义*/ main() { …… } F2.C extern int a,b; /*外部变量说明*/ extern char c; /*外部变量说明*/ func (int x,y) { …… } 在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。 对构造类型的外部变量, 如组等可以在说明时作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。 三、静态变量   静态变量的类型说明符是static。 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量, 例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。 对于自动变量,前面已经介绍它属于动态存储方式。 但是也可以用static定义它为静态自动变量,或称静态局部变量,从而成为静态存储方式。 由此看来, 一个变量可由static进行再说明,并改变其原有的存储方式。 1. 静态局部变量   在局部变量的说明前再加上static说明符就构成静态局部变量。 例如: static int a,b; static float array[5]={1,2,3,4,5};      静态局部变量属于静态存储方式,它具有以下特点: (1)静态局部变量在函内定义,但不象自动变量那样,当调用时就存在,退出函时就消失。静态局部变量始终存在着,也就是说它的生存期为整个源程序。 (2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函内使用该变量。退出该函后, 尽管该变量还继续存在,但不能使用它。 (3)允许对构造类静态局部量赋初值。在组一章中,介绍组初始化时已作过说明。若未赋以初值,则由系统自动赋以0值。 (4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函后不能使用,但如再次调用定义它的函时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个且要在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。 [例5.15]main() { int i; void f(); /*函说明*/ for(i=1;i<=5;i++) f(); /*函调用*/ } void f() /*函定义*/ { auto int j=0; ++j; printf("%d\n",j); }   程序中定义了函f,其中的变量j 说明为自动变量并赋予初始值为0。当main中多次调用f时,j均赋初值为0,故每次输出值均为1。现在把j改为静态局部变量,程序如下: main() { int i; void f(); for (i=1;i<=5;i++) f(); } void f() { static int j=0; ++j; printf("%d\n",j); } void f() { static int j=0; ++j; printf("%d/n",j); } 由于j为静态变量,能在每次调用后保留其值并在下一次调用时继续使用,所以输出值成为累加的结果。读者可自行分析其执行过程。 2.静态全局变量   全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它 的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。 四、寄存器变量   上述各类变量都存放在存储器内, 因此当对一个变量频繁读写时,必须要反复访问内存储器,从而花费大量的存取时间。 为此,C语言提供了另一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写, 这样可提高效率。寄存器变量的说明符是register。 对于循环次较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量。 [例5.16]∑200i=1imain() { register i,s=0; for(i=1;i<=200;i++) s=s+i; printf("s=%d\n",s); } 本程序循环200次,i和s都将频繁使用,因此可定义为寄存器变量。 对寄存器变量还要说明以下几点: 1. 只有局部自动变量和形式参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式。凡需要采用静态存储方式的量不能定义为寄存器变量。 2. 在Turbo C,MS C等微机上使用的C语言中, 实际上是把寄存器变量当成自动变量处理的。因此速度并不能提高。 而在程序中允许使用寄存器变量只是为了与标准C保持一致。3. 即使能真正使用寄存器变量的机器,由于CPU 中寄存器的个是有限的,因此使用寄存器变量的个也是有限的。 内部函和外部函   函一旦定义后就可被其它函调用。 但当一个源程序由多个源文件组成时, 在一个源文件中定义的函能否被其它源文件中的函调用呢?为此,C语言又把函分为两类: 一、内部函   如果在一个源文件中定义的函只能被本文件中的函调用,而不能被同一源程序其它文件中的函调用, 这种函称为内部函 。定义内部函的一般形式是: static 类型说明符 函名(形参表) 例如: static int f(int a,int b) 内部函也称为静态函。但此处静态static 的含义已不是指存储方式,而是指对函的调用范围只局限于本文件。 因此在不同的源文件中定义同名的静态函不会引起混淆。 二、外部函   外部函在整个源程序中都有效,其定义的一般形式为: extern 类型说明符 函名(形参表) 例如: extern int f(int a,int b)如在函定义中没有说明extern或static则隐含为extern。在一个源文件的函中调用其它源文件中定义的外部函时,应 用extern说明被调函为外部函。例如: F1.C (源文件一) main() { extern int f1(int i); /*外部函说明,表示f1函 在其它源文件中*/ …… } F2.C (源文件二) extern int f1(int i); /*外部函定义*/ { …… } 本章小结 1. 函的分类 (1)库函:由C系统提供的函; (2)用户定义函:由用户自己定义的函; (3)有返回值的函向调用者返回函值,应说明函类型( 即返回值的类型 ); (4)无返回值的函:不返回函值,说明为空(void)类型; (5)有参函:主调函向被调函传送据; (6)无参函:主调函与被调函间无据传送; (7)内部函:只能在本源文件中使用的函; (8)外部函:可在整个源程序中使用的函。 2. 函定义的一般形式 [extern/static] 类型说明符 函名([形参表]) 方括号内为可选项。 3. 函说明的一般形式 [extern] 类型说明符 函名([形参表]); 4. 函调用的一般形式 函名([实参表]) 5. 函的参分为形参和实参两种,形参出现在函定义中,实参出现在函调用中,发生函调用时,将把实参的值传送给形参。 6. 函的值是指函的返回值,它是在函中由return语句返回的。 7. 组名作为函时不进行值传送而进行地址传送。形参和实参实际上为同一组的两个名称。因此形参组的值发生变化,实参组的值当然也变化。 8. C语言中,允许函的嵌套调用和函的递归调用。 9. 可从三个方面对变量分类,即变量的据类型,变量作用域和变量的存储类型。在第二章中主要介绍变量的据类型,本章中介绍了变量的作用域和变量的存储类型。 10.变量的作用域是指变量在程序中的有效范围, 分为局部变量和全局变量。 11.变量的存储类型是指变量在内存中的存储方式,分为静态存储和动态存储,表示了变量的生存期。 12.变量分类特性表存储方式存储类型说明符何处定义生存期作用域赋值前的值可赋初值类型动态存储自动变量 auto 寄存器变量 register 函或复合语句内被调用时在定义它的函或复合语句内不定基本类型int或char外部变量extern函之外整个源程序整个源程序静态局部变量static 函或复合语句内静态全局变量static 函之外整个源程序在定义它的函或复合语句内在定义它的源文件内0任何类型 资料收集:beck Copyright 2002 www.vcok.com, All Rights Reserved
图书目录 第Ⅰ部分 预备知识   第1章 ANSI C概述1   1.1 什么是C   1.2 C程序的结构   1.3 变量、值和类型   1.4 表达式   1.5 语句   1.6 函   1.7 小结   1.8 复习题   1.9 编程练习   第2章 C的据类型   2.1 枚举类型   2.2 据和内存   2.3 指针   2.4 组   2.5 指针和组   2.6 记录   2.7 动态分配   2.8 小结   2.9 复习题   2.10 编程练习   第3章 库和接口   3.1 接口的概念   3.2 随机字   3.3 字符串   3.4 标准的I/O库   3.5 其他ANSI库   3.6 小结   3.7 复习题   3.8 编程练习   第Ⅱ部分 递归和算法分析   第4章 递归入门   4.1 一个简单的递归示例   4.2 阶乘   4.3 费波那契函   4.4 其他递归示例   4.5 以递归的方式思考   4.6 小结   4.7 复习题   4.8 编程练习   第5章 递归过程   5.1 汉诺塔   5.2 产生排列   5.3 递归在绘图中的应用   5.4 小结   5.5 复习题   5.6 编程练习   第6章 回溯算法   6.1 用递归回溯解决迷宫问题   6.2 回溯与游戏   6.3 小结   6.4 复习题   6.5 编程练习   第7章 算法分析   7.1 排序问题   7.2 计算复杂度   7.2.6 大O的正式定义   7.3 递归帮助   7.4 标准复杂度类型   7.5 快速排序算法   7.6 学归纳法   7.7 小结   7.8 复习题   7.9 编程练习   第Ⅲ部分 据抽象   第8章 抽象据类型   ……   第9章 效率与ADT   第10章 线性结构   第11章 符号表   第Ⅳ部分 递归据   第12章 递归链表   第13章 树   第14章 表达式树   第15章 集合   第16章 图   第17章 展望Java
C/C++语言经典实用趣味程序设计编程百例精解C/C++语言经典实用趣味程序设计编程百例精解(1)1.绘制余弦曲线 2.绘制余弦曲线和直线 3.绘制圆 4.歌星大奖赛 5.最大 6.高次方的尾 7.阶乘零的个 8.借书方案知多少 9.杨辉三角形 10.制转换 C/C++语言经典实用趣味程序设计编程百例精解(2)11.打鱼还是晒网 12.抓交通肇事犯 13.该存多少钱 14.怎样存钱利最大 15.捕鱼和分鱼 16.出售金鱼 17.平分七筐鱼 18.有限5位 19.8除不尽的自然 20.一个奇异的三位C/C++语言经典实用趣味程序设计编程百例精解(3)21.4位反序 22.车速 23.由两个平方三位获得三个平方二位 24.阿姆斯特朗 25.完全 26.亲密 27.自守 28.回文 29.具有abcd=(ab+cd)2性质的四位 30. C/C++语言经典实用趣味程序设计编程百例精解(4)31.歌德巴赫猜想 32.可逆素 33.回文素 34.要发就发 35.素幻方 36.百钱百鸡问题 37.爱因斯坦的学题 38.换分币 39.年龄几何 40.三色球问题C/C++语言经典实用趣味程序设计编程百例精解(5)41.马克思手稿中的学题 42.最大公约和最小公倍 43.分比较 44.分之和 45.将真分分解为埃及分 46.列出真分序列 47.计算分的精确值 48.新娘和新郞 49.委派任务 50.谁在说谎 C/C++语言经典实用趣味程序设计编程百例精解(6)51.谁是窃贼 52.黑与白 53.迷语博士的难题(1) 54.迷语博士的难题(2) 55.哪个大夫哪天值班 56.区分旅客国籍 57.谁家孩子跑最慢 58.拉丁方阵 59.填表格 60.1~9分成1:2:3的三个3位 C/C++语言经典实用趣味程序设计编程百例精解(7)61.1~9组成三个3位的平方 62.由8个整形成奇特的立方体 63.减式还原 64.乘式还原 65.乘式还原(2) 66.除式还原(1) 67.除式还原(2) 68.九位累进可除 69.魔术师的猜牌术(1) 70.魔术师的猜牌术(2) C/C++语言经典实用趣味程序设计编程百例精解(8)71.约瑟夫问题 72.邮票组合 73.和能表示1~23的5个正整 74.可称1~40磅的4块砝码 75.10个小孩分糖果 76.小明买书 77.波松瓦酒的分酒趣题 78.π的近似值 79.π的近似值(2) 80.奇平方的一个有趣性质 C/C++语言经典实用趣味程序设计编程百例精解(9)81.角谷猜想 82.四方定理 83.卡布列克常 84.尼科彻斯定理 85.回文的形成 86.自动发牌 87.黑白子交换 88.常胜将军 89.抢30 90.搬山游戏 C/C++语言经典实用趣味程序设计编程百例精解(10)91.人机猜游戏 92.人机猜游戏(2) 93.汉诺塔 94.兎子产子 95.将阿拉伯字转换为罗马字 96.选美比赛 97.满足特异条件的列 98.八皇后问题 99.超长正整的加法 100.字移动
《明解C语言 第3版 入门篇》是日本的C语言经典教材,自出版以来不断重印、修订,被誉为“C语言圣经”。 《明解C语言 第3版 入门篇》图文并茂,示例丰富,第3版从190段代码和164幅图表增加到205段代码和220幅图表,对C语言的基础知识进行了彻底剖析,内容涉及组、函、指针、文件操作等。对于C语言语法以及一些难以理解的概念,均以精心绘制的示意图,清晰、通俗地进行讲解。原著在日本广受欢迎,始终位于网上书店C语言著作排行榜首位。 第1章 初识C语言 1 1-1 显示计算结果 2 计算整的和并显示结果 2 程序和编译 2 注释 3 固定代码 4 printf函:格式化输出函 4 语句 5 计算并显示整的差 5 格式化字符串和转换说明 6 符号的称呼 8 无格式化输出 8 字符串常量 10 转义字符 10 1-2 变量 11 变量和声明 11 赋值 12 初始化 13 声明时初始化 14 初始化和赋值 14 1-3 输入和显示 16 通过键盘进行输入 16 格式化输入函scanf 16 乘法运算 17 输出函puts 18 总结 21 第2章 运算和据类型 23 2-1 运算 24 运算符和操作 24 乘除运算符和加减运算符 25 除法运算的商和余 25 使用printf函打印% 26 获取整的最后一位字 26 多个转换说明 27 单目运算符 28 赋值运算符 29 表达式和赋值表达式 30 表达式语句 30 2-2 据类型 31 平均值 31 据类型 32 据类型和对象 33 整型常量和浮点型常量 34 double类型的运算 34 据类型和运算 35 类型转换 37 转换说明 39 总结 42 第3章 分支结构程序 45 3-1 if语句 46 if语句·其1 46 奇的判定 47 if语句·其2 48 奇·偶的判断 49 非0的判断 49 if语句的结构图 50 相等运算符 52 余的判断 53 关系运算符 54 嵌套的if语句 55 判断 57 计算较大值 58 计算三个的最大值 59 条件运算符 60 差值计算 61 复合语句(程序块) 62 逻辑运算符 65 短路值 67 3-2 switch语句 70 switch语句和break语句 70 复杂的switch语句 72 switch语句和if语句 73 选择语句 74 总结 75 第4章 程序的循环控制 77 4-1 do语句 78 do语句 78 复合语句(程序块)中的声明 79 读取一定范围内的值 80 逻辑非运算符·德摩根定律 81 德摩根定律 81 多个整的和及平均值 82 复合赋值运算符 84 后置递增运算符和后置递减运算符 85 4-2 while语句 87 while语句 87 用递减运算符简化程序代码 88 据递增 90 限定次的循环操作 91 字符常量和putchar函 92 do语句和while语句 93 前置递增运算符和前置递减运算符 93 do语句的显示 95 逆向显示整值 96 4-3 for语句 99 for语句 99 使用for语句实现固定次的循环 101 偶的枚举 103 约的枚举 104 表达式语句和空语句 104 循环语句 105 4-4 多重循环 107 二重循环 107 用break语句强制结束循环 108 显示图形 109 多重循环 111 4-5 程序的组成元素和格式 114 关键字 114 运算符 114 标识符 114 分隔符 115 常量和字符串常量 115 自由的书写格式 116 连接相邻的字符串常量 117 缩进 117 总结 118 第5章 组 121 5-1 组 122 组 122 组的声明(使用组前的准备) 123 访问组(组的使用方法) 123 组的遍历 124 组初始化 126 组的复制 127 输入组元素的值 129 对组的元素进行倒序排列 129 使用组进行成绩处理 131 对象式宏 131 组元素的最大值和最小值 133 赋值表达式的判断 134 组的元素个 135 5-2 多维组 138 多维组 138 总结 142 第6章 函 145 6-1 什么是函 146 main函和库函 146 什么是函 146 函定义 147 函调用 148 三个中的最大值 151 将函的返回值作为参传递给函 152 调用其他函 154 值传递 155 6-2 函设计 158 没有返回值的函 158 通用性 159 不含形参的函 160 函返回值的初始化 161 作用域 161 文件作用域 162 声明和定义 163 函原型声明 163 头文件和文件包含指令 164 函的通用性 165 组的传递 166 函的传递和const类型的修饰符 168 线性查找(顺序查找) 170 哨兵查找法 172 多维组的传递 175 6-3 作用域和存储期 178 作用域和标识符的可见性 178 存储期 180 总结 185 第7章 基本据类型 189 7-1 基本据类型和 190 算类型和基本据类型 190 基 191 基转换 192 7-2 整型和字符型 195 整型和字符型 195 头文件 196 字符型 199 位和CHAR_BIT 200 sizeof运算符 200 size_t型和typedef声明 202 整型的灵活运用 202 整型的内部表示 204 无符号整的内部表示 205 有符号整的内部表示 207 按位操作的逻辑运算 209 位移运算符 212 整型常量 216 整型常量的据类型 218 整的显示 218 据溢出和异常 220 7-3 浮点型 221 浮点型 221 浮点型常量 223 头文件 224 循环的控制 225 7-4 运算和运算符 228 运算符的优先级和结合性 228 优先级 228 结合性 228 据类型转换 230 总结 232 第8章 动手编写各种程序吧 235 8-1 函式宏 236 函据类型 236 函式宏 237 函和函式宏 238 不带参的函式宏 239 函式宏和逗号运算符 240 8-2 排序 243 冒泡排序法 243 8-3 枚举类型 246 枚举类型 246 枚举常量 248 命名空间 250 8-4 递归函 251 函和类型 251 阶乘 252 8-5 输入输出和字符 255 getchar函和EOF 255 从输入复制到输出 256 字字符计 256 字符 258 转义字符 261 总结 263 第9章 字符串的基本知识 265 9-1 什么是字符串 266 字符串字面量 266 字符串字面量的长度 266 字符串 268 字符组的初始化赋值 269 空字符串 270 字符串的读取 270 格式化显示字符串 271 9-2 字符串组 273 字符串组 273 读取字符串组中的字符串 274 9-3 字符串处理 275 字符串长度 275 显示字符串 277 字字符的出现次 278 大小写字符转换 279 字符串组的参传递 280 总结 283 第10章 指针 285 10-1 指针 286 函的参 286 对象和地址 287 取址运算符 288 指针 289 指针运算符 291 10-2 指针和函 293 作为函的指针 293 计算和与差 294 二值互换 296 将两个值排序 297 scanf函和指针 298 指针的类型 299 空指针 300 标量型 301 10-3 指针和组 302 指针和组 302 指针运算符和下标运算符 304 组和指针的不同点 306 组的传递 308 总结 311 第11章 字符串和指针 315 11-1 字符串和指针 316 用组实现的字符串和用指针实现的字符串 316 用组实现的字符串和用指针实现的字符串的不同点 318 字符串组 320 11-2 通过指针操作字符串 323 判断字符串长度 323 字符串的复制 325 不正确的字符串复制 328 返回指针的函 329 11-3 字符串处理库函 330 strlen函字符串的长度 330 strcpy函、strncpy函:复制字符串 331 strcat函、strncat函:连接字符串 332 strcmp函、strncmp函:比较字符串的大小关系 332 atoi函、atol函、atof函:转换字符串 333 总结 336 第12章 结构体 339 12-1 结构体 340 据关联性 340 结构体 342 结构体成员和运算符 344 成员的初始化 345 结构体成员和->运算符 346 结构体和typedef 348 结构体和程序 350 聚合类型 351 返回结构体的函 351 命名空间 353 结构体组 353 派生类型 355 12-2 作为成员的结构体 356 表示坐标的结构体 356 具有结构体成员的结构体 357 总结 361 第13章 文件处理 363 13-1 文件与流 364 文件与流 364 标准流 365 FILE型 365 打开文件 365 关闭文件 368 打开与关闭文件示例 369 文件据汇总 370 写入日期和时间 372 获取上一次运行时的信息 376 显示文件内容 378 文件的复制 380 13-2 文本和二进制 382 在文本文件中保存实 382 文本文件和二进制文件 383 在二进制文件中保存实 384 显示文件自身 386 13-3 printf函与scanf函 389 printf函:带格式输出 389 scanf函:带格式的输入 393 总结 397 附录 C语言简介 399 C语言的历史 400 K&R——C语言的圣经 400 C语言标准规范 400 结语 402 参考文献 405

69,337

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧