是什么?有什么用?用在什么地方?(理解)
养成良好的编程习惯;
培养编程思想;
写代码之前应该先分析需求,分析完需求再开始写代码;(写注释)
1.数组的定义
数组的定义格式:
数据类型 变量名称;
数据类型 数组名称[数据的个数];
元素类型 数组名称[元素个数];
元素类型: 就是数组中需要存储的数据类型, 一旦指定, 数组中就只能存储该类型的数据
数据类型 变量名称;
数据类型 数组名称[数据的个数];
元素类型 数组名称[元素个数];
元素类型: 就是数组中需要存储的数据类型, 一旦指定, 数组中就只能存储该类型的数据
元素个数: 就是数组中能够存储的数据(元素)的个数
int scores[3];
// 定义了一个名称叫做scores的数组, 数组中可以存放3个int类型的数据
scores = 12; // 系统搞不清楚应该赋值给谁
只要定义一个C语言的数组,
系统就自动会给数组中的每一块小得存储空间一个编号
这个编号从0开始, 依次递增
数组中系统自动绑定的编号, 我们称之为 索引
3.数组的初始化
需求保持全班101个人的分数
定义数组:
元素类型 数组名称[元素个数];
/*
// 先定义再初始化
int scores[5];
scores[0] = 99;
scores[1] = 88;
scores[2] = 77;
scores[3] = 66;
scores[4] = 100;
// 先定义再初始化
int scores[5];
scores[0] = 99;
scores[1] = 88;
scores[2] = 77;
scores[3] = 66;
scores[4] = 100;
*/
// 依次将{}中的每一个值赋值给数组中的每一个元素
// 并且从0开始赋值
// 也称之为数组的初始化(完全初始化)
int scores[5] = {99,88,77,66,100};
// 部分初始化
// 默认从0开始初始化, 依次赋值
// 注意: 如果"在部分初始化中"对应的内存没有被初始化, 那么默认是0
int scores1[3] = {11, 22};
printf("0 = %i\n", scores1[0]);
printf("1 = %i\n", scores1[1]);
printf("2 = %i\n", scores1[2]);
// 并且从0开始赋值
// 也称之为数组的初始化(完全初始化)
int scores[5] = {99,88,77,66,100};
// 部分初始化
// 默认从0开始初始化, 依次赋值
// 注意: 如果"在部分初始化中"对应的内存没有被初始化, 那么默认是0
int scores1[3] = {11, 22};
printf("0 = %i\n", scores1[0]);
printf("1 = %i\n", scores1[1]);
printf("2 = %i\n", scores1[2]);
⚠️数组注意事项
在定义数组的时候[]里面只能写整型常量或者是返回整型常量的表达式
⚠️ 注意: 如果没有对数组进行初始化(完全和部分), 那么不要随便使用数组中的数据, 可能是一段垃圾数据(随机值)
⚠️ 注意: 定义数组的时候, 数组的元素个数不能使用变量, 如果使用变量, 那么数组中是一些随机值
⚠️注意: 不建议使用变量定义数组, 如果使用了变量定义数组, 作为数组的元素个数, 不初始化的情况下是随机值, 如果初始化会直接报错
⚠️注意: 如果定义的同时进行初始化, 那么元素的个数可以省略
省略之后, 初始化赋值几个数据, 那么数组的长度就是几. 也就是说数组将来就能存储几个数据
int scores5[] = {1, 3};
printf("0 = %i\n", scores5[0]);
printf("1 = %i\n", scores5[1]);
printf("-------\n");
printf("0 = %i\n", scores5[0]);
printf("1 = %i\n", scores5[1]);
printf("-------\n");
⚠️注意; 如果定义数组时没有进行初始化, 那么不能省略元素个数
可以通过[索引] = 的方式, 给指定索引的元素赋值
int socres7[101] = {[99] = 1, [100] = 3};
printf("3 = %i\n", socres7[99]);
printf("4 = %i\n", socres7[100]);
printf("3 = %i\n", socres7[99]);
printf("4 = %i\n", socres7[100]);
⚠️注意: 只能在定义的同时利用{}进行初始化, 如果是先定义那么就不能使用{}进行初始化
如果先定义那么就不能再进行整体赋值, 只能单个赋值
4.数组的遍历
取出数组中所有的值, 称之为遍历
⚠️注意: 在遍历数组的时候, 尽量不要把遍历的次数写死
遍历多少次应该由数组来决定, 也就是说遍历多少次应该通过数组计算得出
printf("scores = %lu\n", sizeof(scores));
// 计算出数组占用的总字节数
printf("scores[0] = %lu\n", sizeof(scores[0]));
// 计算出数组中某一个元素占用的字节数
printf("一个有多少个元素 : %lu\n",
sizeof(scores) / sizeof(scores[0]));
// 动态计算数组的元素个数
int length = sizeof(scores) / sizeof(scores[0]);
// 数组的遍历
for (int i = 0; i < length; i++) {
printf("scores[%i] = %i\n", i,scores[i]);
}
printf("scores[%i] = %i\n", i,scores[i]);
}
return 0;
5.数组的内存分配
1.数组内部存储细节
存储方式:
1)计算机会给数组分配一块连续的存储空间
2)数组名代表数组的首地址,从首地址位置,依次存入数组的第1个、第2个....、第n个元素
3)每个元素占用相同的字节数(取决于数组类型)
4)并且数组中元素之间的地址是连续
![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150517_3.png)
⚠️
注意:字符在内存中是以对应ASCII值的二进制形式存储的,而非上表的形式。
在这个例子中,数组x的地址为它的首元素的地址0x08,数组ca的地址为0x03
2.数组的地址
- 在内存中,内存从大到小进行寻址,为数组分配了存储空间后,数组的元素自然的从上往下排列 存储,整个数组的地址为首元素的地址。
![](http://7xj0kx.com1.z0.glb.clouddn.com/sznc.png)
数组a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc5
因此a == &a[0],即第一个元素的地址就是整个数组的地址
3.数组的越界问题
数组越界导致的问题
约错对象
程序崩溃
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:这句访问到了不属于cs1的内存
printf("cs1[0] = %d\n", cs1[0] );
输出结果: 88
![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150517_6.png)
6.数组与函数
基本数据类型作为函数的参数是值传递
如果形参是基本数据类型, 在函数中修改形参的值不会影响到实参的值
⚠️注意1: 数组名作为函数的参数传递, 是传递的数组的地址
因为数组名就是数组的地址 &number = &number[0] == number
⚠️注意2: 如果数组作为函数的形参, 元素的个数可以省略
如果形参是数组, 那么在函数中修改形参的值, 会影响到实参的值
如果传递的数组的名称, 其实传递的是地址
如果传递的是地址, 其实传递的是指针
指针在64位编译环境占8个字节
⚠️注意: 如果数组作为形参, 那么在函数中就不能通过数组的名称计算出数组元素的个数
因为系统会自动将数组形参转换为指针, 指针占用8个字节
数组的一些练习的函数封装:
选择排序、
冒泡排序
// 遍历数组
void printArray(int nums[], int length)
{
for (int i = 0; i < length; i++) {
printf("排序后:nums[%i] = %i\n", i, nums[i]);
}
}
//选择排序
void selectSort(int nums[], int length)
{
for (int i = 0; i < length - 1; i++) {
for (int j = i + 1; j < length; j++) {
if (nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
// 输出选择排序后的排列
printArray(nums,length);
}
//冒泡排序
void bubbleSort(int nums[], int length)
{
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
// 输出冒泡排序后的排列
printArray(nums, length);
void printArray(int nums[], int length)
{
for (int i = 0; i < length; i++) {
printf("排序后:nums[%i] = %i\n", i, nums[i]);
}
}
//选择排序
void selectSort(int nums[], int length)
{
for (int i = 0; i < length - 1; i++) {
for (int j = i + 1; j < length; j++) {
if (nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
// 输出选择排序后的排列
printArray(nums,length);
}
//冒泡排序
void bubbleSort(int nums[], int length)
{
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
// 输出冒泡排序后的排列
printArray(nums, length);
}
折半查找
// 遍历数组
int findKye(int nums[], int length, int value)
{
for (int i = 0; i < length; i++) {
if (nums[i] == value) {
return i;
}
}
return -1;
}
//折半查找
int findKey2(int nums[], int lengh, int value)
{
// 定义两个变量分别来记录下标最大和最小值
int max = lengh - 1;
int min = 0;
// for循环遍历数组
for (int i ; i < lengh; i++) {
// 定义一个变量来记录下标中间值
int mid = (max + min) / 2;
// 判断要查找的值和中位值比较大小
// 要查找的值比中位值大,则中位变最大位并-1
if (nums[min] > value) {
max = mid - 1;
}
// 要查找的值比中位值小,则中位变最小位并+1
else if (nums[min] < value)
{
min = mid + 1;
}
// 相等则返回下标
else{
return mid;
}
}
// 要查找的值不存在便返回-1
return -1;
int findKye(int nums[], int length, int value)
{
for (int i = 0; i < length; i++) {
if (nums[i] == value) {
return i;
}
}
return -1;
}
//折半查找
int findKey2(int nums[], int lengh, int value)
{
// 定义两个变量分别来记录下标最大和最小值
int max = lengh - 1;
int min = 0;
// for循环遍历数组
for (int i ; i < lengh; i++) {
// 定义一个变量来记录下标中间值
int mid = (max + min) / 2;
// 判断要查找的值和中位值比较大小
// 要查找的值比中位值大,则中位变最大位并-1
if (nums[min] > value) {
max = mid - 1;
}
// 要查找的值比中位值小,则中位变最小位并+1
else if (nums[min] < value)
{
min = mid + 1;
}
// 相等则返回下标
else{
return mid;
}
}
// 要查找的值不存在便返回-1
return -1;
}
进制查表法
void total(int value, int base, int offset)
{
// 1.定义一个数组, 用于保存十六进制中所有的取值
char charValues[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// 2.定义一个数组, 用于保存查询后的结果
char results[32] = {'0'};
// 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引
int pos = sizeof(results)/ sizeof(results[0]);
while (value != 0) {
// 1.取出1位的值
int res = value & base;// 1 7 15
// 2.利用取出来得值到表中查询对应的结果
char c = charValues[res];
// 3.存储查询的结果
results[--pos] = c;
// 4.移除二进制被取过的1位
value = value >> offset;// 1 3 4
}
// 4.打印结果
for (int i = pos; i < 32; i++) {
printf("%c", results[i]);
}
printf("\n");
{
// 1.定义一个数组, 用于保存十六进制中所有的取值
char charValues[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// 2.定义一个数组, 用于保存查询后的结果
char results[32] = {'0'};
// 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引
int pos = sizeof(results)/ sizeof(results[0]);
while (value != 0) {
// 1.取出1位的值
int res = value & base;// 1 7 15
// 2.利用取出来得值到表中查询对应的结果
char c = charValues[res];
// 3.存储查询的结果
results[--pos] = c;
// 4.移除二进制被取过的1位
value = value >> offset;// 1 3 4
}
// 4.打印结果
for (int i = pos; i < 32; i++) {
printf("%c", results[i]);
}
printf("\n");
}