c语言的函数和数组

376 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

一维数组

初识数组

#include <stdio.h>
int main(){
	int x;
	double sum =0;
	int cnt = 0;
	int number[100];//定义数组
	scanf("%d",&x);
	while(x!=-1){
		number[cnt]=x;//对数组中的元素进行赋值
		sum+=x;
		cnt ++;
		scanf("%d",&x);
	}
	if(cnt>0){
		int i;
		double average =sum/cnt;
		
        
        //遍历数组
        for(i=0;i<cnt;i++){
			if (number[i]>average){
				printf("%d\t",number[i]);//使用数组中的元素
			}
		}
	}
}

定义数组

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

例如:int grades[100];

​ int weight[20];

  • 元素的数量必须是整数型
  • C99之前:元素数量必须是编译时刻确定的字面量

那么问题来了怎么理解数组呢?

  • 是一种容器(放很多东西的地方),特点是
    • 其中所有元素具有相同的数据类型
    • 一旦创建,不能改变大小
    • 数组中的元素在内存中是连续一次排列的
    • 数组中的第一个元素是从0开始的

int a[10]

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
  • 在赋值的左边叫左值

数组的单元

  • 数组的每一个单元就是数组类型的一个变量
  • 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数

有效的下标范围

  • 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
  • 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃(segmentation fault)
  • 保证使用有效下标值:[0,(数组的大小-1)]

数组越界:用户输入到数组的个数数组定义的个数 后果:程序会在此处崩溃

解决方案:1.在用户读数时进行计数,如果读入的数超出了数组,那么就不再读入

​ 2.让用户先输入来决定数组的大小,然后再来记录输入

恶趣味:可以创建一个长度为0的数组,即 int a[0],但好像并没有什么卵用😋

统计个数

#include <stdio.h>
int main(void)
{
    const int number =10 //数组的大小
	int x;
	int count[number]; //定义数组
    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++){
		print("%d:%d",i,count[i]) //遍历数组输出
	}
	return 0;
}


数组运算

  • 在一组给定的数据中,如何找出某个数据是否存在?

数组的集成初始化

int a [] = {1,2,3,4,5,6,7,8,9,0};
imt a [4] ={[0] =2, [2] = 3,6,5,7};
  • 用[n]在初始化数据中给出定位
  • 没有定位的数据接在前面的位置后面,即6会被放在第四位([3])
  • 其他位置的值补零
  • 也可以不给出数组大小,让编译器算
  • 特别适合初始数据稀疏的数组

数组的大小

  • sizeof给出整个数组所占据的内容的大小,单位是字节

  • sizeof(a)/sizeof(a[0])用来表示数组有多少个单元

  • sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组单元的个数

  • 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码

数组的赋值

数组不能直接赋值给另一个变量

  • 数组变量本身不能被赋值
  • 要把一个数组的所有元素交给另一个数组,必须采用遍历
for(i=0 ;i<length; i++){
    b[i]=a[i];
}
  • 通常都使用for循环,让循环变量i从0到小于数组的长度,这样循环体内最大的i正好是数组最大的有效下标

  • 常见错误

循环结束条件是<=数组长度,或;

离开循环后,继续用i的值来做数组元素的下标!

  • 数组作为函数的参数是:
    • 不能在[]中给出数组大小
    • 不能再利用sizeof来计算数组的元素个数

二维数组

二维数组

int a[3][5];

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

a [0] [0]a [0] [1]a [0] [4]a [0] [2]a [0] [3]
a [1] [0]a [1] [1]a [1] [4]a [1] [2]a [1] [3]
a [2] [0]a [2] [1]a [2] [4]a [2] [2]a [2] [3]

二位数组的遍历

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]是什么?———————— =a[j]

二维数组的初始化

int a[][5]={
	{0,1,2,3,4,},
	{2,3,4,5,6},
};
  • 列数是必须给出的,行数可以由编译器来数
  • 每一行{},逗号隔开
  • 最后的逗号可以存在(可以装逼)
  • 如果省略,表示补零
  • 也可以用定位(c99)

最外层大括号可以不要

c语言的函数

什么是函数

是一个能接收零个或多个参数并返回零个或一个值的代码块

可以把这里的函数类比为数学中的函数

定义函数

void sum(int a,int b)  //函数头
{
	int 1;						   //函数体
	int sum=0;
	for(i=a;i<=b;i++){
   		sum+=i;
	}
	printf("%d到%d的和是%d\n",a,b,sum);//函数体
}

void:返回类型 ,表示空类型的函数,不需要返回,其他所有函数类型都要return来返回

sum:函数名,在引用该函数时需要使用

函数体:函数的主题部分,就是决定函数究竟是干什么用的

(int a,int b):参数表(数据类型+名字)【可以不用写名字,有数据类型即可】

函数的调用

函数名(参数值);

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

如果有参数,给出正确的数量和顺序,这些值会按照正确顺序依次用来初始化函数中的参数

函数的返回值

//素数求和
int isprime(int i)
{
	int ret = 1;
	int k;
	for(k=2;k<i-1;k++){
		if(i%k==0){
			ret=0;
			break;
		}
	}
	return ret;
}
int max(int a,int b){                         | int max(int a,int b){
    int ret;                                  | 	if(a>b){
    if(a>b){                                  |		   return a;
        ret=a;                                |		}else{
    }else{                                    |		   return b;
        ret=b;                                |      }
    }                                         | }      
    return ret;                               |
}

上述两行代码,你觉得那个更好???

return

  • 停止函数执行,并送回一个值
  • return;
  • return 表达式;
  • 一个函数语句可以出现多个return语句

注意,一个函数中尽量不要有多个return,即尽量保持单一出口。这样做是为了方便修改代码

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",max(a,b));
max(12,13)
  • 可以赋值给变量
  • 可以再传递给函数
  • 可以丢弃

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

函数的先后关系

1.函数先后关系

与脚本语言相同,c的编译器是自上而下分析代码的。即函数的先后也是自上而下的

2.函数声明(函数原型)

#include <stdio.h>
void sum(int a,int b)  
{
	int 1;						   
	int sum=0;
	for(i=a;i<=b;i++){
   		sum+=i;
	}
	printf("%d到%d的和是%d\n",a,b,sum);
}
int main()
{
    sum(1,10);
    sum(20,30);
    return 0;
}
#include <stdio.h>
void sum(int a,int b)  //函数声明
int main()
{
    sum(1,10);
    sum(20,30);
    return 0;
}

void sum(int a,int b) //定义 
{
	int 1;						   
	int sum=0;
	for(i=a;i<=b;i++){
   		sum+=i;
	}
	printf("%d到%d的和是%d\n",a,b,sum);
}

比较上述两行代码,你发现了什么不同吗?

上面的代码与下面的代码相比下面的代码多了一行 void sum(int a,int b)

这里多了一行函数声明,由于c的函数读取是自上而下的,所以如果没有函数声明,我们的编译器会识别不了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);
    

参数传递数据类型的不一致

#include <stdio.h>
void cheer(int i)
{
	printf("cheer %d",i)
}
int main()
{
	cheer(2.4);
	return 0;
}

上述的代码中函数定义为整数int类型,而主函数中cheer的是浮点数double类型。所以猜猜会发生什么?

没错,答案会输出2

这里答案会按照函数的定义来输出,c的编译器会在内部给你做一个转换。这是c的最大漏洞

我们传的参数是什么

void swap(int a,int b);

int main()
{
    int a=5;
    int b=6;
    swap(a,b);
    printf("a=%d b=%d",a,b);
    return 0;
}
void swap(int a,int b){
    int t=a;
    a=b;
    b=t;
}
  • 这样的代码能交换a和b吗?

c语言在调用函数的时候,永远只能传值给函数

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

形式参数和实际参数

对于函数参数表中的参数,叫做形式参数,调用函数给的值叫实际参数

现在,我们不区分实参和形参,我们通常把函数表中的叫做参数,调用函数并参与运算的叫做值

本地变量

定义在函数内部的变量

  • 函数每次运行,产生一个独立的变量空间,在这个空间中的变量就是函数这次运行所独有的,叫做本地变量
  • 生存期:什么这个变量时候出现到什么时候消亡
  • 作用域:在什么范围可以访问这个变量(这个变量可以起作用)
  • 对于本地变量,以上的生存期和作用域是统一的:大括号内

本地变量的规则

  • 本地变量定义在块内,它可以定义在函数块内,也可以定义在语句块内
  • 甚至可以随便拉一对大括号定义变量
void swap(int a,int b);

int main()
{
    int a=5;
    int b=6;
    {
        int i=0;
        printf("%d",i);   //可以随便用一对大括号定义变量
    }
    swap(a,b);
    printf("a=%d b=%d",a,b);
    return 0;
}
void swap(int a,int b){
    int t=a;
    a=b;
    b=t;
}
void swap(int a,int b);

int main()
{
    int a=5;
    int b=6;
    {
        printf("%d",a); //块的嵌套,最外层大括号是主层
    }
    swap(a,b);
    printf("a=%d b=%d",a,b);
    return 0;
}
void swap(int a,int b){
    int t=a;
    a=b;
    b=t;
}
  • 程序运行进入块之前,其变量就不存在了,离开块,其中变量就消失
  • 块外面定义的变量在里面仍然有效,而在里面定义的在外面无效,即就整体而言大块的优先级高于小块
  • 块里面定义和外面的变量同名,在块内运行时覆盖外面的
void swap(int a,int b);

int main()
{
    int a=5;
    int b=6;
    {
        int a=0;
        printf("%d",a);  
    }
    swap(a,b);
    printf("a=%d b=%d",a,b);
    return 0;
}
void swap(int a,int b){
    int t=a;
    a=b;
    b=t;
}

其他细节

没有参数时

  • void f(void);
  • void f();
  • 在传统C中,它表示f函数的参数未知,并不表示没有参数
void swap();

int main()
{
    int a=5;
    int b=6;

    swap(a,b);
    printf("a=%d b=%d",a,b);
    return 0;
}
void swap(double a,double b){
    int t=a;
    a=b;
    b=t;
}

这样写时告诉c语言swap是个函数,但不知道是什么类型,所以c会默认给你补一个整数类型,接着往下运行时会发现swap时浮点是类型,和所猜的不一样,最后输出就会是一个很奇怪的数

逗号运算符号

调用函数时的逗号和逗号运算符怎么区别

  • 调用函数时的括号里逗号就是标点符号,不是运算符
  • f(a,b)
  • 如果是与运算符
  • f((a,b))

注意

  • c语言中函数里面不能嵌套函数

  • int i,j,sum(int a,int b); 意思是定义i,j和函数sum

int main()

  • int main()是一个函数,叫主函数
  • return的0
    • Windows:if error level 1....(P处理文件/bat文件)
    • Unix Bash:echo $?
    • Csh:echo $status

int main(void)也可以

一个程序返回0表示运行正常结束了,否则返回任何一个非0值都是出现了错误