c语言数组实际作用,C语言数组的讲解(一)

谨记

每个人心中都有一片海,自己不扬帆,没人帮您启航,久了就是一片死海。人生,就是一场自己与自己的较量:让积极打败消极,让快乐打败忧郁,让勤奋打败懒惰,让坚强打败脆弱。在每一个充满希望的清晨,告诉自己:努力,就总能遇见更好的自己。

今天这篇文章将为读者介绍C语言中一个非常重要的知识点————数组,相信的知识已经不能满足我们实际开发的需要,所以,为了开发更快捷、更方便,那么今天开始我们就走进C语言的重点知识点。

通过本篇文章,你将学到如下知识点:

一维数组的概念

一维数组的定义方式

一维数组的内存分配

一维数组的排序

二维数组的介绍

二维数组的初始化

二维数组的深入

一、一维数组

1、数组的定义

数组就是具有一定顺序关系的若干变量的一个集合,我们简称数组,其中每一个变量我们称为数组的元素,数组的几个关键点:

1、组成数组的元素都是互不相干的独立的变量

2、这些变量(数组元素)的数据类型必须是相同的

3、变量之间有一定的顺序关系。

例如:int [10];

在前面的文章中已经提到过,数组是C语言的构造数据类型,一个数组可以分解成多个数组元素,这些数组元素可以是基本数据类型或者构造数据类型,因此,如果按照数组元素的类型来进行一个划分,那么数组可以分为数值数组、字符数组、指针数组、结构体数组等。

在C语言中,如果程序员要想使用数组,那么首先必须的去定义。数组的代表就是一维数组。

一维数组就是只有一个下标的数组,我们称为一维数组。定义格式:

[]

存储类型指的是auto, register, static, extern。若省略,相当于auto。

数据类型可以是任一种基本数据类型或构造数据类型。

数组名是用户定义的数组标识符。

方括号中的常量表达式表示数据元素的个数,也称为数组的长度。

对于数组的定义,需要注意一下几点:

1、数组的数据类型,其实就是指的数组元素的取值数据类型,对于同一个数组,数组元素的数据类型都是一样的。

2、数组名应当符合标识符的命名规定,即由字母、数字和下划线组成,但不能以数字开头。

3、在同一作用域中,数组名不能和其他变量的名称一样,

4、方括号中常量表达式表示数组元素的个数,如a[6]表示数组a有6个元素,它需要在数组定义时就确定下来,不能随着程序的运行动态更改。它的下标从0开始计算,因此6个元素分别为a[0]、a[1]、a[2]、a[3]、a[4]、a[5]。

5、不能用变量来表示数组的长度,只能是常量表达式或者常量

6、允许多个相同数据类型的数组和变量声明和定义。

int num[3];//定义了一个名称叫做num的数组,数组中可以存储3个int类型的数据

// num = 15;//会报错,系统不知道应该给谁赋值

// 只要定义一个C语言的数组, 系统就自动会给数组中的每一块小得存储空间一个编号

//这个编号从0开始,依次递增

//数组中系统自动绑定的编号,我们称为索引

num[0] = 10;

num[1] = 12;

num[2] = 20;

注意点:C语言中规定了数组必须逐个元素引用,而不能整体引用。数组的引用实际上就是数组元素的引用。数组元素的一般表示方法为: 数组名[下标]

若数组在定义时指定有n个元素,则数组的下标范围为0~(n-1)。如果数组的下标越界,则有可能导致以下几种结果:

① 若越界访问的内存空间是空闲的,程序可能不受影响,仍能正确运行。

② 若越界访问的空间已经被占用,且写了很重要的数据。在这种情况下,若程序执行了非法写操作,则程序可能会异常终止或崩溃。

③ 若越界访问的空间是空闲的,程序只是进行了读操作,则程序能继续运行,但无法得出正确的结果。

④ 其他情况。

2、一维数组的初始化

一维数组初始化严格上来说有7种。

1、局部数组初始化

对于普通局部数组,若定义时,没有初始化,则数组中元素的值,是不确定的。

2、static数组不初始化

对于static修饰的数组,若定义时,没有初始化,则数组中元素的值默认为0

3、全局数组不初始化

对于全局数组,若定义时,没有初始化,则数组中元素的值默认也为0。

4、全部初始化

与变量在定义时初始化一样,数组也可以在定义时进行初始化,如对整型数组进行初始化。

int a[10]={1, 2, 9, 21, 8, 10, 7, 24, 0, 20};

此处还是要注意,数组只能通过下标逐个引用元素。定义数组时,对数组元素的初始化,只能写成一行,不能换行写。

5、部分初始化:数组在定义时可以对其中的部分数据进行初始化。当“{}”中值的个数少于元素个数时,只给前面部分元素赋值。例如,如下定义就是对数组的前5个数据初始化,而后5个数据自动赋0。

比如:int a[10] = {1,2,3,5} 前面4个有数据初始化,后面6个数据会自动赋0

6、数组全部赋值

若想要对数组中的元素全部赋值,则可以省略数组下标中的常量。编译器会根据初始化列表自动计算数组元素的个数,如下所示。

int a[]={1, 2, 5, 23, 8, 10, 7, 24, 0, 22};

7、数组全部初始化为0

特殊写法: int a[10] = {0};

当然给数组清零,还有其他写法,比如可以使用库函数memset,把数组用0来填充,需要引入头文件string.h。

int main(int argc, const char * argv[]) {

int a[10];

memset(a, 0, sizeof(a));

return 0;

}

可以使用库函数bzero,把数组清0,需要引入头文件strings.h。

int main(int argc, const char * argv[]) {

int a[10];

bzero(a, sizeof(a));

return 0;

}

3、一维数组内存分配

在内存中,数组中的元素占用连续的存储空间,并且根据每个元素所占存储空间来进行内存分配。数组名代表数组的起始地址,是地址常量,对数组名求sizeof(),可以得出数组在内存空间中所占用的总空间。类似的道理,可以很容易利用下面的表达式来计算出数组的元素个数:

数组的元素个数 = sizeof(数组名) / sizeof(数据类型)

#include

int main(int argc, const char * argv[]) {

//定义一个5个元素的数组

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

int sizea = sizeof(a);//求数组所占用的空间大小

printf("%d\n",sizea);

printf("%p\n",a);

printf("数组起始地址%p\n",&a[0]);

int n = sizeof(a) / sizeof(int);//求数组元素个数

for (int i = 0; i < n; i++) {

printf("a[%d]的地址%p\n",i,&a[i]);

}

return 0;

}

运行结果:

20

0x7fff5fbff830

数组起始地址0x7fff5fbff830

a[0]的地址0x7fff5fbff830

a[1]的地址0x7fff5fbff834

a[2]的地址0x7fff5fbff838

a[3]的地址0x7fff5fbff83c

a[4]的地址0x7fff5fbff840

Program ended with exit code: 0

通过上面的程序例子,我们可以看出,对数组名求地址,其实就是数组的第一个元素的地址,所以我们可以变像的说数组名就是一个指针,只不过是一个静态的指针,一个数组内存空间是连续的,从运行结果也可以看出来。

4、数组的排序

A、冒泡排序法

对于冒泡排序法,过程如下:

(1)比较第一个数与第二个数,若为逆序a[0]>a[1],则交换;然后比较第二个数与第三个数;依次类推,直至第n-1个数和第n个数比较为止——第一趟冒泡排序,最终,最大的数被安置在最后一个元素位置上。

(2)对前n-1个数进行第二趟冒泡排序,最终,使次大的数被安置在第n-1个元素位置。

(3)重复上述过程,共经过n-1次冒泡排序后,排序结束。

(省略了头文件和主函数,直接展示的核心代码)

//任意输入10个元素的数组排序

第一种:

int a[N], i, j, k;

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

scanf("%d",&a[i]);

}

for (i = 0; i < N - 1; i++) {

for (j = 0; j < N - 1 - i; j++) {

if (a[j] > a[j+1]) {

k = a[j];

a[j] = a[j+1];

a[j+1] = k;

}

}

}

printf("\n");

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

printf("%5d", a[i]);

}

第二种:

//利用sizeof

int a[] = {1,23,45,6,58,29},i,j,k,n;

n = sizeof(a) / sizeof(a[0]);

for (i = 0; i < n - 1; i++) {

for (j = 0; j < n - 1 - i; j++) {

if (a[j] > a[j+1]) {

k = a[j];

a[j] = a[j+1];

a[j+1] = k;

}

}

}

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

printf("%d\n",a[i]);

}

第三种,循环剥离

int a[] = {1,23,45,6,58,29},i,j,k,n,index;

n = sizeof(a) / sizeof(a[0]);

for (i = 0; i < n - 1; i++) {

index = 0;

for (j = 0; j < n - 1 - i; j++) {

if (a[index] < a[j+1]) {

index = j+1;//下标替换

}

}

k = a[index];//赋值

a[index] = a[n - 1 -i];

a[n - 1 -i] = k;

}

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

printf("%d\n",a[i]);

}

B、选择排序法

选择排序的排序过程如下。

(1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数交换——第一次选择排序,结果最小的数被安置在第一个元素位置上。

(2)再通过n-2次比较,从剩余的n-1个数中找出关键字次小的记录,将它与第二个数交换——第二次选择排序。

(3)重复上述过程,共经过n-1次排序后,排序结束。

(省略了头文件和主函数体)

int a[N], i, j, r, t;//定义数组和变量

printf("Please input %d numbers\n",N);

for (i = 0; i < N; i++)//进行循环输入数组元素

scanf("%d",&a[i]);

for (i = 0; i < N-1; i++)//循环变量

{

r = i;

for (j = i+1; j < N; j++)

if (a[j] < a[r])

r = j;

if(r != i)//判断

{

t = a[r];

a[r] = a[i];

a[i] = t;

}

}

printf ("the array after sort:\n");

for (i = 0; i < N; i++)//打印

printf ("%5d",a[i]);

printf ("\n");

多维数组(二维数组)

1、多维数组定义

前面提到一维数组只有一个下标。那么具有两个或两个以上下标的数组,就称为多维数组。多维数组元素有多个下标,以标识它在数组中的位置。多维数组的说明与一维数组的说明基本类似,其说明的一般形式如下:

可以看出,多维数组与一维数组的说明相比,只是增加了多个下标,其他特性基本与一维数组相同。例如:int b[2][3][4]。

在这里重点介绍二维数组,多维数组的用法可由二维数组类推而得到。

2、二维数组

二维数组定义的一般形式是:

[常量表达式1][常量表达式2]

其中常量表达式1表示第一维下标的长度,即行数,常量表达式2 表示第二维下标的长度,即列数。例如:int a[2][3]。

说明了一个二行三列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量共有2×3个,即:

a[0][0],a[0][1],a[0][2]

a[1][0],a[1][1],a[1][2]

3、二维数组的初始化

==============二维数组============

格式:[常量表达式1][常量表达式2]

其中常量表达式1表示第一维下标的长度,即行数,常量表达式2表示第二维下标的长度。即列数

初始化

① 降维给二维数组赋初值,即按行初始化。每一组的初始值都用{ }括起来。

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

//按降维给a数组元素全部初始化

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

//只初始化了部分数组元素,其他元素为0。第一行为1 0 0,第二行为4 0 0

② 按线性存储的形式给二维数组赋初值。

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

//按线性存储形式给二维数组全部初始化

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

//只初始化了部分数组元素,其他元素为0。第一行为1 0 0,第二行为2 0 0,第三行为0 0 0。

③ 可以省略左边下标范围的方式,给二维数组赋初值。

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

//省略左边下标范围,给数组所有元素初始化

特别要注意的是,第一维的长度可以省略,但是,第二维的长度不能省,比如下面的写法:

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

编译程序时,会有语法错误。

error: array type has incomplete element type

结论:二维数组第二维的长度不能省略。

// int a[2][] = {{1,2,3},{4,5,6}};//报错

4、二维数组内存分配

二维数组在概念上是二维的,也就是说其下标在两个方向上变化,有行和列的说法。下标变量在数组中的位置也处于一个平面之中,而不是象一维数组只是一个向量。但是内存却是连续编址的,是按一维线性排列的。如何在一维存储器中存放二维数组,

在C语言中,二维数组采取了和一维数组类似的存储方式,按行优先存,存储了第一行的元素,即存第二行的,依次类推。

int main(int argc, const char * argv[]) {

int a[2][3] = {{8, 2, 6}, {1, 7, 9}}, i, j;;

for (i = 0; i < 2; i++)

for (j = 0; j < 3; j++)

printf ("%d ", a[i][j]);

printf ("\n");

for (i = 0; i < 2; i++)

for (j = 0; j < 3; j++)

printf ("%p ", &a[i][j]);

printf ("\n");

return 0;

}

运行结果:

8 2 6 1 7 9

0x7fff5fbff810

0x7fff5fbff814

0x7fff5fbff818

0x7fff5fbff81c

0x7fff5fbff820

0x7fff5fbff824

Program ended with exit code: 0

深入理解二维数组,在数组定义形式你可以知道,其实一个二维数组只是比一维数组多了一个下标,那么,从内存分配的角度上来看的话,我们可以把一个二维数组看作是有很多个一维数组构成。例如:int a[3][4]。可以理解成二维数组含有三个元素:a[0],a[1],a[2]。每个元素a[i]由包含四个元素的一维数组组成。举一个程序代码例子

#include

#define DEBUG 0

int main(int argc, const char * argv[]) {

/*

我们也可以把二维数组,看成由多个一维数组组成。例如:int a[3][4]。可以理解成二维数组含有三个元素:a[0],a[1],a[2]。每个元素a[i]由包含四个元素的一维数组组成。

*/

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

//预编译指令

#if DEBUG

a++;

a[0]++;

a[1]++;

a[2]++;

a[3]++;

#endif

printf("a :%p a+1 :%p\n\n", a, a+1);

printf("a[0]:%p a[0]+1:%p &a[0][1]=%p\n", a[0], a[0]+1, &a[0][1]);

printf("a[1]:%p a[1]+1:%p &a[1][1]=%p\n", a[1], a[1]+1, &a[1][1]);

printf("a[2]:%p a[2]+1:%p &a[2][1]=%p\n", a[2], a[2]+1, &a[2][1]);

printf("a[3]:%p a[3]+1:%p &a[3][1]=%p\n", a[3], a[3]+1, &a[3][1]);

//有一个3×4的矩阵,要求输出其中值最大的元素的值,以及它的行号和列号

int max, i, j, r = 0, c = 0;

int b[3][4] ={{24, 89, 2, 41}, {3, 11, 9, 1}};

max = b[0][0];

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

for (j = 0; j < 4; j++)

if (b[i][j] > max)

{

max = b[i][j];

r = i;

c = j;

}

printf("Max=%d, row=%d, column=%d\n", max, r, c);

return 0;

}

运行结果:

a :0x7fff5fbff7e0 a+1 :0x7fff5fbff7f0

a[0]:0x7fff5fbff7e0 a[0]+1:0x7fff5fbff7e4 &a[0][1]=0x7fff5fbff7e4

a[1]:0x7fff5fbff7f0 a[1]+1:0x7fff5fbff7f4 &a[1][1]=0x7fff5fbff7f4

a[2]:0x7fff5fbff800 a[2]+1:0x7fff5fbff804 &a[2][1]=0x7fff5fbff804

a[3]:0x7fff5fbff810 a[3]+1:0x7fff5fbff814 &a[3][1]=0x7fff5fbff814

Max=89, row=0, column=1

Program ended with exit code: 0

通过运行结果我们可以得出结论:

1、 a是二维数组名,是地址常量。

2、 a[0],a[1], a[2], a[3]实际上都是一维数组名,代表一维数组的起始地址,也都是地址常量。

3、 a+1和a的地址差16个字节,相当于4个数组元素。因此,可以看出a代表第1行的地址,a+1代表第2行的地址。

4、 a[0]+1和a[0]的地址差4个字节,相当于1个数组元素。因此,a[0]+1相当于元素&a[0][1], a[1]+1相当于元素&a[1][1],a[2]+1相当于元素&a[2][1]。

如果读者还想继续深入研究二维数组,可以自行查阅一些相关资料,当然,后面的文字中,我也会单独的讲解。

总结

希望读者认真学习,一步一个脚印,踏踏实实的把基础落实好。这篇文章主要介绍了C语言的构造数据类型数组,重点掌握一维数组,了解和熟悉二维数组,望读者掌握好。

结尾

最后,希望读者在读文章的时候发现有错误或者不好的地方,欢迎留言,我会及时更改,感谢你的阅读和评论已经点赞收藏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值