数组与函数

数组

数据可以存放在变量里,每一个变量有一个名字,有一个类型,还有它的生存空间。如果我们需要保存一些相同类型、相似含义、相同生存空间的数据,我们可以用数组来保存这些数据,而不是用很多个独立的变量。数组是长度固定的数据结构,用来存放指定的类型的数据。一个数组里可以有很多个数据,所有的数据的类型都是相同的。

初始数组:

如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?

#include<stdio.h>
int main()
{
int x;
double sum = 0;
int cnt = 0;
int number[100]; //定义一个数组,一个有100int的数组
scanf("%d", &x);

while(x!=-1){
number[cnt]=x; //对数组中的元素赋值
/*
{
int i;
printf("%d\t",cnt);    // \t相当于tab键
for(i=0; i<=cnt; i++){
printf("%d\t", number[i]); 
}
printf("\n");
}
*/
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]);
}
}
}

return 0;
}

}

这个程序存在安全隐患,没有判断cnt会不会超过number[100]

定义数组:

<类型>变量名称[元素数量]

如:

int grades[100]

double weight[20]

元素数量必须是整数

C99之前,元素数量必须是编译时刻确定的字面常量,不能包含变量,不管该变量有没有初始化

数组是一种容器,特点是:

其中所有元素具有相同数据类型;

一旦创建,不能改变大小;

数组中的元素在内存中是连续依次排列的

 

一个int的数组,10个单元,a[0],a[1],a[2],a[3],a[4]...a[9]

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]

 

 

每个单元就是一个int的变量;

可以出现在赋值符号左边或者右边,左边的叫左值,右边的叫右值;

a[2] = a[1] = 6 : 读取a[1]的值,加上6,写给a[2]

 

使用数组时,放在[ ]里的数字叫下标或索引,下标从0开始计数;

有效的下标范围:

编译器和运行环境都不会检查数组下标是否会越界,无论是做读还是写;

一旦程序运行,越界的数组访问可能造成问题,严重的可能造成程序崩溃   

segmentation fault

所以程序员有责任来保证使用有效的数组下标[0, 数组大小-1]

可以先让用户输入有多少数字要计算,再定义数组,可以用C99的新功能实现。C99允许用变量指明数组长度

int x;
double sum = 0;
int cnt;

printf("请输入数字的数量:");

scanf("%d" ,&cnt);

if(cnt>0){

int number[cnt];
scanf("%d", &x);

while(x!=-1){
number[cnt]=x; //对数组中的元素赋值
/*
{
int i;
printf("%d\t",cnt);    // \t相当于tab键
for(i=0; i<=cnt; i++){
printf("%d\t", number[i]); 
}
printf("\n");
}
*/
sum+=x;
cnt ++;
scanf("%d", &x);
}

长度为0的数组可以存在,但没用a[0]

字符可以做下标:

如a['A']

字符以ASCII码存储,ASCII对应数字

 

数组例子:统计个数:

写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-表示结束

 

#include<stdio.h>
int main()
{
const int number = 10; //用变量number指明数组大小,C99才允许
int count[number]; //定义数组
int x;
int i;

for(i=0; i<number; i++){ //初始化数组,用循环遍历数组,给每一个元素赋值
count[i] = 0;
}

scanf("%d", &x);
while(x!=-1){
if(x>=0&&x<=9){
count[x]++; //数组参与运算
}
scanf("%d", &x);
}

for(i=0; i<number; i++){ //用for循环遍历数组,输出每一个元素的值
printf("%d:%d\n", i ,count[i]);
}


return 0;
}

 

函数的定义与使用

初见函数:

求和:求出1到10、20到30、35到45的三个和

#include<stdio.h>

void sum(int begin, int end){
int i;
int sum;
for(i=begin; i<=end; i++){
sum += i;
}
printf("%d到%d的和是%d\n",begin, end, sum);
}


int main()
{
sum(1,10);
sum(20,30);
sum(35,45);

return 0;
}

使用函数可以让程序结构变得更简洁直观,提高代码的利用率,减少同类代码重复出现,可以重复调用。

函数的定义和使用:

函数是一块代码,接受零个或多个参数,做一件事情,并返回零个或一个值

调用函数:

格式: 函数名(参数值);

() 起到了表示函数调用的重要作用,即使没有参数也需要()

如果有参数,则需要给出正确的参数数量和顺序

这些值会被按照顺序依次用来初始化函数中的参数

函数返回:

函数知道每一次是哪里调用它,会返回到正确的地方(会返回到调用它的下面那个地方)

 

从函数中返回值:

return 停止函数的运行,并送回一个值

 return;   或   return 表达式;

一个函数里可以出现多个return语句,但违背“单一出口原则”

int max(int a, int b)  // 定义一个函数max

{

  int ret;

  if(a>b){

    ret = a;

}else{

    ret = b;

}

    return ret;

}

 

int a, b, c;

a = 5;

b = 6;

c = max(10,12);  //可以赋值给变量

c = max(a,b);

c = max(c,23);

c = max(max(c,a),5);  //可以再传递给函数

printf("%d\n", max(a,b));

max(12,13);  //也可以丢弃

 

没有返回值的函数:

void 函数名(参数表)

不能使用带值的return,也可以没有return

调用的时候不能做返回值的赋值(c = sum(12,13))

void sum(int begin, int end)

{

  int i;

  int sum;

  for(i=begin; i<=end; i++){

    sum += i;

}

  printf("%d到%d的和是%d\n", begin, emd, sum);

}

 如果函数有返回值,就必须用带值的return

 

函数原型:

函数先后关系:

void sum(int begin, int end)

{

  int i;

  int sum;

  for(i=begin; i<=end; i++){

    sum += i;

}

  printf("%d到%d的和是%d\n", begin, emd, sum);

}

 

int main()

{

  sum(1,10);

  sum(20,30);

  sum(35,45);

  

  return 0;

}

像这样把定义函数写在上面,是因为:

C的编译器自上而下顺序分析代码

看到sum(1,10)时,编译器要知道sum()要几个参数,每个参数什么类型,返回什么类型

这样才能检查对sum的调用是否正确

 

void sum(int begin, int end);   //声明

int main()

{

  sum(1,10);

  sum(20,30);

  sum(35,45);

  

  return 0;

}

void sum(int begin, int end)  //定义

{

  int i;

  int sum;

  for(i=begin; i<=end; i++){

    sum += i;

}

  printf("%d到%d的和是%d\n", begin, emd, sum);

}

 函数原型:

函数头以分号 ";" 结尾,就构成函数原型;

函数原型目的是告诉编译器这个函数长什么样 

名称

参数(数量及类型)

返回类型

旧标准习惯把函数原型写在调用它的函数里面,现在一般写在调用它的函数前面

原型里可以不写参数名字,但是为了用户可读性,建议写上

 

参数传递

调用函数:

如果函数有参数,调用函数时必须传给它数量、类型正确的值;

可以传递给函数的值是表达式的结果,包括:

字面量

变量

函数的返回值

计算的结果

int a, b, c;

a = 5;

b = 6;

c = max(10, 12);

c= max(a, b);

c = max(c, 23);

c = max(max(23, 45), a);

c= = max(23 + 45, b);

 

调用函数时给的值与参数类型不匹配,编译器总是悄悄把类型转换好,这是C语言的漏洞,后续的java/C++在这方面很严格

 

C语言在调用函数时,只能传递值给函数

void swap(int a, int b);

int main(){

int a = 5;

int b = 6;

 

swap(a, b);

printf("a=%d b=%d\n", a, b);

return 0;

}

 

void swap(int a, int b){

int t = a;

a = b;

b = t;

}

这样不能交换a,b的值

传值:

每个函数有自己的变量空间,参数也位于这个独立的变量空间中,和其他函数没有关系

过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值叫做“实际参数”

我们认为,形式参数和实际参数是参数和值的关系

 

本地变量:

函数的每次运行就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,叫做“本地变量”

 定义在函数内部的变量就是本地变量

参数也是本地变量

 

本地变量的规则:

  本地变量是定义在块内的;

  可以是定义在函数的块内;

  也可以是定义在语句的块内;

  甚至随便拉一对大括号{}来定义变量;

程序运行进入这个块之前,其中的变量不存在,离开这个快,变量就消失了

块外面定义的变量在里面仍然有效

块里面定义了和外面同名的变量则掩盖了外面的

不能在一个块里定义同名的变量

本地变量不会被默认初始化

参数在进入函数时被初始化了

 

没有参数时:

void f(void)  明确表示这个函数不接收任何参数

void f() 不代表没有参数,而表示函数的参数表未知,不知道有没有参数,不知道参数类型,不知道参数数量

 

逗号运算符:

f(a,b) 调用函数时,"," 是标点符号

f((a,b)) "," 是运算符

 

C语言不允许函数嵌套定义,不能在函数里定义函数

 

 二维数组:

int a[3][5];

通常理解为a是一个3行5列的矩阵

 

二维数组可被看作一种特殊的一维数组,即它的元素为一维数组。比如“int a[3][4];”可以看作有三个元素,每个元素都为一个长度为 4 的一维数组。而且 a[0]、a[2]、a[3] 分别是这三个一维数组的数组名。

在 C 语言中,二维数组中元素排列的顺序是按行存放的,即在内存中先顺序存放第一行的元素,再存放第二行的元素,这样依次存放。

 

二维数组的遍历:

for(i=0; i<3; i++){

  for(j=0; j<5; j++){

    a[i][j] = i*j;

}

 a[i][j] 是一个int,表示第i行,第j列上的单元

a[i,j] 是运算,结果是j

 

二维数组的初始化:

1.分行给二维数组赋初值,比如:

int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12},};   最后一个逗号可以存在

2.也可以将所有数据写在一个花括号内,按数组排列的顺序对各元素赋初值。比如:

int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

但第1种方法更好,一行对一行,界限清楚。第 2 种方法如果数据多,写成一大片,容易遗漏,也不易检查。

3.也可以只对部分元素赋初值。比如:

int a[3][4] = {{1, 2}, {5}, {9}};

它的作用是对第一行的前两个元素赋值、第二行和第三行的第一个元素赋值。其余元素自动为 0。初始化后数组各元素为:

 

4. 如果在定义数组时就对全部元素赋初值,即完全初始化,则第一维的长度可以不指定(由编译器来数),但第二维的长度不能省。比如:

int a[][4{{1234}{5678}{9101112},}

但这种省略的写法几乎不用,因为可读性差。

5. int a[3][4]={0};

二维数组“清零”,里面每一个元素都是零。

 

二维数组输出

在讲述一维数组的时候说过,“数组的元素只能一个一个输出”,二维数组也不例外。在一维数组中

是用一个 for 循环进行输出,而二维数组元素的输出要使用两个 for 循环嵌套。

# include <stdio.h>
int main(void)
{
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int i; //行循环变量
int j; //列循环变量
for (i=0; i<3; ++i)
{
for (j=0; j<4; ++j)
{
printf("%-2d\x20", a[i][j]);    //“%-2d”,其中“-”表示左对齐,如果不写“-”则默认表示右对齐;“2”表示这个元素输出时占两个空格的空间,所以连同后面的 \x20 则每个元     }                                          //素 输出时都占三个空格的空间。       
printf("\n");
}
return 0;
}

posted @ 2021-10-31 21:15  泰勒斯儿童营养牙膏  阅读(67)  评论(0编辑  收藏  举报