6.数组与函数
前面的都是小闹的东西
如果之前有人用之前的知识和你装X,那多半是个sb
下面,我们正式进入"基础的基础"的大门
数组
数组初步介绍
当我们要存储同一类型的多个变量时,我们使用数组
可以把数组看成一个篮子,相同类型的变量全部放进去
<数组类型> <名字> [大小]
int GroupA [100];
<类型> <名称> [元素数量] 这里的类型是指数组内部存储的数据的类型
int grades[100];
doubke weight[20];
数组通过下标来访问对应位置的变量,下标从0开始
我们上面定义了大小为100的数组,则下标范围是0-99
另外,元素数量必须是整数
在C99之前:元素数量必须是编译时刻确定的字面量 (不能是变量、程序运行过程中动态产生的数字)
数组定义实例:
#include <stdio.h>
int main(){
//写一个程序,计算平均数并输出大于平均数的数字
int x ;
double sum = 0;
int cnt = 0 ;
int number [100]; //定义一个数组,其大小为100个
//int 名字A [大小X] 表示这个数组A里面最多可以放 X个int
scanf("%d",&x);
while (x! = -1){
number [cnt] = x ; //让数组上cnt位置的那个单元=x ;而cnt是递增的,所以x分别会在1,2,3....位置上存放
//对数组中的元素进行赋值
sum += x ;
cnt ++;
scanf ("%d",&x);
}
if (cnt > 0 ){
printf("%f \n",sum/cnt);
int i ;
for (i = 0 ; i<cnt;i++){ //进行数组数字的输出。遍历数组
//遍历数组:浏览整个数组
if (number[i]>sum/cnt){//使用数组中的元素
printf("%d \n",number[i]);
//当数组i位置的数符合条件,输出它
}
}
}
;return 0 ;
}
1.数组中的元素具有相同的数据类型
2.一旦创建,数组不能改变其大小
3.使用数组时在[]中的数字是下标/索引,下标从0开始计数
4.!!无论是编译器还是运行环境,都不会去检查下标是否越界,无论你在读还是写(segmentation fault)
5.数组中的元素在内存中是连续依次排列的
6.可以创建长度为0的数组,但是没什么锤子用
二维数组
我们把数放到一个方形区域里来,想象一个个方格!
二维数组的定义和使用同一维,不过它有两个中括号,代表行列,先行后列
遍历二维数组将利用嵌套循环
#include <stdio.h>
int main(void){
int a [3][5] ; //定义了一个三行五列的矩阵,定义基本都是先行(y数)后列(x数)的
int therow = 3 ;
int thecol = 5 ;
//二维数组的遍历
for(int i=0;i< therow ;i++){
for(int j=0;j<thecol;j++){
a [i][j] = (i+1)*(j+1) ; //数组名字+数组下标,就可以表示这是一个普通的变量了,可以在一些地方直接使用
printf("%d \n",a[i][j]);
}
}
//二维数组初始化
int b [][5] = {
{1,2,3,4,5},
{6,2,4,5,7},
};
/*
列数是不可以省略的,函数可以让编译器自己整
每个{}之间使用大括号隔开,最后一个的逗号存不存在无所谓,如果省略,表示“补零”
*/
; return 0 ;
}
函数
重头戏之一来了
定义函数
<返回值> <函数名字>(参数表){
函数执行,如果有返回值的话要return
}
比如我们定义一个加法的函数sum
void sum(int begin,int end){
int i ;
int sum = 0 ;
for(i=begin ; i<=end ;i++){
sum+= i;
}
printf("%d 到 %d的和是%d \n",begin,end,sum);
}
定义的函数要写在用它的地方之前才行,比如我想在主函数中使用sum函数,就要先写它
实例:
#include <stdio.h>
void sum(int begin,int end){
int i ;
int sum = 0 ;
for(i=begin ; i<=end ;i++){
sum+= i;
}
printf("%d 到 %d的和是%d \n",begin,end,sum);
}
/*
在这里,我们在主函数之前定义了一个自己的函数sum
*/
int main (void){
sum(1,10);//在这里,我们运用了自己的sum函数
sum(20,30);
sum(35,45);
; return 0 ;
}
显然,这样子并不美观,那么我们可以先用原型声明来“占位”
原型声明
我们可以现在最开始声明我们这个函数的基本内容,而不写执行代码
然后在主函数结束后,再在后面补上对应的执行代码
比如:
#include <stdio.h>
//C的编译器是由上而下来分析代码的,和eclipse不一样,这导致你要用的函数需要在前面先写出来
//这个在各编译器是不一样的,不过还是建议用的先写在前面
//但是可以把函数头加上分号放在前面,其它部分放在后面,这样就可以了。这个做法叫做 原型声明
void sum (int a , int b); //声明
int main ()
{
sum(1,10);
; return 0 ;
}
void sum (int a, int b){ //定义
int i ;
int sum = 0 ;
for (i =a ; i <=b ; i ++){
sum +=1 ;
}
}
//另外,原型声明里面可以只给参数类型,不给参数名字。因为原型声明的意义就是让编译器知道有这个东西
return
返回返回值的操作是必不可少的,而且要和你所声明的返回值对应
当然,如果你声明返回值是void(无),那就不用return了
#include <stdio.h> 、
//当使用void类型的函数时,函数是没有返回值的,也就是可以没有return
int max (int a,int b)
{//返回值是int类型的函数max
int ret ;
if(a>b){
ret = a ;
}else {
ret = b ;
}
return ret ; //返回一个ret的数值,另外在一个函数里面可以有多个return语句,而且return不一定要在尾(不过这不是单一出口,习惯不好)
}
int main () {
/* return
1.return 会停止函数的运行,并且返回一个值
2.写法 return <值> ; 或者 return[表达式] ;
*/
int a,b,c ;
c = max(12,10);
printf("%d \n ",c);
return 0 ;
}
参数传递和本地变量
说实话觉得这里有一点点点点深入了,不过还是写一下好了
函数每次运行都产生独立的变量空间
在这个空间中的变量是它这次运行独有的,称为本地变量
定义在函数内部的变量就是本地变量/局部变量
参数也是本地变量/局部变量
生存期:变量从出现到消失的时间
作用域:该变量可以起作用的范围
对于本地变量,二者属于一个范围,那就是大括号内,我们把他叫块
1.本地变量是定义在块内的,可以是函数块内,也可以是语句块内
2.程序运行到某个块之前,该块其中的变量是不存在的,离开了之后,其中的变量也会消失
块外面定义的变量在块里面是依然生效的
3.如果块里面定义了和外面同名的变量,那么块内的变量会覆盖外面的 (Java不能这么干)
4.一个块中是不能定义同名变量的
5.本地变量不会被默认初始化,参数在进入函数的时候被初始化了
#include <stdio.h>
//以下内容请利用debug辅助理解
//这样的代码可以完成a、b数值的互换吗 答案是不可以
void swap(int a , int b);
int main(void){
int a = 5 ;
int b = 16 ;
swap(a,b);
printf("a = %d b = %d",a,b);
return 0 ;
}
void swap(int a,int b){
//虽然在这里的参数是a b ,刚才传进来的数值也是a b ,但实质上二者是完全不同的东西
//swap函数中,只是把a的值5,以及把b的值16 给到了swap的形式参数a b 里面,此ab非彼ab
int t = a ;
a = b ;
b = t ;
}
其它
1.void f(void) 表示f函数没有参数
void f() 表示f函数的参数表未知(传统C)
2.逗号在圆括号内算作标点符号,而不是运算符
f(a,b) 传入a,b
f((a,b)) 传入(a,b),此时我们要的是(a,b)而非a,b。因此逗号会被当作运算符被使用
3.C语言不允许在函数里面定义函数
C啊,你是那么牛逼那么基础,又是那么捞
#include <stdio.h>
void swap(); //原型声明
int main(void){
int a = 5 ;
int b = 6 ;
//原型声明的时候外面没有指定参数,在这里外面尝试传入两个int
swap(a,b); //而事实上,我们的函数会对两个double进行处理,因此结果会出错(但是运行是正常的)
printf("a = %d , b = %d \n",a,b);
;return 0 ;
}
//注意这里给的是double
void swap (double a , double b){
int t = a ;
printf("IN SWAP,a = %f , b = %f \n",a,b);
a = b ;
b = t ;
}
数组运算
因为有那么点承上启下的意思,所以放在这里了
集成初始化时的定位:
1.用[n]在初始化数据中给出定位
2.没有定位的数据会接在前面的位置后面
3.其它位置的数值同前文,补为0
4.也可以不给出数组的大小,让编译器自己算
5.这种做法比较适合初始数据稀疏的数组
#include <stdio.h>
int main (void){
int a [] = {1,2,3,5,6,7}; //集成初始化
for(int i=0;i<7;i++){ //这里提一句,很多时候习惯使用<某个阙值
printf("%d \t",a[i]);
}
//如果一开始就规定了数组的大小,但是没有填充完数组,那么会把剩余未规定的部分全部初始化为0
//int a [10] ={2};
//int b[10] = {[0] = 2 , [2] =3 ,6 ,}; 只能在C99下使用
//sizeof可以给出整个数组所占据的内容的大小,单位为字节。在整形数组中,一个单位4字节
printf("\n %d \n",sizeof(a));
printf("\n %d \n",sizeof(a[0]));
printf("\n 所以a的大小就是 %d \n",sizeof(a) / sizeof(a[0]));
//sizeof(a) / sizeof(a[0] 起到了Java中类似与length系列的作用
; return 0 ;
}
数组不能直接被赋值,像 int b [] = a 是不行的,我们只能使用遍历来完成这件事
数组作为函数参数时,我们习惯于用另一个参数来传入数组的大小————为什么呢,继续看下一篇吧