【前言】本系列(初阶)适用于初学者课前预习或者课后复习资料,包含了大部分基础知识点梳理与代码例方便看官理解,有问题请指出。本人邮箱地址:p772307283@outlook.com
可爱捏
1.数组的创建与初始化
1.1数组创建
数组的定义非常简单,就是一组相同类型的元素的集合。
其创建的方式为
type arrname[const n];
//type是数组的元素类型
//arrname是数组名
//const n是常量表达式,用以指定数组大小
需要知道的是,在C99之前,[]中必须给出常量,但C99支持了变长数组,数组大小可由变量指定,但变长数组不可以初始化。
1.2数组初始化
初始化的意思就是在创建数组时对数组内容给出合理的初始值。
int arr1[10] = { 1,2,3 };
这被称为不完全初始化。后面的元素被默认为了0(可通过监视窗口查看)。
int arr2[] = { 1,2,3 };
数组中未给定const n,这是合法的。系统根据后面的元素个数来确定const n
int arr3[3] = { 1,2,3 };
所有要素都具有的数组。
char数组也是同理,但是char有几个需要注意的地方。
char数组的内存是如何分配的呢?
#include<stdio.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a','b','c' };
printf("%s\n", arr1);
printf("%s", arr2);
}
编译运行,arr2的打印不符预期,在abc后面跟上了一大串的“烫烫烫”和其他文字。
这是因为arr2区别于arr1的地方在于,arr1确保了在最后一个字符后跟上了一个'\0',作为字符串的结束方式。而arr2的'\0'具有随机性,你不知道在距离最后一个字符的什么地方才能找到他。
并且了解过栈帧的看官知道,在main的栈帧空间里,会先预填入很多的0xcccch,而0xcccch就是汉字烫的编码。
1.3一维数组使用
数组使用下标引用操作符[]
在这里只需要了解其输入和输出。
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
//输入
for (int i = 0; i < sizeof(arr) / sizeof(arr[1]); i++)
{
arr[i] = i;
}
//输出
for (int i = 0; i < sizeof(arr) / sizeof(arr[1]); i++)
{
printf("%d ", arr[i]);
}
}
其中需要注意的是如何求取整型数组的长度。
通常我们使用sizeof的方式来解决。以整段数组字节大小除以一个小段的字节大小,就能得到数组有多少个小段,也就能得到数组的长度了。
还有一点:数组通过下标进行访问,且开始的下标为0
那么一维数组是如何进行存储的呢?结论是:数组在内存中是连续存放的。
以代码示例来证明
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[1]);
for (int i = 0; i < sz; i++)
{
printf("&arr[%d]=%p\n", i, &arr[i]);
}
return 0;
}
得到的运行结果
&arr[0]=00000099B32FFCA8
&arr[1]=00000099B32FFCAC
&arr[2]=00000099B32FFCB0
&arr[3]=00000099B32FFCB4
&arr[4]=00000099B32FFCB8
显而易见这是十六进制,从8到c,是差4个字节,以此类推,每个小段都占有4个字节,也正好对应了int,其地址也是连续的。
2.二维数组
2.1二维数组的创建与初始化
int arr[3][3] = { 1,2,3 };
这就是一个未完全初始化的二维数组。第一个3代表着有3排,第二个3代表着有3列,所以一共就是9个元素。
对于其初始化需要注意的有一点:行可以省但是列不可以省。
很显然,如果只有行,不知道有多少列,你该如何去存放你的元素?
而只有列的话仍然可以对元素进行正常的存放。
2.2二维数组的使用
二维数组的使用同一维数组并无太大差异。
例如arr[3][3]
其排列为
arr[0][0] arr[0][1] arr[0][2]
arr[1][0] arr[1][1] arr[1][2]
arr[2][0] arr[2][1] arr[2][2]
#include<stdio.h>
int main()
{
int arr[3][3] = { 0 };
//输入
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
arr[i][j] = i + j;
}
}//输出
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("arr[%d][%d]=%d\n", i, j, arr[i][j]);
}
}
return 0;
}
得到结果:
arr[0][0]=0
arr[0][1]=1
arr[0][2]=2
arr[1][0]=1
arr[1][1]=2
arr[1][2]=3
arr[2][0]=2
arr[2][1]=3
arr[2][2]=4
同样的,二维数组的地址也是连续存放的。可以自行验证。
可以将第一排看作第一个一维数组,第二排看作第二个一维数组,第三排看作第三个一维数组,这三个一维数组存放是连续的。
小tip:数据越界
数组的下标具有范围的限制。例如一个元素只有n个元素,但如果下标为n,则是数组越界。
因为下标总是由0开始的。
数组的越界可能编译器无法察觉,所以还请大家在编写代码时小心甄别。
二维数组也存在类似的情况捏。
3.以冒泡排序讲解数组函数传参
冒泡排序是几种经典排序算法中最简单的一种,其思想在于 通过多次循环进行遍历,例如一个长度为10的数组,第一次遍历9次,第二次则遍历8次,直到遍历完整个数组,在遍历的同时对其进行排序。
对这个数组进行升序的操作
第一趟让9与剩下的元素进行比较,最坏的情况下需要比较9次。
9 8 7 6 5 4 3 2 1 0
8 9 7 6 5 4 3 2 1 0
8 7 9 6 5 4 3 2 1 0
。。。。。。
8 7 6 5 4 3 2 1 0 9
这样就让9来到了最终的位置。以此类推接着用同样的方法操作8 7等数字,但10个数字只需要冒泡排序9趟,当9个元素都来到了合适的位置上时,剩下的那一个就已经自动的排在合适的位置了。
那么代码如下:
void bubblesort(int arr[])
{
int sz = sizeof(arr) / sizeof(arr[1]);
for (int i = 0; i < sz-1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 3,6,8,2,1,9,5,4,7,0 };
bubblesort(arr);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
但是你在运行后发现了问题,其数组并未按照预定的想法进行排序。那是哪里出现了问题呢?
对程序进行监视就可以知道,sz的值一直都是1,那么问题就看出来了,sz等于1,后面的循环直接就卡住了。
那么是什么导致了sz的值是1呢?
答案是传参传进来的arr。数组作为参数进行传递时,传进函数会退化成指针。也就是说其实sizeof(arr)计算的并非整个数组的大小,而是arr这个指针的大小,arr时int型的指针,大小为4字节,arr【1】的大小也是4字节,就导致了sz=1了。
#include<stdio.h>
void bubblesort(int arr[],int sz)
{
for (int i = 0; i < sz-1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 3,6,8,2,1,9,5,4,7,0 };
int sz = sizeof(arr) / sizeof(arr[1]);
bubblesort(arr,sz);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
更改完成。
sizeof(数组名),计算的是整个数组的大小。sizeof内部的数组名就代表整个数组。
&数组名,取出了数组的地址,此时的数组名也代表整个数组。
其他情况下,数组名都表示首元素的地址。