定义
数组就是相同数据类型构成的一组数
数组名
- 数组名的值是一个指针常量(你不能修改常量的值),它是数组第一个元素的地址,他的类型取决于数组元素的类型
- 数组和指针是不一样的,数组具有确定数量的元素,而指针只是个标量值
- 指针常量所指向的是内存中数组的起始位置,如果修改这个指针常量,唯一可行的方法就是把整个数组移动到内存的其他位置
只有在两种场合下,数组名并不用指针常量来表示
- 当数组名作为sizeof操作符,sizeof返回整个数组的长度,而不是指向数组的指针的长度
- 单目操作符&的操作数时,取一个数组名的地址所产生的是一个指向数组的指针,而不是指向某个指针常量值得指针
这两者是等价的
int a[10];
int *c;
c = &a[0];
c = a;
初始化
类型标识符 + 数组名 + 数组元素的个数(常量表达式) + 初始值
int arr[5] = {0};
- excess element 超过 数组越界
数组元素不要超过给定的,数组越界有可能导致程序崩溃
中括号中数组的元素的个数 - 不能放变量
int arr6[4] = {1,2,3,4,5};
int i = 10;
int arr1[i] = {0};
int arr1[8 + 2] = {0};
int arr2[] = {1,2,3};// 不建议
float arr3[3] = {1.0,2.0,3.0};
double arr4[3] = {2.0,3.0,4.023123};
long arr5[4] = {5345,64563,234};
指针 和 下标
下标
下标和数组名一起使用,用于标识该集合中某个特定的值
int b[10];
*(b+3);
b的值是一个指向整型的指针,*(b+3)
所指向的是数组的第1个元素向后移3个整数长度的位置,然后间接访问这个新的位置
除了优先级之外,下标引用和间接访问完全相同
array[subscript];
*(array+(subscript));
关于下标和指针的理解
int array[10];
int *ap = array +2;
指针 | 转换 | 下标 |
---|---|---|
ap | array + 2 | &array[2] |
*ap | *(array + 2) | array[2] |
ap[0] | *(ap+(0)) = array + 2 | &array[2] |
ap+6 | array + 2 + 6 = array + 8 | &array[8] |
*ap+6 | *(array + 2) + 6 | array[2]+6 |
*(ap+6) | *(array 2 + 6) | array[8] |
ap[6] | *(ap+(6)) | &array[8] |
&ap | ap指针所在的地址 | 无 |
ap[-1] | *(array+(-1)) | array[1] |
2[array]
相当于*(2+(array))
也就是说*(array+2)
- 有些编译器会有下标检查,但这是一个不小的负担,不过对于如今的电脑而言,并不是个大的问题
- 指针有时候会比下标更有效率,但不应因为效率而影响程序的可读性
一般有数组的地方就有循环
遍历一个数组 数组下标,从0开始到 数组元素个数-1 为止
for (int i = 0 ; i < 3; i++) {
printf("%d\n",arr2[i]);
}
数组 和 指针
- 声明一个数组时,编译器将根据声明所指定的元素数量为数组元素保留内存空间,然后在创建数组名,它的值是一个常量,指向这段空间的起始位置.
- 声明一个指针变量时,编译器只为指针变量保存内存空间,如果它是一个自动变量,它甚至根本不会被初始化
int a[5];
int *b;
上述声明后
- 表达式
*a
是完全合法的,但表达式*b
是非法的 -
*b
将访问内存中某个不确定的位置,或者导致程序终止 -
b++
能通过编译,但a++
却不能,因为a的值是一个常量
作为函数参数的数组名
当数组名作为参数传递给函数时是指向数组第一个元素指针的拷贝,函数如果执行了下标引用,实际上是对这个指针执行间接访问操作,函数可以访问和修改调用程序的数组元素
以下两个声明只是在当前这个上下文环境中相等
int fun(char *string);
int fun(char string[]);
字符数组 和 字符串
字符数组
char arr[4] = {'a','n','s','d'};
char arr[4] = "ansd";
以下声明,尽管第二个声明看像去是一个字符串常量,实际上并不是
char message[] = "Hello";
char *message2 = "Hello";
以上声明
- 前者初始化一个字符数组的元素
- 后者是一个真正的字符串常量,这个指针变量被初始化为指向这个字符串常量的存储位置
输出方法
for (int i = 0; i < 4; i++) {
printf("%d\t",arr[i]);
printf("%c\t\n",arr[i]);
}
多维数组
初始化
// 方法一
int matrix[2][3] = {100,101,102,110,111,112};
// 方法二
matric[0][2] = 102;
// 方法三
int matix2[3][5]={
{00,01,02,03,04},
{10,11,12,13,14},
{20,21,22,23,24}
}
存储顺序
int a[12];
int b[3][4];
int c[2][2][3];
数值 | int a[12]; |
int b[3][4]; |
int c[2][2][3]; |
---|---|---|---|
0 | a[0] | b[0][0] | c[0][0][0] |
1 | a[1] | b[0][1] | c[0][0][1] |
2 | a[2] | b[0][2] | c[0][0][2] |
3 | a[3] | b[0][3] | c[0][1][0] |
4 | a[4] | b[1][0] | c[0][1][1] |
5 | a[5] | b[1][1] | c[0][1][2] |
6 | a[6] | b[1][2] | c[1][0][0] |
7 | a[7] | b[1][3] | c[1][0][1] |
8 | a[8] | b[2][0] | c[1][0][2] |
9 | a[9] | b[2][1] | c[1][1][0] |
10 | a[10] | b[2][2] | c[1][1][1] |
11 | a[11] | b[2][3] | c[1][1][2] |
// 给数组a赋值
int *d = a;
for (int i = 0; i < 12; i++)
{
*d = i;
d++;
}
// 给数组b赋值
d = &b[0][0];
for (int i = 0; i < 12; i++)
{
*d = i;
d++;
}
// 给数组c赋值
d = **C;
for (int i = 0; i < 12; i++)
{
*d = i;
d++;
}
// 打印数组a
for (int i = 0; i < 12; i++)
{
cout << "a"<<i<<"="<<a[i] << endl;
// printf("a[%d] = %d \n", i,a[i]);
}
// 打印数组b
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
cout << "b" << i << j <<"=" << b[i][j] << endl;
}
}
// 打印数组c
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 3; k++)
{
cout << "c" <<i<<j<<k<<"=" <<c[i][j][k] << endl;
}
}
}
数组名
- 一维数组名得值是一个指针常量,类型:指向元素类型的指针,它指向数组的1个元素
- 二维数组名的值是一个指向一个包含x个某类型元素的数组的指针
int matrix[2][3];
-
matrix;
类型:指向包含3个整形元素的数组的指针 -
*matrix;
类型:指向整形的指针
下标
array[s2][s1];
*(*(array+(s1))+(s2));
注意:
array[3,4];
相当于
array[4];
逗号操作符首先对第1个表达式求值,但随即丢弃这个值,最后的结果是第2个表达式的值
指向数组的指针
对于一维数组,以下声明是合法的
int vector[10],*vp = vector;
但对于多维数组,以下声明则是非法的
int matrix[3][10],*mp = matrix;
因为matrix并不是一个指向整形的指针,而是一个指向整形数组的指针,其指针声明如下
int (*)p[10];
下标的引用的优先级高于间接访问,但由于括号的存在,首先执行的还是间接访问,接下来执行的是下标引用,所以p指向的是某种类型的数组,对该数组进行下标引用操作得到的是一个整型值,所以p是一个指向整型数组的指针
int (*p)[10] = matrix;
它使p指向matrix的第一行,p是一个指向拥有10个整形元素的数组指针
如果需要一个指针逐个访问整型元素可以进行如下声明
int *pi = &matrix[0][0];
int *pi = matrix[0];
作为参数的多维数组
其实际传递的是指向数组第一个元素的指针,多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式求值
int matrix[3][10];
fun(matrix);
参数matrix的类型是指向包含10个整形元素的数组的指针
fun的函数原型如下
void fun(int (*mat)[10]);
void fun(int mat[][10]);
数组长度自动计算
对于多维数组,只有第一维才能根据初始化列表缺省地提供,其他几个维度必须显式地写出
指针数组
int *api[10];
- 下标引用高于间接访问,所以在这个表达式中,首先执行下标引用,因此api是某类型的数组,其元素个数为10
- 随后进行间接访问操作,得到一个整形,所以其元素类型为指向整形的指针
例:
以下为一个指针数组
char const keyword[]={
"do",
"for",
"if",
"register",
"return";
"switch"
"while"
};
#define N_KEYWORD (sizeof(keyword)/sizeof(keyword[0]))
以下则为一个矩阵
char const keyword[][9]={
"do",
"for",
"if",
"register",
"return";
"switch"
"while"
};
sizeof用于对数组中的元素进行自动计数
- 通过sizeof来得到数组中元素的个数
- sizeof(keyword)的结果为整个数组所占用的字节数
- sizeof(keyword[0])为数组每个元素所占用的字节数
- 这两个数相除为整个数组的个数
sizeof是字符的大小
int a[] = {1,2,3,4,5};
sizeof(a) / sizeof(int) 数据大小 / 数据类型大小 = 多少个
数组:排序 冒泡排序
冒泡排序是一个双层的for循环 ,外层循环控制的是比较的趟数,内层循环控制的是每次比较的次数
int arr6[5] = {6,5,4,3,1};
for(int i = 0; i < 5 - 1; i++) {
for(intj = 0; j < 5 - i - 1; j++) {
if(arr6[j] > arr6[j+1]) {
arr6[j] = arr6[j] ^ arr6[j+1];
arr6[j+1] = arr6[j] ^ arr6[j+1];
arr6[j] = arr6[j] ^ arr6[j+1];
}
}
}
for(int i = 0; i < 5; i++) {
printf("arry5[%d] is %d\n",i,arr6[i]);
}