数组
一维数组
定义
- 声明:
类型 数组名[常量表达式]
例:
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];
那一个二维数组在内存中如何存储的呢?其实很简单,他是把二维数组拉平了,拉成一个线性的结构来存储的
初始化
- 明确指出各个位置的值
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就是实参
形参(形式参数)
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。
实参(实际参数)
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。
形参和实参的功能是传递数据,发生函数调用时,实参的值会传递给形参。
形参和实参的区别和联系
-
形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
-
实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
-
实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。
-
函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
数组与函数
前面我们分别介绍了函数与数组,那如果函数与数组结合起来,又是发生怎样的结果呢?
我们知道,在调用函数的时候,我们只需要把变量名作为参数传递到函数中即可,那么,如果我们想要在一个函数中对数组进行操作,我们改如何把数组传递过去呢?
其实很简单,我们只需要把数组名传递过去即可。那么到这里也与就会有人问,我们传递参数的时候,不是值传递吗?也就是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就可以完成数组的排序呢?这就是我上面所说的址传递。
我们前面对数组作为函数的参数已经介绍的差不多了,那么我们就总结下数组作为函数的参数需要注意的是什么?
注意
- 与数组元素作为函数参数不同的是,用数组名作为函数参数时,要求形参与相对应的实参都必须是类型相同的数组,否则会发生错误。
- 用数组名作为函数参数时,并不是进行值的传递,即不是把实参数组的每一个元素的值都赋与形参数组的各个元素。因为实际上形参数组并不存在,编译系统并不为形参数组分配内存。
总结:数组名就是数组的首地址。因此在数组名作为函数参数时所进行的传递只是地址的传递,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也等于有了实际的数组,即实参与形参有一段共用的内存空间。
那么到这里呢,函数与数组的内容就基本上介绍了一下,如果对指针与数组的结合这块感兴趣的话,可以去下面这个博客看下,附链接,见下
处于舞台中心的“指针”