C语言知识回顾——数组

什么是数组?数组可以说是目前为止讲到的第一个真正意义上存储数据的结构。虽然前面学习的变量也能存储数据,但变量所能存储的数据很有限。那么到底什么是数组呢?顾名思义,数组就是很多数的组合。那么这些数有没有什么要求呢,是不是不管什么数组合在一起都是数组呢?那就让我们回顾一下C语言——数组中这一章,敲黑板了!

【1】一维数组的定义和引用
1.一维数组的定义
一维数组是指数组中的每个元素只带有一个下标的数组
定义方式:类型说明符 数组名【常量表达式】

2.一维数组的元素引用
数组元素的引用形式为:数组名【下标表达式】
●一个数组元素实质上是一个变量名,代表内存中的一个存储单元,一个数组占据的是一连串连续的存储单元。
●引用数组元素时,数组的下标可以是整型常量,也可以是整型表达式。
●数组必须先定义后使用。
●只能逐个引用数组元素而不能一次引用整个数组

【注意】
同一数组中存储的数必须满足以下两个条件:
①这些数的类型必须相同
②这些数在内存中必须是连续存储的

数组是在内存中连续存储的具有相同类型的一组数据的集合

3.一维数组的初始化
当数组定义后,系统就会为该数组在内存中开辟一串连续的存储单元,但这些存储单元中并没有确定的值。可以在定义数组的时候为所包含的数组元素赋初值。
例如:int a[6]={0,1,2,3,4,5};

int a[10] 就是表示在内存中开辟了10个连续的int大小的空间,总大小为10*sizeof(int);

★所赋初值放在一对花括号中,数值类型必须与所说明的类型一致。
★所赋初值之间用逗号隔开,系统将按这些数值的排列顺序,从a[0]元素开始以此给数组a中的元素赋值。
★不能跳过前面的元素给后面的元素赋初值,但是允许为前面的元素赋值为0。
★当所赋初值个数少于所定义数组的元素个数时,系统将自动给后面的其他元素赋初值0。
★可以通过赋初值来定义一维数组的大小,定义数组时的一对方括号中可以不指定数组的 大小。

◇◇◇◇◇◇◇◇★让我们来具体巩固一下:

int a[5];

它表示定义了一个整型数组,数组名为 a,定义的数组称为数组 a。数组名 a 除了表示该数组之外,还表示该数组的首地址。

此时数组 a 中有 5 个元素,每个元素都是 int 型变量,而且它们在内存中的地址是连续分配的。也就是说,int 型变量占 4 字节的内存空间,那么 5 个int型变量就占 20 字节的内存空间,而且它们的地址是连续分配的。

这里的元素就是变量的意思,数组中习惯上称为元素。

在定义数组时,需要指定数组中元素的个数。方括号中的常量表达式就是用来指定元素的个数。数组中元素的个数又称数组的长度。
数组中既然有多个元素,那么如何区分这些元素呢?方法是通过给每个元素进行编号。

数组元素的编号又叫【下标】

数组中的下标是从 0 开始的(而不是 1)

那么,如何通过下标表示每个数组元素的呢?
是通过“数组名[下标]”的方式。例如“int a[5];”表示定义了有 5个元素的数组a,这5个元素分别为 a[0]、a[1]、a[2]、a[3]、a[4]。其中 a[0]、a[1]、a[2]、a[3]、a[4] 分别表示这 5 个元素的变量名。

那为什么数组的下标是从 0 开始而不是从 1 开始呢?
试想,如果从 1 开始,那么数组的第 5 个元素就是 a[5],而定义数组时是 int a[5],两个都是 a[5] 就容易产生混淆。而下标从 0 开始就不存在这个问题了!所以定义一个数组a[n],那么这个数组中元素最大的下标是 n–1,而元素 a[i] 表示数组 a 中第 i+1 个元素。

另外,方括号中的常量表达式可以是“数字常量表达式”,也可以是“符号常量表达式”。但不管是什么表达式,必须是常量,绝对不能是变量。通常情况下 C 语言不允许对数组的长度进行动态定义,换句话说,数组的大小不依赖程序运行过程中变量的值。非通常的情况为动态内存分配,此种情况下数组的长度就可以动态定义,这个稍后会讲。

一维数组的初始化可以使用以下方法实现:

  1. 定义数组时给所有元素赋初值,这叫——完全初始化
    例如:
int a[5] = {1, 2, 3, 4, 5};

通过将数组元素的初值依次放在一对花括号中,如此初始化之后,a[0]=1;a[1]=2;a[2]=3;a[3]=4;a[4]=5,即从左到右依次赋给每个元素。需要注意的是,初始化时各元素间是用逗号隔开的,不是用分号。

  1. 可以只给一部分元素赋值,这叫——不完全初始化
    例如:
int a[5] = {1, 2};

定义的数组 a 有 5 个元素,但花括号内只提供两个初值,这表示只给前面两个元素 a[0]、a[1] 初始化,而后面三个元素都没有被初始化。不完全初始化时,没有被初始化的元素自动为0。

【注意】
不完全初始化完全不初始化不一样。如果完全不初始化,即只定义int a[5];而不初始化,那么各个元素的值就不是0了,所有元素都是垃圾值。

当然你也不能写成int a[5]={};如果大括号中什么都不写,那就是极其严重的语法错误。大括号中最少要写一个数。比如int a[5]={0};这时就是给数组清零,此时数组中每个元素都是零。此外,如果定义的数组的长度比花括号中所提供的初值的个数少,也是语法错误,如a[2]={1,2,3,4,5};

  1. 如果定义数组时就给数组中所有元素赋初值,那么就可以不指定数组的长度,因为此时元素的个数已经确定了。编程时我们经常都会使用这种写法,因为方便,既不会出问题,也不用自己计算有几个元素,系统会自动分配空间。
    例如:
int a[5] = {1, 2, 3, 4, 5};

可以写成:

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

第二种写法的花括号中有 5 个数,所以系统会自动定义数组 a 的长度为 5。但是要注意,

只有在定义数组时就初始化才可以这样写

如果定义数组时不初始化,那么省略数组长度就是语法错误!!!
比如:

int a[];

那么编译时就会提示错误,编译器会提示你没有指定数组的长度

下面给大家写一个简单的程序:

# include <stdio.h>
int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    int i;
    for (i=0; i<5; ++i)
    {
        printf("%d\n", a[i]);
    }
    return 0;
}

输出结果是:
1
2
3
4
5

a 表示数组的名字,[5] 表示这个数组有 5 个元素,并分别用 a[0]、a[1]、a[2]、a[3]、a[4] 表示。并分别把花括号内的 1、2、3、4、5 赋给变量 a[0]、a[1]、a[2]、a[3]、a[4]。再次强调,数组下标从 0 开始,即从 a[0] 开始,而不是 a[1];

同样,也可以用 scanf 手动从键盘对数组进行初始化:

# include <stdio.h>
int main(void)
{
    int a[5] = {0};  //数组清零初始化
    int i;
    printf("请输入5个数:");
    for (i=0; i<5; ++i)
    {
        scanf("%d", &a[i] );
    }
    for (i=0; i<5; ++i)
    {
        printf("%d\x20", a[i]);
    }
    printf("\n");
    return 0;
}

输出结果是:
请输入5个数:1 2 3 4 5
1 2 3 4 5

同使用 scanf 给字符数组输入字符串时有所不同,输入数字时必须用 for 循环进行输入,而输入字符串时无须用循环,直接用 scanf 就可以了。

数组必须先定义,然后使用

C 语言规定,只能逐个引用数组元素,而不能一次引用整个数组。前面讲过,数组元素的表示形式为:

数组名[下标](下标可以是整型常量或整型表达式)
比如:

a[0] = a[5] + a[7] - a[2 * 3]

千万要注意,定义数组时用到的**数组名[常量表达式]和引用数组元素时用到的数组名[下标]**是有区别的:
定义数组时的常量表达式表示的是数组的长度,而引用数组元素时的下标表示的是元素的编号。

比如:

# include <stdio.h>
int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};  //定义长度为5的数组a
    int t;
    t = a[3];  /*引用数组a中下标为3的元素a[3], 此时的3不代表数组的长度*/
    printf("t = %d\n", t);
    return 0;
} 

输出结果是:
t = 4

int a[5];是定义了有 5 个元素的数组,这 5 个元素分别为 a[0]、a[1]、a[2]、a[3]、a[4]。而 t=a[3] 中的 a[3] 不是数组,只是其中的元素 a[3];

因此,下面这个程序是错的:

# include <stdio.h>
int main(void)
{
    int a[5];
    a[5] = {1, 2, 3, 4, 5};
    return 0;
}

错误的原因是下面的 a[5] 不是数组。只有在定义的时候“a[常量]”表示的才是数组,此时方括号中的数字才表示数组长度。除此之外程序中任何地方看到“a[常量]”都不是数组,都只是数组的一个元素、一个变量,此时的“常量”表示的是元素的下标。
此外,当给元素单独赋值时不能加大括号,因为元素就是变量,即 a[5] 只是一个变量名。前面是怎么给变量赋值的现在就怎么给数组元素赋值,比如a[5]=1;但是对于上面这个程序,这么写还是错误的。因为数组元素的下标是从 0 开始的,数组 a 的元素只有 a[0]~a[4],并没有 a[5] 这个元素。

△△△△△△△△提到数组,就不得不说指针了
一维数组和指针
定义一个指针,指向int a[10] 的首地址
int *p = a;
或者
int *p;p = a;//注意这里不是&a,因为a作为数组名,本身就是地址。

 a[0] = 10;  //数组版
 *p = 10;    //指针版
 
 a[2] = 20;    //数组
 *(p+2) = 20;    //指针版

首先,p = a,代表着首地址 ,比如是0xA0,那么 p+1 是什么意义?
p+1 表示在首地址的基础上,往下偏移1个空间量。那难道是p+1=0XA1吗?
不是的,因为这个数组的一个空间量是int大小 ,所以它的地址应该是0XA0+(sizeof(int)偏移量)//0XA4*,然后再进行*运算代表取值。

数组指针操作的基本思想

在首地址的基础上,进行指针偏移,然后进行取值和赋值的操作。

【2】二维数组的定义和引用
(1)二维数组的定义
1.在C语言中,二维数组中的元素排列的顺序是:
按行存放,即在内存中先顺序存放第一行的元素,再存放第二行的元素。

二维数组的存储总是占用一块连续的存储单元

2.一般形式为:类型说明符 数组名【常量表达式1】【常量表达式2】

(2)二维数组元素的引用
表示形式为:数组名【下标表达式1】【下标表达式2】
★数组的下标可以是整型表达式
★数组元素可以出现在表达式中,也可以被赋初值

(3)二维数组的初始化
●可以在定义二维数组的同时给二维数组的各元素赋初值。
●全部初值放在一对花括号中,每一行的初值又分别括在一对花括号中,之间用逗号隔开。
●当某行一对花括号内的处置个数少于该行的元素时,系统将自动地给后面的元素赋初值0。
●不能跳过每行前面的元素而给后面的元素赋初值。

(4)通过赋初值定义二维数组的大小
对于一维数组,可以在数组定义语句中省略方括号中的常量表达式,通过所赋初值的个数来确定数组的大小。
对于二维数组,只可以省略第一个方括号中的常量表达式,而不是省略第二个方括号中的常量表达式。

二维数组的初始化可以按行分段赋值,也可按行连续赋值
例如,对于数组 a[5][3],按行分段赋值应该写作:

int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };

按行连续赋值应该写作:

int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};

这两种赋初值的结果是完全相同的。

二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组,C语言允许这种分解。
例如,二维数组a[3][4]可分解为三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2]。
这三个一维数组可以直接拿来使用。这三个一维数组都有 4 个元素,比如,一维数组 a[0] 的元素为 a[0][0]、a[0][1]、a[0][2]、a[0][3];

【3】字符数组
1.字符数组的定义
字符数组,顾名思义就是数组中的每一个元素都是字符。
定义方法同普通数组的定义相同,即逐个对数组元素赋值。

2.字符数组的初始化及引用
1.初始化
对字符数组初始化,可逐个元素地赋值,即把字符逐个赋给数组元素。
●如果花括号中提供的初值个数(即字符个数)大于数组长度,则编译时会按语法错误处理。
●如果初值个数小于数组长度,则将这些字符赋给数组中前面那些元素,其余的元素定为空字符 ‘\0’。

2.引用形式
采用下标引用,即数组名【下标】

3.字符串和字符串结束标志
C语言中,将字符串作为字符数组来处理。为了测定字符串的实际长度,C语言规定了一个字符串结束标志,以字符 ‘\0’ 代表。就是说,在遇到字符 ‘\0’ 时,表示字符串结束,由它前面的字符组成字符串。

4.字符数组的输入输出
字符数组的输入输出有两种方法:
①用"%c"格式符,将字符逐个输入或输出
②用"%s"格式符,将整个字符串一次输入或输出。

5.字符串处理函数
在调用这些函数之前,须在程序前面的命令行包含标准头文件"string.h"
●puts()
调用形式为puts(字符数组),将一个字符串(以’\0’结束),输出到终端设备。

●gets()
调用形式为gets(字符数组),从终端输入一个字符串到字符数组中,并且得到一个函数值。

●strcat()
调用形式为strcat(字符数组1,字符数组2),该函数将字符数组2所指字符串的内容连接到字符数组1所指的字符串后面,并自动覆盖字符数组1串末尾的’\0’。该函数返回字符数组1的地址值。

●strlen()
调用形式为strlen(字符数组),此函数计算出以字符数组为起始地址的字符串的长度,并作为函数值返回。

●strcpy()
调用形式为strcpy(字符数组1,字符数组2),把字符数组2所指字符串的内容复制到字符数组1所指的存储空间中。函数返回字符数组1的值,即目地串的首地址。

●strcmp()
调用形式为strcmp(字符数组1,字符数组2),该函数用来比较字符数组1和字符数组2所指字符串的大小。(按ASCII码值的大小)
若字符数组1>字符数组2,函数值大于0(正数)
若字符数组1=字符数组2,函数值等于0
若字符数组1<字符数组2,函数值大于0(负数)

另外注意:
①(’\0’)不占字符串长度
②用scanf输入字符串时,其空格会全部忽略
③用字符串方式赋值比用字符逐个赋值要多占用一个字节;
④不能用字符串常量对字符数组名进行整体赋值,可以用字符串常量给一维字符数组赋值;

发布了21 篇原创文章 · 获赞 44 · 访问量 1589

猜你喜欢

转载自blog.csdn.net/weixin_43964458/article/details/104415877