C语言初阶最详细的数组知识总结,超多干货!!(带三大应用实例)
Posted Do
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言初阶最详细的数组知识总结,超多干货!!(带三大应用实例)相关的知识,希望对你有一定的参考价值。
目录
数组
本篇文章干货满满,细节超多,堪称精华版,博主花了很长时间给大家整理和搜集的,绝对有你想知道又不了解的知识!!赶紧来看看吧,看完别忘记一键三连哦
一维数组的创建和初始化
数组的创建
数组是一组相同类型元素的集合。 数组的创建方式:
type_t arr_name [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
数组创建的实例:
//代码1
int arr1[10];
//代码2
int count = 10;
int arr2[count];//数组可以正常创建?
//代码3
char arr3[10];
float arr4[1]
注:上面代码2块是不行的,因为数组中[ ]应是常量,而不是变量。
数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。 看代码:
int arr1[10] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a',98, 'c'};
char arr5[] = {'a','b','c'};//等价于char arr5[3]={'a','b','c'}
char arr6[] = "abcdef";
数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
但是对于下面的代码要区分内存中如何分配,并且要知道字符串长度和数组大小分别是多少:
char arr1[3] = {'a','b','c'};//三个字符,'a','b','c'
char arr2[5]="abc";//5个字符:a b c 0 0
char arr3[] = "abc";//四个字符:a b c \\0
char arr4[]={'a','b','c'};//三个字符:a b c
请看结果:
由此结果我们可以得出一些结论:
- 没有声明数组大小和声明数组大小,内存存储空间是完全不一样的,打印出来的结果也完全不同,字符串数组本身存在‘\\0’,所以即使没有声明数组大小也可以成功打印出来,但是对于字符数组而言,如果没有声明数组大小,并且字符中没有‘\\0’时,屏幕会回显除字符以外的随机字符,因为'\\0'始终是字符串的结束标志。
- strlen是求字符串长度的,求长度时'\\0'只是内容,但是不包括在内,所以arr3的有效长度为3,;但是对于arr4字符数组来说,即没有声明数组大小,也没有'\\0'作为结束标志,所以arr4的长度也是随机的。
- sizeof是求数组大小的,求大小时'\\0'包括在内,所以arr3的大小为4;而arr4的大小就是数组的大小而不是字符串大小,所以为3。
一维数组的使用
对于数组的使用我们之前介绍了一个操作符: [ ] ,下标引用操作符。它其实就数组访问的操作符。 我们来看代码:
#include <stdio.h>
int main()
{
int arr[10] = {0};//数组的不完全初始化
//计算数组的元素个数
int sz = sizeof(arr)/sizeof(arr[0]);
//对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:
int i = 0;//做下标
for(i=0; i<10; i++)
{
arr[i] = i;
}
//输出数组的内容
for(i=0; i<10; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
总结:
1. 数组是使用下标来访问的,下标是从0开始。
2. 数组的大小可以通过计算得到。
一维数组在内存中的存储
对于知道数组在内存中是怎么存储的,这能很好的帮助我们理解数组以及指针,下面通过代码来解释:
#include <stdio.h>
int main()
{
int arr[10] = {0};
int i = 0;
for(i=0; i<sizeof(arr)/sizeof(arr[0]); ++i)
{
printf("&arr[%d] = %p\\n", i, &arr[i]);
}
return 0;
}
揭晓打印结果:
结论:
整型数组在内存中是四个字节。
数组在内存中是连续存放的,随着数组下标的增长,元素的地址由低到高变化。
一维数组的指针访问
代码示例1:
#include<stdio.h>
int main()
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
printf("%p\\n", arr);
printf("%d\\n", *arr);
return 0;
}
结论:
这里的*arr为解引用操作,*arr为首元素。
数组名其实存放的就是首元素的地址
代码示例2:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *p);
p++;
}
return 0;
}
结论:
我们定义了一个指针p,指向arr,然后我们通过指针来访问数组。
p++为指针的算法,因为是整型指针,所以p++是跳过四个字节,到下一个数字。
二维数组的创建和初始化
二维数组的创建
//数组创建
int arr[3][4];
char arr[3][5];
double arr[2][4];
二维数组的初始化
//数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};
注:使用二维数组时,行下标可以省但是列下标不可以省。
二维数组的使用
二维数组的使用和一维数组一样也是通过下标的方式。 看代码:
#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
arr[i][j] = i * 4 + j;
}
}
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
}
return 0;
}
运行结果:
二维数组在内存中的存储
像一维数组一样,这里我们尝试打印二维数组的每个元素:
#include <stdio.h>
int main()
{
int arr[3][4];
int i = 0;
for(i=0; i<3; i++)
{
int j = 0;
for(j=0; j<4; j++)
{
printf("&arr[%d][%d] = %p\\n", i, j,&arr[i][j]);
}
}
return 0;
}
内存展示:
结论:
其实二维数组在内存中也是连续存储的。
随着数组下标的增长,元素的地址也是由低到高变化。
二维数组的指针访问
我们知道了一维数组的内存存储模式之后,我们尝试使用指针对一维数组进行访问:
#include<stdio.h>
int main()
{
int arr[3][5] = { 0 };
int *p = &arr[0][0];
int i = 0;
int j = 0;
for (i = 0; i < 15; i++)
{
*(p + i) = i + 1;
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
}
return 0;
}
当然指针也是有类型的:
#include<stdio.h>
int main()
{
int num = 0x11223344;
int *p = #
*p = 0;
return 0;
}
这里的指针p是int型,所以我们对它的改变,是改变4个字节的大小,同样的,如果我们对p进行加1操作,指针向后跳4个字节。
#include<stdio.h>
int main()
{
int num = 0x11223344;
//int *p = #
//*p = 0;
char *pc = #
*pc = 0;
return 0;
}
我们可以从内存中观察到,只改变了1个字节的大小,因为pc指针的类型是char。如果我们对pc进行加1操作,指针向后跳1个字节。
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\\n", &arr[0]);
printf("%p\\n", &arr[0] + 1);
printf("---------------\\n");
printf("%p\\n", arr);
printf("%p\\n", arr + 1);
printf("---------------\\n");
printf("%p\\n", &arr);
printf("%p\\n", &arr + 1);
return 0;
}
我们可以从输出结果看出&arr[0],arr,&arr它们的地址虽然相同,但它们的意义是不同的,&arr,取的是数组的地址,对它进行加1,就相当于跳过整个数组,又因为数组是int型,总共有10个元素,所以跳过的字节大小是40。
有关数组的运算
关于数组我们必须要学会有关数组的一些运算:
#include<stdio.h>
int main()
{
//一维数组
int a[] = { 1, 2, 3, 4 };
printf("%d\\n", sizeof(a));//16
//1.数组名单独放在sizeof内部,数组名表示整个数组,所以sizeof(数组名)计算的是是数组总大小,单位是字节
//2.&数组名,数组名表示整个数组,所以&数组名取出的是整个数组的地址
//3.除此之外,所有的数组名都表示首元素的地址
printf("%d\\n", sizeof(a + 0));//4 a代表首元素地址,a+i代表第i个元素的地址,在32位平台下所有的地址的大小都是4个字节
printf("%d\\n", sizeof(*a));//4 a是首元素地址,*a是首元素--1,int型占4个字节大小
printf("%d\\n", sizeof(a + 1));//4 a是首元素地址,a+1是第二个元素的地址,它还是一个地址
printf("%d\\n", sizeof(a[1]));//4 a[1]--第二个元素
printf("%d\\n", sizeof(&a));//4 &a虽然取出的是整个数组的地址,但它还是一个地址
printf("%d\\n", sizeof(*&a));//16 &a取出的是整个数组的地址,对它进行解引用,就是这个数组,这个数字的大小就是16
printf("%d\\n", sizeof(&a + 1));//4 &a取出的是整个数组的地址,加1跳过了整个数组(16个字节),但它还是一个地址
printf("%d\\n", sizeof(&a[0]));//4 &a[0]取的是第一个元素的地址
printf("%d\\n", sizeof(&a[0] + 1));//4 &a[0] + 1取的是第二个元素的地址
return 0;
}
#include<stdio.h>
int main()
{
//字符数组
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\\n", sizeof(arr));//6
printf("%d\\n", sizeof(arr + 0));//4 首元素地址
printf("%d\\n", sizeof(*arr));//1 首元素地址解引用是首元素(a),char类型占1个字节
printf("%d\\n", sizeof(arr[1]));//1 首元素
printf("%d\\n", sizeof(&arr));//4 数组的地址
printf("%d\\n", sizeof(&arr + 1));//4 下一个数组的地址,跳过了f
printf("%d\\n", sizeof(&arr[0] + 1));//4 第二个元素的地址
printf("%d\\n", strlen(arr));//随机值 strlen()求的是字符串长度,以'\\0'为结束标志,这里并没有'\\0',所以会一直往后数
printf("%d\\n", strlen(arr + 0));//随机值 还是从'a'开始数,但没有'\\0',所以停不下来
printf("%d\\n", strlen(*arr));//程序会崩掉 strlen()接收的是一个地址,*arr是字符'a',这里把'a'的ASCII码值(97)作为一个地址访问,这一块的地址是不能被访问的
printf("%d\\n", strlen(arr[1]));//错误 传的是'b',和传的是'a'效果一样
printf("%d\\n", strlen(&arr));//随机值 &arr虽然取的是数组的地址,但数组的地址和数组首元素的地址是一样的,也是从‘a'开始数,但并没有'\\0'
printf("%d\\n", strlen(&arr + 1));//随机值 但这个随机值和前边的随机值意义不同,它是把'a','b','c','d','e','f'跳过去了,从f后边开始数
printf("%d\\n", strlen(&arr[0] +1));//随机值 这个是从'b'开始往后数的
return 0;
}
#include<stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%d\\n", sizeof(arr));//7 里边还有'\\0',只不过我们看不到而已
printf("%d\\n", sizeof(arr + 0));//4 arr+0---首元素地址
printf("%d\\n", sizeof(*arr));//1 对首元素地址解引用是首元素
printf("%d\\n", sizeof(arr[1]));//1 第二个元素
printf("%d\\n", sizeof(&arr));//4 数组的地址也是地址
printf("%d\\n", sizeof(&arr + 1));//4 也是一个地址,不过这个地址在'\\0'后边,跳过了整个数组
printf("%d\\n", sizeof(&arr[0] + 1));//4 从b开始的一个地址
printf("%d\\n", strlen(arr));//6 strlen()以'\\0'为结束标志,但不算'\\0'
printf("%d\\n", strlen(arr + 0));//6 arr+0与arr都代表首元素地址
printf("%d\\n", strlen(*arr));//错误 这传进来的不是一个地址,而是一个字符
printf("%d\\n", strlen(arr[1]));//错误
printf("%d\\n", strlen(&arr));//6 数组的地址也是首元素地址,地址的位置是一样的
printf("%d\\n", strlen(&arr + 1));//随机值 跳过了'\\0',从'\\0'往后数,不知道会数到哪里去
printf("%d\\n", strlen(&arr[0] + 1));//5 从第二个元素(b)开始往后数,遇到'\\0'结束
return 0;
}
#include<stdio.h>
int main()
{
char *p = "abcdef";
printf("%d\\n", sizeof(p));//4 p是指针变量,里边存的是a的地址
printf("%d\\n", sizeof(p + 1));//4 还是一个地址,不过是指向了b的地址
printf("%d\\n", sizeof(*p));//1 对a的地址解引用就是a
printf("%d\\n", sizeof(p[0]));//1 第一个元素(a)
printf("%d\\n", sizeof(&p));//4 &p取的是p的地址,p是一个指针,指向a的地址,但p的地址是什么并不知道
printf("%d\\n", sizeof(&p + 1));//4 &p+1--跳过了p的一个地址
printf("%d\\n", sizeof(&p[0] + 1));//4 还是一个地址,这个地址指向了b的地址
printf("%d\\n", strlen(p));//6 从a开始向后数
printf("%d\\n", strlen(p + 1));//5 从b开始向后数
printf("%d\\n", strlen(*p));//错误 *p就是a,strlen()要的是一个地址,而不是a的ASCII码值(97)
printf("%d\\n", strlen(p[0]));//错误
printf("%d\\n", strlen(&p));//随机值
printf("%d\\n", strlen(&p + 1));//随机值
printf("%d\\n", strlen(&p[0] + 1));//5 从b开始往后数
return 0;
}
#include<stdio.h>
//二维数组
int main()
{
int a[3][4] = { 0 };
printf("%d\\n", sizeof(a));//48 整个数组有12个元素,每个元素都是int型
printf("%d\\n", sizeof(a[0][0]));//4 代表的是第一行第一列那个元素
printf("%d\\n", sizeof(a[0]));//16 a[0]--第一行数组名,第一行总共有4个元素
printf("%d\\n", sizeof(a[0] + 1));//4 a[0]降级变为a[0][0]的地址,a[0]+1是a[0][1]的地址
printf("%d\\n", sizeof(a + 1));//4 a--首元素(第一行)地址,a+1--第二行地址
printf("%d\\n", sizeof(&a[0] + 1));//4 第二行地址
printf("%d\\n", sizeof(*a));//16 对第一行地址解引用就是第一行元素
printf("%d\\n", sizeof(a[3]));//16 这里有好多人会出错,认为这个数组并没有这么大,只有3行,不能访问第4行,其实这里并没有访问第4行,它只是一个类型(1行的大小)
return 0;
}
这些运算都有很详细的解析,大家仔细看,会有很大的收获。
数组作为函数参数
往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序函数将一个整形数组排序。 那我们将会这样使用该函数
数组的应用实例1:冒泡排序
冒泡排序函数的错误设计
#include <stdio.h>
void bubble_sort(int arr[])
{
int sz = sizeof(arr)/sizeof(arr[0]);//这样对吗?
int i = 0;
for(i=0; i<sz-1; i++)
{
int j = 0;
for(j=0; j<sz-i-1; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
int main()
{
int arr[] = {3,1,7,5,8,9,0,2,4,6};
bubble_sort(arr);
for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
return 0;
}
很显然这样是不对的,sz代表的是数组元素的个数,应该是10,但是这里求出来会是1,那么为什么呢?因为sz应该在函数外部求,再通过调用函数传参过去,那么正确的冒泡排序应该是怎么样的呢?
首先我们要搞明白数组作为参数传递的时候,传递的是首元素地址还是整个数组的地址?
数组名是什么?
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5};
printf("%p\\n", arr);
printf("%p\\n", &arr[0]);
printf("%d\\n", *arr);
return 0;
}
结论:
数组名是数组首元素的地址。
但有两个例外:
1. sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
2. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组
除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。
所以冒泡函数的正确设计应该是这样的:
#include<stdio.h>
void bubble_sort(int arr[], int sz) //编写函数冒泡排序数组
{
int i = 0;
int temp = 0;
for (i = 0; i < sz - 1; i++) //确定排序的趟数
{
int j = 0;
int flag = 1; //假设这一趟排序的数列已经有序
for (j = 0; j < sz - 1 - i; j++) //每一趟冒泡排序两两交换的次数
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = 0; //本趟排序其实不完全有序
}
}
if (flag == 1) //不用排序了
{
break;
}
}
}
int main()
{
int i = 0;
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组大小;
bubble_sort(arr, sz); //这里的arr指的是首元素地址;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
数组的应用实例2:三子棋
已经为大家准备好了,参考:https://blog.csdn.net/weixin_55124128/article/details/116195494
数组的应用实例3:扫雷游戏
这个也给大家准备好了,见参考:https://blog.csdn.net/weixin_55124128/article/details/116356115
总结
数组和指针紧密联系,所以学好数组对大家学指针有很好的帮助,本篇涉及了一维数组二维数组的创建和初始化,运用,存储,指针访问,以及数组的运算和三个应用实例,我从中受益匪浅,相信对大家有不少帮助。还请大家多多支持我,给我个一键三连,谢谢大家!!
以上是关于C语言初阶最详细的数组知识总结,超多干货!!(带三大应用实例)的主要内容,如果未能解决你的问题,请参考以下文章