概念:
一堆相同类型的数据的有序集合
格式:
元素类型 数组名称[ 元素个数 ]
定义数组:
// 定义了一个名称叫做scores的数组, 数组中可以存放3个int类型的数据 int scores[3]; // 只要定义一个C语言的数组, 系统就自动会给数组中的每一块小得存储空间一个编号 // 这个编号从0开始, 一次递增 // 数组中系统自动绑定的编号, 我们称之为 索引 scores[0] = 12; scores[1] = 66; scores[2] = 59;
数组初始化:
完全初始化:
// 依次将{}中的每一个值赋值给数组中的每一个元素 // 并且从0开始赋值 int scores[5] = {99,88,77,66,100};
部分初始化:
// 默认从0开始初始化, 依次赋值 // 注意: 如果"在部分初始化中"对应的内存没有被初始化, 那么默认是0 int scores1[3] = {11, 22};
注意点:
1.如果没有对数组进行初始化(完全和部门), 那么不要随便使用数组中的数据, 可能是一段垃圾数据(随机值)
2.定义数组的时候, 数组的元素个数不能使用变量 变量作为数组的元素个数, 不初始化的情况下是随机值, 如果初始化会直接报错
3.如果定义的同时进行初始化, 那么元素的个数可以省略, 省略之后, 初始化赋值几个数据, 数组就能存储几个数据,如果定义数组时没有进行初始化, 那么不能省略元素个数
4.可以通过[索引] = 的方式, 给指定索引的元素赋值
int socres7[101] = {[99] = 1, [100] = 3};
5.只能在定义的同时利用{}进行初始化, 如果是先定义那么就不能使用{}进行初始化
int scores8[3]; //scores8 = {1 , 4, 19}; // 如果先定义那么就不能再进行整体赋值, 只能单个赋值 scores8[0] = 1;
数组遍历:
int scores[6] = {1, 23, 44, 66, 71, 88, 99 , 2}; // 动态计算数组的元素个数 int length = sizeof(scores) / sizeof(scores[0]); //length 不要写 6 这样的魔鬼数字 for (int i = 0; i < length; i++) { printf("scores[%i] = %i\n", i,scores[i]); }
数组内存分配:
存储方式:
1)计算机会给数组分配一块连续的存储空间
2)数组名代表数组的首地址,从首地址位置,依次存入数组的第1个、第2个....、第n个元素
3)每个元素占用相同的字节数(取决于数组类型)
4)并且数组中元素之间的地址是连续。
// 变量在内存中的存储 // 由于变量的内存寻址是从大到小, 所以存储数据时会从高字节开始存储 int num = 10; // 0000 0000 0000 0000 0000 0000 0000 1010 /* 00001010 0x7fff5fbff7cc 00000000 0x7fff5fbff7cd 00000000 0x7fff5fbff7ce 00000000 0x7fff5fbff7cf */ // 注意: 数组的存储和变量有点不一样, 数组存储元素, 是从所占用的低字节开始存储 // 数组的元素自然的从上往下排列存储,整个数组的地址为首元素的地址。 char charValues[4] = {'l', 'u', 'c', 'k'}; /* charValues[0] = 0x7fff5fbff7c8 charValues[1] = 0x7fff5fbff7c9 charValues[2] = 0x7fff5fbff7ca charValues[3] = 0x7fff5fbff7cb */ //&charValues == charValue == &charValue[0]
数组越界问题:
#include <stdio.h> int main(int argc, const char * argv[]) { int nums[2] = {1, 5}; int values[3] = {7, 8, 9}; // 注意点: 在使用数组的时候, 一定不要访问不属于字节的存储空间, 这样会导致数据混乱 // 有时候如果访问了不属于自己的存储空间, 程序会报错 values[3] = 44; printf("values[3] = %i\n", values[3]); printf("nums[0] = %i\n", nums[0]); nums[-1] = 88; printf("values[2] = %i\n", values[2]); return 0; } /* 输出结果: values[3] = 44 nums[0] = 44 values[2] = 88 */
下图分析 values[3]访问到了nums[0]的空间 nums[-1] 访问到了 values[2] 的空间
数组练习:
// 从键盘录入当天出售BTC的价格并计算出售的BTC的总价和平均价(比如说一天出售了4个比特币) // 1.1定义数组保存每个比特币的价格 int values[4] = {-1}; // 1.2动态计算数组的元素个数 int length = sizeof(values) / sizeof(values[0]); // 1.3定义变量保存总和 int sum = 0; for (int i = 0; i < length; i++) { printf("请输入第%i个比特币的价格\n", i + 1); scanf("%i", &values[i]); //2.0 计算总和 sum += values[i]; } /* // 2.计算总和 int sum = 0; for (int i = 0; i < length; i++) { sum += values[i]; } */ // 3.计算平局值 int average = sum / length; // 4.输出结果 printf("sum = %i, average = %i\n", sum, average);
数组与函数:
1 数组名作为函数的参数传递, 是传递的数组的地址
因为数组名就是数组的地址 &number = &number[0] == number
2 如果数组作为函数的形参, 元素的个数可以省略
3 如果形参是数组, 那么在函数中修改形参的值, 会影响到实参的值
void change(int values[]) { values[1] = 99; } int main(int argc, const char * argv[]) { int nums[2] = {1, 5}; change(nums); printf("nums[1] = %i\n", nums[1]); return 0; } 输出结果: 99
注意点:
如果传递的数组的名称, 其实传递的是地址 如果传递的是地址, 其实传递的是指针 指针在64位编译环境占8个字节
如果数组作为形参, 那么在函数中就不能通过数组的名称计算出数组元素的个数 因为系统会自动将数组形参转换为指针, 指针占用8个字节 所以只能读取到8个字节的数据
举例:
要求定义一个函数, 实现遍历数组. (只要别人传递数组给函数, 就要求输出数组中所有元素的值)
#include <stdio.h> void printArray(int values[5]); int main(int argc, const char * argv[]) { int nums[3] = {1 , 3 , 5}; printf("size = %i\n", sizeof(nums)); // size = 12 printArray(nums); // 数组名称就是数组的地址 return 0; } void printArray(int values[5]) { printf("size = %i\n", sizeof(values)); // size = 8 // 1.动态计算数组的元素个数 int length = sizeof(values) / sizeof(values[0]); // 2.遍历数组 for (int i = 0; i < length; i++) { printf("values[%i] = %i\n", i,values[i]); // 输出: values[0] = 0 values[1] = 3 } }
改正后(能正确输出数组的所有元素):
#include <stdio.h> void printArray(int values[5], int length); int main(int argc, const char * argv[]) { int nums[3] = {1 , 3 , 5}; printf("size = %lu\n", sizeof(nums)); // size = 12 int length = sizeof(nums) / sizeof(nums[0]); printArray(nums, length); return 0; } void printArray(int values[5], int length) { printf("size = %lu\n", sizeof(values)); // size = 8 // 遍历数组 for (int i = 0; i < length; i++) { printf("values[%i] = %i\n", i,values[i]); // values[0] = 0 values[1] = 3 } }
数组练习:
#include <stdio.h> int main(int argc, const char * argv[]) { // 要求从键盘输入6个0~9的数字,排序后输出 // 1.定义数组保存用户输入的数据 int nums[10] = {0}; // 2.接收用户的数据 int value = -1; for (int i = 0; i < 6; i++) { printf("请输入第%i个数据\n", i + 1); scanf("%i", &value); nums[value] = nums[value] + 1; } for (int i = 0; i < 10; i++) { for (int j = 0; j < nums[i]; j++) { printf("%i\n", i); // 1, 1, 2, 3, 3, 6 } } return 0; } /* 1. nums[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 2. 用户输入的数组当做数组索引 3. 输入一次 索引对应的值 + 1 输入 1, 1, 2, 3, 3, 6之后 数组对应值 : nums[10] = {0, 2, 1, 2, 0, 0, 1, 0, 0, 0}; 下标: 0 1 2 3 4 5 6 7 8 9 4. 然后对数组排序输出 */
数组排序:
冒泡排序
int nums[6] = {99, 12, 88, 34, 5, 7}; int length = sizeof(nums) / sizeof(nums[0]); for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - 1 - i; j++) { if (nums[j] > nums[j + 1]) { int temp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = temp; } } }
选择排序:
int nums[8] = {99, 12, 88, 34, 5, 44, 12, 100}; int length = sizeof(nums) / sizeof(nums[0]); 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; } } }
选择--冒泡排序优化(提取函数)
#include <stdio.h> void selectSort(int nums[], int length); void printArray(int nums[], int length); //void swap(int v1, int v2); void swap(int nums[], int i, int j); void bubbleSort(int nums[], int length); int main(int argc, const char * argv[]) { // 已知一个无序的数组, 里面有5个元素, 要求对数组进行排序 int nums[8] = {99, 12, 88, 34, 5, 44, 12, 100}; int length = sizeof(nums) / sizeof(nums[0]); printArray(nums, length); bubbleSort(nums, length); printf("----------------\n"); printArray(nums, length); return 0; } // 遍历数组 void printArray(int nums[], int length) { for (int i = 0; i < length; i++) { printf("nums[%i] = %i\n", i, nums[i]); } } void bubbleSort(int nums[], int length) { for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - 1 - i; j++) { if (nums[j] > nums[j + 1]) { swap(nums, j, j+1); } } } } // 选择排序 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; */ // swap(nums[i], nums[j]); swap(nums, i, j); } } } } // 基本数据类型作为函数的参数, 是值传递, 在函数中修改形参不会影响实参的值 /* void swap(int v1, int v2) { int temp = v1; v1 = v2; v2 = temp; } */ // 交换两个数的值 void swap(int nums[], int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
折半查找:
思路:
在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;
若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;
若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。
不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败。
#include <stdio.h> #include <time.h> int findKey(int nums[], int key, int length); int findKey2(int nums[], int length, int key); int findKey3(int nums[], int length, int key); int main(int argc, const char * argv[]) { // 现在已知一个有序的数组, 和一个key. 要求从数组中找到key对应的索引的位置 // 对该方法进行封装, 要求找到就返回对应的索引, 找不到就返回-1 int nums[500000] = {1, 3, 5, 7, 9, [499999] = 99}; int key = 99; int length = sizeof(nums) / sizeof(nums[0]); // 消耗了1640毫秒 clock_t startTime = clock(); int index = findKey(nums, key, length); clock_t endTime = clock(); printf("消耗了%lu毫秒\n", endTime - startTime); printf("index = %i\n", index); // 消耗了3毫秒 clock_t startTime = clock(); int index = findKey2(nums, length, key); // 消耗了2毫秒 int index = findKey2(nums, length, key); clock_t endTime = clock(); printf("消耗了%lu毫秒\n", endTime - startTime); printf("index = %i\n", index); return 0; } int findKey3(int nums[], int length, int key) { int min, max, mid; min = 0; max = length - 1; // 只要还在我们的范围内就需要查找 while (min <= max) { // 计算中间值 mid = (min + max) / 2; if (key > nums[mid]) { min = mid + 1; }else if (key < nums[mid]) { max = mid - 1; }else { return mid; } } return -1; } int findKey2(int nums[], int length, int key) { int min, max, mid; min = 0; max = length - 1; mid = (min + max) / 2; while (key != nums[mid]) { // 判断如果要找的值, 大于取出的值, 那么min要改变 if (key > nums[mid]) { min = mid + 1; // 判断如果要找的值, 小于取出的值, 那么max要改变 }else if (key < nums[mid]) { max = mid - 1; } // 超出范围, 数组中没有需要查找的值 if (min > max) { return -1; } // 每次改变完min和max都需要重新计算mid mid = (min + max) / 2; } return mid; } //循环遍历查找 int findKey(int nums[], int key, int length) { for (int i = 0; i < length; i++) { if (nums[i] == key) { return i; } } return -1; }
进制转换查表法:
#include <stdio.h> void total(int value, int base, int offset); void ptintBinary(int num); void printfOct(int num); void printfHex(int num); int main(int argc, const char * argv[]) {
// ptintBinary(10); // printfOct(10); printfHex(10); return 0; } //转十六进制 void printfHex(int num) { total(num, 15, 4); } //转八进制 void printfOct(int num) { total(num, 7, 3); } //转二进制 void ptintBinary(int num) { total(num, 1, 1); } // 转换所有的进制 // value就是需要转换的数值 // base就是需要&上的数 // offset就是需要右移的位数 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"); }