C复习数组和函数.docx
《C复习数组和函数.docx》由会员分享,可在线阅读,更多相关《C复习数组和函数.docx(13页珍藏版)》请在冰豆网上搜索。
C复习数组和函数
一、数组的声明
声明数组的语法为在数组名后加上用方括号括起来的维数说明。
本节仅介绍一维数组。
下面是一个整型数组的例子:
intarray[10];
这条语句定义了一个具有10个整型元素的名为array的数组。
这些整数在内存中是连续存储的。
数组的大小等于每个元素的大小乘上数组元素的个数。
方括号中的维数表达式可以包含运算符,但其计算结果必须是一个长整型值。
这个数组是一维的。
下面这些声明是合法的:
intoffset[5+3];
floatcount[5*2+3];
下面是不合法的:
intn=10;
intoffset[n];/*在声明时,变量不能作为数组的维数*/
二、用下标访问数组元素
intoffset[10];
表明该数组是一维数组,里面有10个数,它们分别为offset[0],offset[1],……offset[9];千万注意,数组的第一个元素下标从0开始。
一些刚学编程的人员经常在这儿犯一些错误。
offset[3]=25;
上面的例子是把25赋值给整型数组offset的第四个元素。
在赋值的时候,可以使用变量作为数组下标。
main()
{
inti,offset[10];
for(i=0;i<10;i++)scanf(%d,&offset[i]);
for(i=9;i>=0;i--)printf(%d,offset[i]);
printf(\n);
}
题目的意思是先输入10个整数,存入到数组中,然后反序输出。
三、数组的初始化
前面说了,变量可以在定义的时候初始化,数组也可以。
intarray[5]={1,2,3,4,5};
在定义数组时,可以用放在一对大括号中的初始化表对其进行初始化。
初始化值的个数可以和数组元素个数一样多。
如果初始化的个数多于元素个数,将产生编译错误;如果少于元素个数,其余的元素被初始化为0。
如果维数表达式为空时,那么将用初始化值的个数来隐式地指定数组元素的个数,如下所式:
intarray[]={1,2,3,4,5};
这也表明数组array元素个数为5。
main()
{
inti,array[]={1,3,5,7,9,11};
for(i=0;i<5;i++)printf(%d,array[i]);
printf(\n);
}
最终结果为13579
四、字符数组
整数和浮点数数组很好理解,在一维数组中,还有一类字符型数组。
chararray[5]={'H','E','L','L','O'};
对于单个字符,必须要用单引号括起来。
又由于字符和整型是等价的,所以上面的字符型数组也可以这样表示:
chararray[5]={72,69,76,76,79};/*用对应的ASCII码*/
举一个例子:
main()
{
inti;
chararray[5]={'H','E','L','L','O'};
for(i=0;i<5;i++)printf(%d,array[i]);
printf(\n);
}
最终的输出结果为7269767679
但是字符型数组和整型数组也有不同的地方,看下面的:
chararray[]=”HELLO”;
如果我们能看到内部的话,实际上编译器是这样处理的:
chararray[]={'H','E','L','L','O','\0'};
看上面最后一个字符'\0',它是一个字符常量,TurboC编译器总是给字符型数组的最后自动加上一个\0,这是字符的结束标志。
所以虽然HELLO只有5个字符,但存入到数组的个数却是6个。
但是,数组的长度仍然是5。
inti;
i=strlen(array);/*求字符串的长度,在string.h里面*/
可以看出i仍然是5,表明最后的'\0'没有算。
#includestring.h
main()
{
inti,j;
chararray[]=094387fdhgkdladhladaskdh;
j=strlen(array);
for(i=0;i
printf(\n);
}
其实我们可以根据判断'\0'来输出字符串,看下面的:
main()
{
inti;
chararray[]=094387fdhgkdladhladaskdh;
for(i=0;array[i]!
='\0';i++)printf(%c,array[i]);
printf(\n);
}
举几个例子:
1.输入10个整数存入数组中,然后把它们从小到大排列并放在同一数组中。
(思路:
先找出最小的,放在第一个位置,为了防止把原先的数覆盖掉,可以把原先的第一个数和最小数的位置互换)。
main()
{
intarray[10];
inti,j,min,stmp;
for(i=0;i<10;i++)scanf(%d,&array[i]);
for(i=0;i<9;i++)
{
min=array[i];
for(j=i+1;j<10;j++)
if(min>array[j])/*里面的4行语句很重要*/
{
min=array[j];
stmp=array[i];
array[i]=array[j];
array[j]=stmp;
}
}
for(i=0;i<10;i++)printf(%d,array[i]);
printf(\n);
}
分析:
先让第一个值作为基准,如果后面有比它小的,那么就把这两个数互换一下,同时把基准换成小的值。
两个数互换应该这样(stmp=a;a=b;b=stmp;),而不是(a=b;b=a;),想想这是为什么?
必须要用一个变量作为桥梁。
这种一个一个的把最小的放在前面的排序方法,我们形象的叫做冒泡法。
函数
函数中包含了程序的可执行代码。
每个C程序的入口和出口都位于函数main()之中。
main()函数可以调用其他函数,这些函数执行完毕后程序的控制又返回到main()函数中,main()函数不能被别的函数所调用。
通常我们把这些被调用的函数称为下层(lower-level)函数。
函数调用发生时,立即执行被调用的函数,而调用者则进入等待状态,直到被调用函数执行完毕。
函数可以有参数和返回值。
程序员一般把函数当作“黑箱”处理,并不关心它内部的实现细节。
当然程序员也可以自己开发函数库。
说明一点,函数这一节很重要,可以说一个程序的优劣集中体现在函数上。
如果函数使用的恰当,可以让程序看起来有条理,容易看懂。
如果函数使用的乱七八糟,或者是没有使用函数,程序就会显得很乱,不仅让别人无法查看,就连自己也容易晕头转向。
可以这样说,如果超过100行的程序中没有使用函数,那么这个程序一定很罗嗦
一、函数的定义
一个函数包括函数头和语句体两部分。
函数头由下列三不分组成:
函数返回值类型
函数名
参数表
一个完整的函数应该是这样的:
函数返回值类型函数名(参数表)
{
语句体;
}
函数返回值类型可以是前面说到的某个数据类型、或者是某个数据类型的指针、指向结构的指针、指向数组的指针。
函数名在程序中必须是唯一的,它也遵循标识符命名规则。
参数表可以没有也可以有多个,在函数调用的时候,实际参数将被拷贝到这些变量中。
语句体包括局部变量的声明和可执行代码。
我们在前面其实已经接触过函数了,如abs(),sqrt(),我们并不知道它的内部是什么,我们只要会使用它即可。
二、函数的声明和调用
为了调用一个函数,必须事先声明该函数的返回值类型和参数类型,这和使用变量的道理是一样的(有一种可以例外,就是函数的定义在调用之前,下面再讲述)。
看一个简单的例子:
voida();/*函数声明*/
main()
{
a();/*函数调用*/
}
voida()/*函数定义*/
{
intnum;
scanf(%d,&num);
printf(%d\n,num);
}
在main()的前面声明了一个函数,函数类型是void型,函数名为a,无参数。
然后在main()函数里面调用这个函数,该函数的作用很简单,就是输入一个整数然后再显示它。
在调用函数之前声明了该函数其实它和下面这个程序的功能是一样的:
main()
{
intnum;
scanf(%d,&num);
printf(%d\n,num);
}
可以看出,实际上就是把a()函数里面的所有内容直接搬到main()函数里面(注意,这句话不是绝对的。
)
我们前面已经说了,当定义在调用之前时,可以不声明函数。
所以上面的程序和下面这个也是等价的:
voida()
{
intnum;
scanf(%d,&num);
printf(%d\n,num);
}
main()
{
a();
}
因为定义在调用之前,所以可以不声明函数,这是因为编译器在编译的时候,已经发现a是一个函数名,是无返回值类型无参数的函数了。
那么很多人也许就会想,那我们何必还要声明这一步呢?
我们只要把所有的函数的定义都放在前面不就可以了吗?
这种想法是不可取的,一个好的程序员总是在程序的开头声明所有用到的函数和变量,这是为了以后好检查。
前面说了,在调用之前,必须先声明函数,所以下面的做法也是正确的。
main()
{
voida();
a();
}
voida()
{
intnum;
scanf(%d,&num);
printf(%d\n,num);
}
一般来说,比较好的程序书写顺序是,先声明函数,然后写主函数,然后再写那些自定义的函数。
main()函数可以调用别的函数,我们自己定义的函数可以再调用其他函数。
看下面的例子:
voida();
voidb();
main()
{
a();
}
voida()
{
b();
}
voidb()
{
intnum;
scanf(%d,&num);
printf(%d\n,num);
}
main()函数先调用a()函数,而a()函数又调用b()函数。
在C语言里,对调用函数的层数没有严格的限制,我们可以往下调用100层、1000层,但是在这里我们并不提倡调用的层数太多(除非是递归),因为层数太多,对以后的检查有一些干扰,函数调过来调过去,容易让自己都晕头转向。
一、函数参数传递
1.形式参数和实际参数
函数的调用值把一些表达式作为参数传递给函数。
函数定义中的参数是形式参数,函数的调用者提供给函数的参数叫实际参数。
在函数调用之前,实际参数的值将被拷贝到这些形式参数中。
2.参数传递
先看一个例子:
voida(int);/*注意函数声明的形式*/
main()
{
intnum;
scanf(%d,&num);
a(num);/*注意调用形式*/
}
voida(intnum_back)/*注意定义形式*/
{
printf(%d\n,num_back);
}
在主函数中,先定义一个变量,然后输入一个值,在a()这个函数中输出。
当程序运行a(num);这一步时,把num的值赋值给num_back,在运行程序过程中,把实际参数的值传给形式参数,这就是函数参数的传递。
形参和实参可能不只一个,如果多于一个时,函数声明、调用、定义的形式都要一一对应,不仅个数要对应,参数的数据类型也要对应。
voida(int,float);
main()
{
intnum1;
floatnum2;
scanf(%d,&num1);
scanf(%f,&num2);
a(num1,num2);
}
voida(intnum1_back,floatnum2_back)
{
printf(%d,%f\n,num1_back,num2_back);
}
上面的例子中,函数有两个参数,一个是整型,一个是浮点型,那么在声明、调用、定义的时候,不仅个数要一样,类型也要对应。
如果不对应,有可能使的编译错误,即使没错误,也有可能让数据传递过程中出现错误。
再看一个例子:
voida(int);
main()
{
intnum;
scanf(%d,&num);
a(num);
}
voida(intnum)
{
printf(%d\n,num);
}
看上面的例子,形式参数和实际参数的标识符都是num,程序把实际参数num的值传递给形式参数num。
有些人可能就不明白了,既然两个都是num,为什么还要传递呢?
干脆这样不就行了吗:
voida();
main()
{
intnum;
scanf(%d,&num);
a();
}
voida()
{
printf(%d\n,num);
}
其实不然,这就要涉及到标识符作用域的问题。
作用域的意思就是说,哪些变量在哪些范围内有效。
一个标识符在一个语句块中声明,那么这个标识符仅在当前和更低的语句块中可见,在函数外部的其实地方不可见,其他地方同名的标识符不受影响,后面我们会系统讲解作用域的问题。
在这儿你就要知道两个同名的变量在不同的函数中是互不干扰的。
前面将的都是变量与变量之间的值传递,其实函数也可以传递数组之间的值。
看下面的例子:
voida(int[]);
main()
{
intarray[5],i;
for(i=0;i<5;i++)scanf(%d,&array[i]);
a(array);
}
voida(intarray[])
{
inti;
for(i=0;i<5;i++)printf(%d\t,array[i]);
printf(\n);
}
这就是数组之间的值传递。
注意他们的声明和定义形式,和变量参数传递有什么区别?
有了后面的[]就表明传递的是一个数组。
其中在定义的时候,也可以写成voida(intarray[5]);想想,如果我们写成了intarray[4]会有什么情况发生?
二、函数值的返回
其实我们也可以把函数当作一个变量来看,既然是变量,那一定也可以有类型。
现在要求在main()函数里输入一个整数作为正方形的边长,在子函数里求正方形的面积,然后再在主函数里输出这个面积。
我们前面的程序都是在子函数里输出的,现在要求在主函数里输出,这就需要把算好的值返回回来。
先看例子:
inta(int);/*声明函数*/
main()
{
intnum,area;
scanf(%d,&num);
area=a(num);/*调用时的形式*/
printf(%d,area);
}
inta(intnum)
{
intarea_back;
area_back=num*num;
returnarea_back;/*返回一个值*/
}
和前面的程序有几点不同:
(1).声明函数类型时,不是void,而是int。
这是由于最后要求的面积是整型的,所以声明函数的返回值类型是整型。
(2).return语句它的意思就是返回一个值。
在C语言中,return一定是在函数的最后一行。
(3).调用函数的时候,由于函数有一个返回值,所以必须要用变量接受这个返回值(不是绝对的),如果我们不用一个变量接受这个值,函数还照样返回,但是返回的这个值没有使用。
上面的例子运行过程是这样的,先把实参的值传递给形参,然后在子函数里计算面积得到area_back,然后返回这个面积到主函数,也就是把area_back赋值给area,最后输出。
前面说了,返回值有时不一定非要用一个变量来接受,我们可以把上面的程序简化为:
inta(int);
main()
{
intnum;
scanf(%d,&num);
printf(%d,a(num));/*函数调用放在这儿*/
}
inta(intnum)
{
intarea_back;
area_back=num*num;
returnarea_back;
}
对于函数而言,一个函数只能返回一个值,如果想返回一组数值,就要使用数组或者结构或者指针。
其实对于这些,还是返回一个值,只是这个值是一个地址而已。
但是对于数组的返回有和变量不同,因为数组和地址是联系在一起的。
看一个例子:
voida(int[]);
main()
{
intarray[5]={1,2,3,4,5},i;
a(array);
for(i=0;i<5;i++)printf(%d,array[i]);
}
voida(intarray[])
{
inti;
for(i=0;i<5;i++)array[i]++;
}
看看这个程序,好象函数没有返回值,但是函数的功能的确实现了,在主函数当中输出的值的确都各加了1上来。
这就是因为数组和变量不同的缘故,此处传递的是指针地址。
下面看一个实际例子,加深对函数的理解:
用函数实现,判断一个整数是不是素数?
在主函数里输入输出,子函数里判断。
#includemath.h
intjudge(int);
main()
{
intnum,result;
scanf(%d,&num);
result=judge(num);
if(result==1)printf(yes\n);
elseprintf(no\n);
}
judge(intnum)
{
inti,flag=1;
for(i=2;i<=sqrt(num);i++)
if(num%i==0)
{
flag=0;
break;
}
returnflag;
}
可以看出,函数的功能就是为了让程序看起来有条理,一个函数实现一个特定的功能。
如果我们还和以前那样,把所有代码都放在main()函数,好象程序就显的臃肿了。
而且函数有一个显著的好处就是很方便的使用。