数组解析

前言

所谓数组,是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。 这些无序排列的同类数据元素的集合称为数组。
数组是用于储存多个相同类型数据的集合。

C语言数组的概念

数组的概念和定义

我们知道,要想把数据放入内存,必须先要分配内存空间。放入4个整数,就得分配4个int类型的内存空间:

1
int a[4];

这样,就在内存中分配了4个int类型的内存空间,共 4×4=16 个字节,并为它们起了一个名字,叫a。
我们把这样的一组数据的集合称为数组(Array),它所包含的每一个数据叫做数组元素,所包含的数据的个数称为数组长度(Length),例如int a[4];就定义了一个长度为4的整型数组,名字是a。
数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)。
此处演示一下如何使用循环结构将数据放入数组,然后依次读取数组元素的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
int main()
{
int arr[10];
int i = 0;
printf("请依次输入十个数字:\n");
for(i=0; i<10; i++)
{
scanf("%d",&arr[i]);
}

for(i=0; i<10; i++)
{
printf("%d ",arr[i]);
}

return 0;
}

注意:①数组中每个元素的类型必须相同,对于 int arr[10] 每一个元素都必须为int
②访问数组元素时,下边的取值范围为0≤index<length

数组内存是连续的

数组是一个整体,它的内存是连续的;也也就是说,数组元素之间是相互挨着的,彼此之间没有一点点缝隙。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。

二维数组的定义

一般形式:

1
dataType arrayName[length1][length2];

其中,dataType为数据类型,arrayName为数组名,length1为第一维下标的长度,length2位第二维下标的长度。
我们可以将二维数组看为一个表格,length1为行,length2为列。列入:

1
int a[3][4];

便是一个三行四列名为a的数组。
此处演示二维数组的初始化及输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9};
int i = 0;
int j = 0;
for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
for(j=0; j<sizeof(arr[0])/sizeof(arr[0][0]); j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}

注意:

对于二维数组的初始化要注意:

1.可以只对部分元素赋值,为赋值的元素自动取零。如:

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

2.如果对全部元素赋值,第一维长度可以不给出。如:

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

也可以写为

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

(即可以没有行,不能没有列)
3.二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组,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]。

C语言数组元素的查询

对无序数组查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
//无序数组的查询
int main()
{
int a[10] = {1, 10, 6, 296, 177, 23, 0, 100, 34, 999};
int n;
int i = 0;
int t = -1;
printf("请输入希望得到的数: ");
scanf("%d",&n);
for(i=0; i<10; i++)
{
if(a[i] == n)
{
t = i;
break;
}
}

if(t < 0)
{
printf("该数组中无此数\n");
}
else
{
printf("找到了此数:%d ",n);
}

return 0;
}

解析:遍历整个数组,用if语句判断输入数字是否存在于数组当中

对有序数组的查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>
//有序数组的查询
int main()
{
int a[10] = {1, 10, 6, 296, 177, 23, 0, 100, 34, 999};
int n;
int i = 0;
int t = -1;
printf("请输入希望得到的数: ");
scanf("%d",&n);
for(i=0; i<10; i++)
{
if(a[i] == n)
{
t = i;
break;
}
else if(a[i]>n)
{
break;
}
}

if(t < 0)
{
printf("该数组中无此数\n");
}
else
{
printf("找到了此数:%d ",n);
}

return 0;
}

解析:有序数组查询方法和无序相似,但是由于知道数组是升序排序,所以在查到所需数字时不必再遍历后面的数字,所以有18~21行

C语言字符数组和字符串

用来存放字符的数组称为字符数组,例如:

1
char c[20] = {'a','b','c','d'};  //给部分数组元素赋值

也可以将字符串直接赋值给字符数组,例如:

1
char c[50] = {"you have a pair of beautiful eyes"};


1
char c[50] = "you have a pair of beautiful eyes"; /这样更简洁方便

在定义数组时为了方便也可不指定数组长度,例如:

1
2
char c[] = "you have a pair of beautiful eyes";
char c[] = {'a','b','c','d','\0'}; // 注意\0

字符串结束标志 \0

字符串是一系列连续的字符的组合,要想在内存中定位一个字符串,除了要知道它的开头,还要知道它的结尾
字符串的开头很容易,知道它的名字(字符数组名或者字符串名)就可以
在C语言中,字符串总是以’\0’作为结尾,所以’\0’也被称为字符串结束标志,或者字符串结束符。
C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到’\0’就认为到达了字符串的末尾,就结束处理。’\0’至关重要,没有’\0’就意味着永远也到达不了字符串的结尾。
由” “包围的字符串会自动在末尾添加’\0’。例如,”abc123”从表面看起来只包含了 6 个字符,其实C语言会在最后隐式地添加一个’\0’,这个过程是在后台默默地进行的,所以我们感受不到。
需要注意的是,逐个字符地给数组赋值并不会自动添加’\0’,例如:

1
char c[] = {'a','b','c','d'};

此时并不会如愿输出abcd,而是有其他多余的东西,所以我们需在末尾添加’\0’,如上一个语句
当用字符数组存储字符串时,要特别注意’\0’,要为’\0’留个位置;这意味着,字符数组的长度至少要比字符串的长度大 1。请看下面的例子:

1
char a[7] = "abcdef";

“abcdef”看起来只包含了 6 个字符,我们却将 a 的长度定义为 7,就是为了能够容纳最后的’\0’。如果将 a 的长度定义为 6,它就无法容纳’\0’了,系统会报错。

字符串长度 strlen

字符串长度,就是字符串包含了多少个字符(不包括最后的结束符’\0’)。例如”abc”的长度是 3,而不是 4
他的用法举例:

1
2
3
4
5
6
7
8
9
#include<stdio.h>
#include<string.h>
int main()
{
char str[] = "c:\test\code\35\test.c"; //转义字符
long f = strlen(str);
printf("%ld",f);
return 0;
}

输出 17

字符串的输入和输出

字符串的输出

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(){
char str[] = "hello";
printf("%s\n", str); //通过字符串名字输出
printf("%s\n", "hello"); //直接输出
puts(str); //通过字符串名字输出
puts("hello"); //直接输出
return 0;
}

注意,输出字符串时只需要给出名字,不能带后边的[ ],例如,下面的两种写法都是错误的:

1
2
printf("%s\n", str[]);
puts(str[10]);

字符串的输入

在C语言中,有两个函数可以让用户从键盘上输入字符串,它们分别是:
scanf():通过格式控制符%s输入字符串。除了字符串,scanf() 还能输入其他类型的数据。
gets():直接输入字符串,并且只能输入字符串。

但是,scanf() 和 gets() 是有区别的:
scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。
但是,scanf() 和 gets() 是有区别的:
scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。
注意: 就目前学到的知识而言,int、char、float 等类型的变量用于 scanf() 时都要在前面添加&,而数组或者字符串用于 scanf() 时不用添加&,它们本身就会转换为地址。

C语言字符串处理函数

字符串连接函数strcat

strcat 是 string catenate 的缩写,意思是把两个字符串拼接在一起,strcat() 将把 arrayName2 连接到 arrayName1 后面,并删除原来 arrayName1 最后的结束标志’\0’。这意味着,arrayName1 必须足够长,要能够同时容纳 arr1 和 arr2,否则会越界(超出范围)。
程序举例:

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[100] = "abcde";
char arr2[60] = "aaaaa";
//scanf("%s",arr2);
strcat(arr1, arr2);
printf("%s",arr1);
return 0;
}

可以看到输出结果为 abcdeaaaaa
及去掉了arr1的\0 将arr2的内容添加到了arr1后面

字符串复制函数 strcpy()

strcpy 是 string copy 的缩写,意思是字符串复制,也即将字符串从一个地方复制到另外一个地方。trcpy() 会把 arrayName2 中的字符串拷贝到 arrayName1 中,字符串结束标志’\0’也一同拷贝。
程序举例:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[50] = "hello";
char arr2[50] = "goodbye";
strcpy(arr1,arr2);
printf("arr1:%s",arr1);
return 0;
}

可看到程序的输出结果为: arr:goodbye
arr1拷贝了arr2的内容并被完全覆盖
另外注意,strcat() 要求 arr1 要有足够的长度,否则不能全部装入所拷贝的字符串。

字符串比较函数 strcmp()

strcmp 是 string compare 的缩写,意思是字符串比较。字符本身没有大小之分,strcmp() 以各个字符对应的 ASCII 码值进行比较。strcmp() 从两个字符串的第 0 个字符开始比较,如果它们相等,就继续比较下一个字符,直到遇见不同的字符,或者到字符串的末尾。

返回值:若 arrayName1 和 arrayName2 相同,则返回0;若 arr1 大于 arr2,则返回正数;若 arr1 小于 arr2,则返回负数。
程序举例:

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "Abad";
char arr3[] = "abcd";
printf("arr1:arr3=%d\n",strcmp(arr1,arr3));
printf("arr1:arr2=%d\n",strcmp(arr1,arr2));
printf("arr2:arr3=%d\n",strcmp(arr2,arr3));
return 0;
}

运行结果:0 1 -1

附:冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<stdio.h>
#include<string.h>

void bubble_sort(int arr[],int sz)
{
int i = 0;
int flag = 0;
for(i=0; i<sz; i++)
{
int j = 0;
for(j=0; j<sz-1; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
flag = 1;
}
}
if(flag == 0)
{
break;
}
}
}

void print(int arr[],int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}

int main()
{
int arr[10] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz);
print(arr,sz);
return 0;
}