C程序--数组与函数

数组

一维数组

定义
  1. 声明
    类型 数组名[常量表达式]
    例:
    int a[10];
  • 注意:数组下标从零开始
    在这里插入图片描述
    2.定义时注意事项
	int n = 10;
 	int a[n];

上面代码块中我们定义了一个长度为10的数组。但我们定义数组时,我们并不知道这个数组应该定义多大,当然最好的情况是定义一个合适大小的数组,因为这样既不浪费也不会不够用,但是呢,我们很多时候并不知道一个数组该多大,所以在这种情况下我们通常会定义一个比较大的数组,可能有的人会想,这样有点浪费,那我能不能定义一个可变大小的数组?比如说

int n;
scanf("%d", &n);
int a[n]={0};

那这样做允不允许呢?
这个在以前的ANSI 标准中是不允许的,但是在新的C99标准中允许了。由于老的C语言标准C89才是主流,所以这个有些编译器不支持。所以我建议还是不要这样定义数组。当然,如果你想只改一个地方就把数组的大小改变,而不是整篇代码的去改,我们提供两个办法:
    * 定义符号常量:

const int i = 4;
int a[i] = {0,1};

这里的i是一个符号常量,那么以后所有的程序中,只要涉及到数组的大小,我们都用i表示,如果用着用着发现数组不够用了,那么我们只需要改变i的大小即可。当然这个方式并不是定义了一个可变长的数组,我个人的观点,是不支持定义可变长的数组的。
    * 定义程序的预定义:

#include <stdio.h>
#define N 4
int main()
{
	int a[N]={1,2};
}

上面这段代码是预定义(当然,我习惯叫宏定义)了一个N,N 的值是4,那么在程序的任意一个地方遇到N,都可以把它当作4来看待。(ps:这两种方式我个人推荐这种方式定义数组,还有一点就是,一般预定义的常量,我们习惯用大写的字母或单词表示),通过这种方式,我们也可以很方便对数组大小进行修改

初始化
  • 数组的初始化
    在这里插入图片描述
    注意:
  • 我们可不可以指定义一个数组,不对其进行初始化?
    例如:
int a[4];
printf("%d %d %d %d", a[0],a[1],a[2],a[3]);

(我这里由于数组小是直接一个一个输出的,当然我并不建议这样输出 ,推荐for循环输出)
当然是不可以的,那么这段代码会输出什么呢?会输出一些意料之外的数。这是因为我们为这个数组申请了一块内存空间,我们并不知道我们计算机中的程序有没有用过这块地址,如果没用过,那么会输出0,如果用过,那么就是一些乱七八糟的数

  • 如果我定义了一个数组,而我指明了部分元素,那剩下的元素是什么呢?
int a[4] = {1,2};
printf("%d %d %d %d", a[0],a[1],a[2],a[3]);

如果将上面的数组输出,会得到什么呢? 会输出 1 2 0 0 ,那为什么为赋值的部分是0呢?因为很多编译器会将为未初始化的元素自动初始化为0

  • 注意数组下标越界的问题
    虽然说我们可以少初始化数组中的元素,但我们不能初始化的元素多于数组下标,例:
int a[4] = {1,2,3,4,5,6};
printf("%d %d %d %d", a[0],a[1],a[2],a[3]);

那这段代码会输出什么呢? 答案编译错误,因为数组越界

二维数组

以前我们定义的数组都是存放一串数的,这些数是按照一个线性排列放在数组中的,这个时候我们就想定义一个数组存放二维空间里面的序列
比方说我们想定义一个数组存放下图的数据在这里插入图片描述
这样像一个表一样,把数存放在一个二维空间里,那么怎样定义一个这样的结构呢?很简单,我们可以定义一个二维数组来存放在这里插入图片描述
向上图一样,我们就定义了一个二维数组,数组名是a,后面出现了两个常量表达式3 4,通过这种方式我们就定义了一个3行4列的二维数组,通过观察这个二维数组,我们可以知道,我们在定义这个3行4列的二位数组的时候,其实我们就得到了3个以为数组,第一个一维数组叫a[0],第二个一维数组叫a[1],第三个一维数组叫a[2];
那一个二维数组在内存中如何存储的呢?其实很简单,他是把二维数组拉平了,拉成一个线性的结构来存储的
在这里插入图片描述

初始化
  1. 明确指出各个位置的值
    int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16}};
    输出:
for(int i = 0; i < 3; i++){	
	for(int j = 0; j < 4; j++){
		printf("%d\t", a[i][j]);
	}
	printf("\n");
}
  • 省略内层大括号
    int a[3][4] = {1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14};
    这样也是可以的,因为即使你不给出内层大括号,计算机也十分清楚这些元素与二维数组的对应关系。其实不单是内层大括号可以省略,其实最高维的下标也可以省略,即:
    int a[][4] = {1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16}
    这样初始化的时候,计算机会自动计算这个二维数组的行,他会数4个元素给第一行,在数4个给第2行,在数4个给第3行,在数,没了,那么就完成了初始化。当然,如果还有两个元素15,16,那么没关系,它会将这2个给第4行,而且第4行的剩余两个为0

  • 我们还可以对二维数组的部分元素初始化
    例:int a[][4] = {{1,4}, {0,9,12}, {13,14,16}}
    这样,每一行中为赋值的部分,会自动补0
    我们可以int a[3][4] = {0};这样我们可以简单的将二维数组初始化为0,当然这种情况下,我们必须清楚的指出二维数组的行和列,否则程序根本不可能执行出来

函数

定义

函数是一段可以重复使用的代码,用来独立地完成某个功能,它可以接收用户传递的数据,也可以不接收。接收用户数据的函数在定义时要指明参数,不接收用户数据的不需要指明,根据这一点可以将函数分为有参函数和无参函数

声明

函数的定义包含一个函数头(function head,或称为声明符(declarator))和一个函数块函数头指定了函数的名称、返回值的类型以及参数的类型和名称(如果有参数的话)。函数块中的语句明确了该函数要做的事。
所以函数的声明部分,我们要确定三件事

  • 函数的返回值类型
  • 函数的名称
  • 参数
    确定了以上部分,我们就可以确定一个函数了
int i_min(int a, int b)
{
	int min = a;
	if(min > b){
		min = b;
	}
	return min;
}

上述代码,函数的返回值类型为int,函数的名称为i_min,函数的参数为 a,b,而花括号里面就是函数体,函数体描述了函数要做的事情,也就是函数的功能
函数的声明与调用

#include <stdio.h>
// 声明函数
int i_min(int a, int b)
{
 int min = a;
 if(min > b){
  min = b;
 }
 return min;
}
int main()
{
	int a,b;
	a = 1;
	b = 2;
	int min = i_min(a,b); // 函数的调用
	printf("%d", min);
	return 0;
}

上面这种方式是将函数的原型与实现写在了一起,并且它的位置实在主函数的上方,注意这种写法,函数的原型与实现是必须放到主函数的上方的,因为如果你写在了主函数的下面,如下面的代码,在编译到调用函数的位置的时候,程序会找不到这个函数,而报错

#include <stdio.h>
int main()
{
 int a,b;
 a = 1;
 b = 2;
 int min = i_min(a,b); // 函数的调用
 printf("%d", min);
 return 0;
}
// 函数的原型
int i_min(int a, int b)
{
 int min = a;
 if(min > b){
  min = b;
 }
 return min;
}

这就是上面所述的错误的写法

#include <stdio.h>
// 函数的原型
int i_min(int a, int b);

int main()
{
 int a,b;
 a = 1;
 b = 2;
 int min = i_min(a,b); // 函数的调用
 printf("%d", min);
 return 0;
}
// 函数的实现
int i_min(int a, int b)
{
 int min = a;
 if(min > b){
  min = b;
 }
 return min;
}

我们也可以将函数的原型放在主函数的上面,也可以放在主函数中,想声明变量一样,而实现在函数的下面。
函数的返回值类型

void 代表无返回值
int 代表返回值类型为int型,对应的函数体中return type;type–> int
float 代表返回值类型为float型,对应的函数体中 return type;type–>float
double 代表返回值类型为double型,对应的函数体中 return type;type–>double
char 代表返回值类型为char 型,对应的函数体中 return type;type–>char
函数的形参与实参
形参int i_min(int a, int b) 这个代码中 int a,int b 就是形参
实参i_min(a,b); 这里a,b就是实参
形参(形式参数)
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。

实参(实际参数)
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。

形参和实参的功能是传递数据,发生函数调用时,实参的值会传递给形参。

形参和实参的区别和联系

  1. 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。

  2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。

  3. 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。

  4. 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
    数组与函数
    前面我们分别介绍了函数与数组,那如果函数与数组结合起来,又是发生怎样的结果呢?
    我们知道,在调用函数的时候,我们只需要把变量名作为参数传递到函数中即可,那么,如果我们想要在一个函数中对数组进行操作,我们改如何把数组传递过去呢?
    其实很简单,我们只需要把数组名传递过去即可。那么到这里也与就会有人问,我们传递参数的时候,不是值传递吗?也就是copy了一份,那么我们在函数中对数组的一系列操作不就是没用吗?其实我想说的是,我们传递的数组名,他其实就是这个数组的首地址,假设有一个数组a[5],那么他的数组名a其实代表的就是这个数组的首地址。这里我们是吧这个数组的首地址传递过去,也即是址传递

  • 我们这里设计到了值传递址传递
    那这两种传递有什么区别呢?

    • 首先是值传递,上面我们解释了值传递就是copy,就是给这个变量拷贝了一个副本,这样无论我们对这个副本进行什么操作,对原本的变量都不会产生任何影响
    • 那么址传递呢?址传递其实就是把这个变量的地址传递了过去,也就是说,我们被调函数与函数此时操作的这个变量是同一块内存地址的变量,所以我们在函数中对这个变量的操作会影响到主函数中的这个变量。数组传递首地址就是这个原理
      下面我们看一段代码吧,也许能让我们会用更深的理解
#include <iostream>
using namespace std;
//冒泡排序 
void bubble_sort(int a[],int lenth)
{
	 int i,j;                                   //定义两个变量,用于控制循环 
	 int temp = 0;                              //定义一个变量,用于排序交换数值 
	 for(i = 0; i < lenth - 1; i++){                //外层循环,控制循环次数 
		  for(j = 0; j < lenth - 1 - i; j++){       
		   //内层循环,寻找符合if语句条件的数据,进行排序 
  		 	if(a[j] > a[j + 1]){
  				  temp = a[j];
   				 a[j] = a[j + 1];
  				  a[j + 1] = temp;                  
  				     //交换数据,第3个杯子理论(即 你想把  a=5   b=4交换位置)你可以这样,用一个变量d存储a, 
 			  }           //在把b的值给a,d中储存的a给b,伪代码是,d = a;a = b; b = d; 
		  }
	 }
}
int main()
{
	 int a[10] = {1,5,7,98,65,3,4,59,41,45};
	 bubble_sort(a,10);
	 for(i = 0; i < 10; i++){                      //遍历输出排序后的数据 
  		  if(i % 5 == 0 && i != 0){
 		    cout << endl;
   		 }                                       
    	   //这个if语句仅仅是为了控制格式,即每5个输出占一行, 
  		  cout << a[i] << " ";
	}
 	return 0;
}

相信这段代码大家都很熟悉吧,我们先来看下输出
在这里插入图片描述为何这段代码在调用函数的时候,是bubble_sort(a,10);这样调用,只传递a就可以完成数组的排序呢?这就是我上面所说的址传递
我们前面对数组作为函数的参数已经介绍的差不多了,那么我们就总结下数组作为函数的参数需要注意的是什么?
注意

  1. 与数组元素作为函数参数不同的是,用数组名作为函数参数时,要求形参与相对应的实参都必须是类型相同的数组,否则会发生错误。
  2. 用数组名作为函数参数时,并不是进行值的传递,即不是把实参数组的每一个元素的值都赋与形参数组的各个元素。因为实际上形参数组并不存在,编译系统并不为形参数组分配内存。

总结:数组名就是数组的首地址。因此在数组名作为函数参数时所进行的传递只是地址的传递,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也等于有了实际的数组,即实参与形参有一段共用的内存空间。

那么到这里呢,函数与数组的内容就基本上介绍了一下,如果对指针与数组的结合这块感兴趣的话,可以去下面这个博客看下,附链接,见下
处于舞台中心的“指针”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值