数组就是把多个拥有相同数据类型的数据放在一起,成为一个整体。在C语言中的数组有着它的特性,在初学时了解它特性(先不关心它的底层原理)在代码中是如何体系的,有助于写出较好的代码!
Table of Contents
一维数组
一维数组的初始化:
1,申明即初始化;
2,先申明,再定义;
void MyArray()
{
int a[3] = { 1,2,3 }; //1,申明即初始化;
int b[3]; //2,先申明,再定义
for (int i = 0; i < 3; i++)
{
b[i] = i + 1;
}
}
一维数组的引用方式
- a[2]
- *(a+2)
- (&a[1])[1] //在a[1]的基础上再前进1个;
- *(p+2) 注:先定义指针p=a;
- p[2]
void MyArray()
{
char a[5] = { 'a','b','c','d','e' };
char* p;
p = a; //p保存的是数组a的首元素的地址;
printf("a[2]=%c\n", a[2]);
printf("*(a+2)=%c\n", *(a + 2));
printf("(&a[1])[1]=%c\n", (&a[1])[1]);
printf("(&p[1])[1]=%c\n", (&p[1])[1]);
printf("p[2]=%c\n", p[2]);
printf("*(p+2)=%c\n", *(p + 2));
}
一维数组a和&a的区别辨析
//一维数组a和&a的区别辨析
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char b[30];
printf("&b:%d, &b+1:%d\n", &b, &b+1);//&b代表数组的首元素地址,步长为整个数据长度乘以数据类型
//&b:16580044, &b+1:16580074 //单步增量为30
printf("b:%d, b+1:%d\n", b, b+1);//b代表数组的首元素地址,步长为数据类型
//b:16580044, b + 1 : 16580045 //单步增量为1
system("pause");
return 0;
}
辨析一维数组的加一和其地址加一
辨析一维数组的加一和其地址加一
#include <stdio.h>
int main()
{
int a = 10; //告诉编译器,分配4个字节的内存
int b[10]; //告诉编译器,分配4*10 = 40 个字节的内存
//b+1 和 &b+1的结果不一样,为啥?
printf("b:%p, b+1: %p, &b:%p, &b+1: %p\n", b, b + 1, &b, &b + 1);
//是因为 b 和 &b 所代表的数据类型不一样
//b 代表数组首元素的地址,一个元素4字节,加1,则加4个字节;
//&b 代表整体数组的地址,一个数组4*10字节,加1,则加40个字节;
return 0;
}
一维动态数组
传统一维数组的缺点
“传统数组”就是前面所使用的数组,与动态内存分配相比,传统数组主要有以下几个缺点:
- 数组的长度必须事先指定,而且只能是常量,不能是变量。比如像下面这么写就是对的:
int a[5];
而像下面这么写就是错的:
int length = 5;
int a[length]; //错误
- 因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动态地扩充和缩小。
- 对于数组所占内存空间程序员无法手动编程释放,只能在函数运行结束后由系统自动释放,所以在一个函数中定义的数组只能在该函数运行期间被其他函数使用。
malloc函数的使用:
那么动态内存是怎么造出来的?在讲如何动态地把一个数组造出来之前,我们必须要先介绍 malloc 函数的使用。
malloc 是一个系统函数,它是 memory allocate 的缩写。其中memory是“内存”的意思,allocate是“分配”的意思。顾名思义 malloc 函数的功能就是“分配内存”。要调用它必须要包含头文件<stdlib.h>。它的原型为:
# include <stdlib.h>
void *malloc(unsigned long size);
malloc 函数只有一个形参,并且是整型。该函数的功能是在内存的动态存储空间即堆中分配一个长度为size的连续空间。函数的返回值是一个指向所分配内存空间起始地址的指针,类型为 void*型。
简单的理解,malloc 函数的返回值是一个地址,这个地址就是动态分配的内存空间的起始地址。如果此函数未能成功地执行,如内存空间不足,则返回空指针 NULL。
那么,如何判断一个内存是静态内存还是动态内存呢?凡是动态分配的内存都有一个标志:都是用一个系统的动态分配函数来实现的,如 malloc 或 calloc。
int i = 5; //静态分配
int *p = (int*)malloc(4); //动态分配
动态数组的一般原则:
- malloc是由程序员在堆栈动态开辟空间
- 返回值开辟空间的首地址,但是类型是void *,需要强制类型转换
- 分配的内存空间应该能整除类型所占的字节数
- 包含头文件malloc.h
- 只能用free(p)来释放p所指向的动态开辟的内存空间。
- 对动态内存空间的操作,用*p来操作。
- 可以用多个指针指向这个动态空间
- 当有多个指针只向这个动态空间时,只能用free一个指针,多次重复释放要被报错
- 可以将动态开辟的的内存指针作为函数参数
第一个动态数组:
第一个动态数组
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
int main()
{
int length, i; //length是数组的长度,i是循环变量
scanf("%d", &length);
int *p = (int *)malloc(20); //构造一个一维数组,长度是4字节 * 5个=20字节
if (length > 5)
{
printf("长度超出了5个,需要重新申请长度。\n");
p = (int*)realloc(p, length * 4);
}
for (i = 0; i < length; i++) //初始化
{
p[i] = i;
}
for (i = 0; i < length; i++) //输出
{
printf("%4d", p[i]);
}
free(p);
system("pause");
return 0;
}
一维动态数组的实例:
一维动态数组的实例
#include <stdio.h>
#include <time.h> //必不可少头文件:随机数
#include <stdlib.h> //必不可少头文件:动态分配空间
void GetRandomNumber(int* a, int n)
{
srand((unsigned int)time(NULL));
int i;
for (i = 0; i < n; i++)
{
a[i] = rand() % 100;
}
}
void show(int* a, int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
int* a = NULL;
int n;
scanf("%d", &n);
a = (int*)malloc(sizeof(int) * n);
GetRandomNumber(a, n);
show(a, n);
return 0;
}
二维数组
理解二维数组:
a[x][y] 本质是有x个一维数组,每个一维数组里面还存放着y个元素;
a[2][3] 本质是有2个一维数组,每个数组里存着3个元素;
二维数组的本质:
char a0[30] = "22222222222";
char a1[30] = "11111111111";
char a2[30] = "bbbbbbbbbbb";
char a3[30] = "aaaaaaaaaaaa";
//4个a[30]的一维数组,这样写很冗余,能不能把四个写成一个整体呢?二维数组
//定义二维数组,不写第一个[]值有条件, 必须要初始化
//a代表首行地址,首行地址和首行首元素地址有区别,但是他们的值是一样区别:
//步长不一样:当a代表首行地址时步长为整个数组长度;当a代表首行元素地址时步长为单个类型的长度;
char a[][30] = { "22222222222", "11111111111", "bbbbbbbbbbb", "aaaaaaaaaaaa" };
二维数组的初始化:
int a[4][5]; //第一种方式;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
a[i][j] = i * j;
}
}
int a[4][5] = { //即表示有4个一维数组,每个数组里有5个元素;
{10,20,30,40,50},
{11,21,31,41,51},
{12,22,32,42,52},
{13,23,33,43,53}
}; //等价于a[4][5]={10,20,30,40,50,11,21,31,41,51,……43,53};
printf("a[2][2]=%d\n", a[2][2]);
printf("*(a[i]+j)=%d\n",*(a[2]+2)); //第i行的前进j个元素;
printf("*(*(a+i)+j)=%d\n",*(*(a+2)+2)); //先前进到第i行,再前进到第j个元素;
printf("(*(a+i))[j]=%d\n",(*(a+2))[2]);
printf("*(&a[0][0]+i*5+j)=%d\n", *(&a[0][0] + 2 * 5 + 2));
引用形式
先定义数组:a[4][5]
- a[i][j]
- *(a[i]+j) //在第i+1个一维数组基础上,再走j个位置;
- *(*(a+i)+j)
- (*(a+i))[j]
- *(&a[0][0]+i*5+j)
若定义:int * p[4](指针的数组), m ;
for(m=0; m<4;m++) p[m] = a[m] ;
- p[i][j]
- *(p[i]+j)
- *(*(p+i)+j)
- (*(p+i))[j] //请与⑴-⑷对比
若定义 int (*q)[5](数组的指针); q=a ;
- q[i][j]
- *(q[i]+j)
- *(*(q+i)+j)
- (*(q+i))[j]
二维数组的a和&a辨析
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
//初识化方式1,
int a1[3][4] = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
//初始化方式2,等价于方式1
int a2[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
//初始化方式3,等价于方式1和2
int a3[][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++) //显示二维数组
{
for (j = 0; j < 4; j++)
{
printf("%d ", a3[i][j]);
}
printf("\n");
}
printf("\n");
//二维数组数组名代表第0行的首地址(区别于第0行首元素地址,虽然值一样)
//区别是它们步长不一样
printf("a:%d, a+1:%d\n", a3, a3 + 1); //步长为:16,因为是首行的所有地址
//a:9435728, a + 1 : 9435744 //地址值
printf("%d, %d\n", *(a3 + 0), *(a3 + 0) + 1); //第0行首元素地址,前进一位
//9435728, 9435732 //地址值
printf("%d, %d\n", a3[0], a3[0] + 1);
//9435728, 9435732 //地址值
//二维数组a中:
// a : 代表第0行首地址
// a+i 等价于 &a[i] : 代表第i行首地址
// *(a+i) 等价于 a[i] : 代表第i行首元素地址
// *(a+i)+j -> &a[i][j]: 第i行第j列元素的地址
// *(*(a+i)+j) -> a[i][j]: 第i行第j列元素的 值
system("pause");
}
二维数组作参数传递
两种形式:
- void MyFunction(char **a, int n);
- void MyFunction(char a[][30], int n);
二维数组在函数间传递
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void print_array_err(char **a, int n) //方式一
{
printf("a: %d, a+1:%d\n", a, a + 1);
int i = 0;
for (i = 0; i < n; i++)
{
//printf("%s\n", a[i]); //首行地址,和首行首元素地址的值是一样
// a+i, *(a+i)
}
}
void print_array(char a[][30], int n) //方式二
{
//printf("a: %d, a+1:%d\n", a, a + 1);
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s, ", a[i]); //首行地址,和首行首元素地址的值是一样
}
printf("\n");
}
void sort_array(char a[][30], int n)
{
int i = 0;
int j = 0;
char tmp[30];
for (i = 0; i < n - 1; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(a[i], a[j])> 0)
{
//交换的内存块
strcpy(tmp, a[i]);
strcpy(a[i], a[j]);
strcpy(a[j], tmp);
}
}
}
}
int main()
{
char a[][30] = { "22222222222", "11111111111", "bbbbbbbbbbb", "aaaaaaaaaaaa" };
int n = sizeof(a) / sizeof(a[0]);
printf("before sort:\n");
print_array(a, n);
sort_array(a, n);
printf("\nafter sort:\n");
print_array(a, n);
printf("\n");
system("pause");
return 0;
}
二维动态数组
常规的二维数组申明及使用
void NomalDoubleArray(int a[][5]) //注意二维数组在参数传递中的格式;
{
for (i = 0; i<2; i++)
{
for (j = 0; j<5; j++)
{
a[i][j] = i * j;
printf("%2d", a[i][j]);
}
putchar('\n');
}
}
int main()
{
int a[2][5]; //申明一个二维数组;
NomalDoubleArray(a);
return 0;
}
牛逼的指针形式二维动态数组的使用方式:
a.声明:int **a;
b.给内存:a=(int **)malloc(二维数的行数 * seizeof(int*));
for(i=0;i<二维数组的行数;i++)
a[i]=(int *)malloc(每行有多少个元素 * sizeof(int));
c.即可以如常规的二维数组使用;
最原始实现
动态的二维数组(最原始实现)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h> //必不可少头文件
#include <stdlib.h> //必不可少头文件
void GetRandomNumber(int** a, int n)
{
srand((unsigned int)time(NULL));
int i, j;
for (i = 0; i < n; i++)
{
a[i] = (int*)malloc(sizeof(int) * n); //表明每行有n个元素位置
for (j = 0; j < n; j++)
{
a[i][j] = rand()%100;
}
}
}
void show(int** a, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
printf("%d ", a[i][j]);
}
printf("\n第%d行完毕!\n", i+1);
}
}
int main()
{
int n = 100;
int** a; //申明一个二维数组指针、二维指针变量
a = (int**)malloc(sizeof(int*) * n); //表明二维数组总共有n行
GetRandomNumber(a, n);
show(a, n);
return 0;
}
逻辑严密
二维动态数组要逻辑严密地使用
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void InitArray(char** a, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
a[i] = (char*)malloc(sizeof(char) * n);
if (a[i] == NULL) //如果没有判空操作,上一行代码则会发出警告:“取消对NULL指针的引用”
{
return;
}
for (j = 0; j < n; j++)
{
a[i][j] = 'a';
}
}
}
void ShowArray(char** a, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
printf("%c ", a[i][j]);
}
printf("\n一行输出完了\n");
}
}
int main()
{
char** a;
int n = 100;
a = (char**)malloc(sizeof(char*) * n); //这里千万不能写为” sizeof(char) * n”!!!
InitArray(a, n);
ShowArray(a, n);
return 0;
}
释放内存
释放二维动态数组内存
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void InitArray(char** a, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
a[i] = (char*)malloc(sizeof(char) * n);
if (a[i] == NULL) //如果没有判空操作,上一行代码则会发出警告:“取消对NULL指针的引用”
{
return;
}
for (j = 0; j < n; j++)
{
a[i][j] = 'a';
}
}
}
void ReleaseArray(char** a, int n)
{
int i;
for (i = 0; i < n; i++)
{
free(*(a + i));
}
}
void ShowArray(char** a, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
printf("%c ", a[i][j]);
}
printf("\n一行输出完了\n");
}
}
int main()
{
char** a;
int n = 100;
a = (char**)malloc(sizeof(char*) * n);
InitArray(a, n);
ShowArray(a, n);
ReleaseArray(a, n);
return 0;
}
正常返回:
区分行列
区分行列的动态二维数组
#include <stdio.h>
#include <stdlib.h>
int main()
{
int** a;
int i, j;
int row, column; //行,列
row = 2; column = 3;
/*
2行3列为:
第一行第一列 第一行第二列
第二行第一列 第二行第二列
*/
a = (int**)malloc(sizeof(int*) * row);
for (i = 0; i < row; i++)
{
a[i] = (int*)malloc(sizeof(int) * column);
for (j = 0; j < column; j++)
{
a[i][j] = i * j;
}
}
for (i = 0; i < row; i++)
{
for (j = 0; j < column; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
return 0;
}
/*
输出:
0 0 0
0 1 2
*/
三维数组
初始化方式1:
int a[5][5][3]; //a[x][y][z]
int x, y, z;
int count = 1;
//注意xyz的位置,根据xyz的顺序,决定for的层次;
//顺序为xyz,循环层次为zyx;画个三维图理解一下;
for (z = 0; z < 3; z++)
{
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
a[x][y][z] = count++;
}
}
}
for (z = 0; z < 3; z++) //这个改变x、y、z的位置可以改变输出方式!
{
printf("\n第%d维度,即z=%d\n", z + 1, z);
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
printf("%d ",a[x][y][z]);
}
putchar('\n');
}
}
初始化方式2:
step1:
step2:
step3:
/*二维数组:a[2][3],由其本质,得:
a[0]: {x,x,x,}
a[1]: {x,x,x,}
*/
int a[5][5][3] = {
{ { 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 } },
{ { 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 } },
{ { 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 } },
{ { 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 } },
{ { 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 },{ 1,2,3 } },
};
int x, y, z;
for (z = 0; z < 3; z++) //这个改变x、y、z的位置可以改变输出方式!
{
printf("\n第%d维度,即z=%d\n", z + 1, z);
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
printf("%d ",a[x][y][z]);
}
putchar('\n');
}
}
数组之间的相互转化
二维数组转三维数组
二维数组
三维数组
转换原则:把一维数组以3个为单位、由睡着的扳成直立的,即为三维数组;
三维数组的xyz相互转化
设定三位数组为a[x][y][z];
例如:把a[500][500][3]转化为a[3][500][500];
int a[5][5][3]; //a[x][y][z]
int b[3][5][5];
int x, y, z;
int count = 1;
for (z = 0; z < 3; z++)
{
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
a[x][y][z] = count++;
}
}
}
printf("\n改变前的数组样式:-------\n");
for (z = 0; z < 3; z++)
{
printf("\n第%d维度,即z=%d\n", z + 1, z);
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
printf("%3d", a[x][y][z]);
}
putchar('\n');
}
}
printf("\n改变后的数组样式:-------\n");
for (z = 0; z < 3; z++)
{
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
b[z][x][y]=a[x][y][z]; //和输出的方式一样,只是xyz的地位变了;
}
}
}
for (y = 0; y < 5; y++)//这个改变x、y、z的位置可以改变输出方式!
{
printf("\n第%d维度,即z=%d\n", y + 1, y);
for (x = 0; x < 5; x++)
{
for (z = 0; z < 3; z++)
{
printf("%3d", b[z][x][y]);
}
putchar('\n');
}
}