概念:
一堆相同类型的数据的有序集合
格式:
元素类型 数组名称[ 元素个数 ]
定义数组:
// 定义了一个名称叫做scores的数组, 数组中可以存放3个int类型的数据
int scores[];
// 只要定义一个C语言的数组, 系统就自动会给数组中的每一块小得存储空间一个编号
// 这个编号从0开始, 一次递增
// 数组中系统自动绑定的编号, 我们称之为 索引
scores[] = ;
scores[] = ;
scores[] = ;
数组初始化:
完全初始化:
// 依次将{}中的每一个值赋值给数组中的每一个元素
// 并且从0开始赋值
int scores[] = {,,,,};
部分初始化:
// 默认从0开始初始化, 依次赋值
// 注意: 如果"在部分初始化中"对应的内存没有被初始化, 那么默认是0
int scores1[] = {, };
注意点:
1.如果没有对数组进行初始化(完全和部门), 那么不要随便使用数组中的数据, 可能是一段垃圾数据(随机值)
2.定义数组的时候, 数组的元素个数不能使用变量 变量作为数组的元素个数, 不初始化的情况下是随机值, 如果初始化会直接报错
3.如果定义的同时进行初始化, 那么元素的个数可以省略, 省略之后, 初始化赋值几个数据, 数组就能存储几个数据,如果定义数组时没有进行初始化, 那么不能省略元素个数
4.可以通过[索引] = 的方式, 给指定索引的元素赋值
int socres7[] = {[] = , [] = };
5.只能在定义的同时利用{}进行初始化, 如果是先定义那么就不能使用{}进行初始化
int scores8[];
//scores8 = {1 , 4, 19};
// 如果先定义那么就不能再进行整体赋值, 只能单个赋值
scores8[] = ;
数组遍历:
int scores[] = {, , , , , , , };
// 动态计算数组的元素个数
int length = sizeof(scores) / sizeof(scores[]);
//length 不要写 6 这样的魔鬼数字
for (int i = ; i < length; i++) {
printf("scores[%i] = %i\n", i,scores[i]);
}
数组内存分配:
存储方式:
1)计算机会给数组分配一块连续的存储空间
2)数组名代表数组的首地址,从首地址位置,依次存入数组的第1个、第2个....、第n个元素
3)每个元素占用相同的字节数(取决于数组类型)
4)并且数组中元素之间的地址是连续。
// 变量在内存中的存储
// 由于变量的内存寻址是从大到小, 所以存储数据时会从高字节开始存储
int num = ; // 0000 0000 0000 0000 0000 0000 0000 1010
/*
00001010 0x7fff5fbff7cc
00000000 0x7fff5fbff7cd
00000000 0x7fff5fbff7ce
00000000 0x7fff5fbff7cf
*/
// 注意: 数组的存储和变量有点不一样, 数组存储元素, 是从所占用的低字节开始存储
// 数组的元素自然的从上往下排列存储,整个数组的地址为首元素的地址。
char charValues[] = {'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[] = {, };
int values[] = {, , };
// 注意点: 在使用数组的时候, 一定不要访问不属于字节的存储空间, 这样会导致数据混乱
// 有时候如果访问了不属于自己的存储空间, 程序会报错
values[] = ;
printf("values[3] = %i\n", values[]);
printf("nums[0] = %i\n", nums[]);
nums[-] = ;
printf("values[2] = %i\n", values[]);
return ;
}
/*
输出结果:
values[3] = 44
nums[0] = 44
values[2] = 88
*/
下图分析 values[3]访问到了nums[0]的空间 nums[-1] 访问到了 values[2] 的空间
数组练习:
// 从键盘录入当天出售BTC的价格并计算出售的BTC的总价和平均价(比如说一天出售了4个比特币)
// 1.1定义数组保存每个比特币的价格
int values[] = {-};
// 1.2动态计算数组的元素个数
int length = sizeof(values) / sizeof(values[]);
// 1.3定义变量保存总和
int sum = ;
for (int i = ; i < length; i++) {
printf("请输入第%i个比特币的价格\n", i + );
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[] = ;
} int main(int argc, const char * argv[]) {
int nums[] = {, };
change(nums);
printf("nums[1] = %i\n", nums[]);
return ;
}
输出结果:
注意点:
如果传递的数组的名称, 其实传递的是地址 如果传递的是地址, 其实传递的是指针 指针在64位编译环境占8个字节
如果数组作为形参, 那么在函数中就不能通过数组的名称计算出数组元素的个数 因为系统会自动将数组形参转换为指针, 指针占用8个字节 所以只能读取到8个字节的数据
举例:
要求定义一个函数, 实现遍历数组. (只要别人传递数组给函数, 就要求输出数组中所有元素的值)
#include <stdio.h>
void printArray(int values[]); int main(int argc, const char * argv[]) {
int nums[] = { , , };
printf("size = %i\n", sizeof(nums)); // size = 12
printArray(nums); // 数组名称就是数组的地址
return ;
} void printArray(int values[])
{
printf("size = %i\n", sizeof(values)); // size = 8
// 1.动态计算数组的元素个数
int length = sizeof(values) / sizeof(values[]);
// 2.遍历数组
for (int i = ; i < length; i++) {
printf("values[%i] = %i\n", i,values[i]); // 输出: values[0] = 0 values[1] = 3
}
}
改正后(能正确输出数组的所有元素):
#include <stdio.h>
void printArray(int values[], int length); int main(int argc, const char * argv[]) {
int nums[] = { , , };
printf("size = %lu\n", sizeof(nums)); // size = 12
int length = sizeof(nums) / sizeof(nums[]);
printArray(nums, length);
return ;
} void printArray(int values[], int length)
{
printf("size = %lu\n", sizeof(values)); // size = 8
// 遍历数组
for (int i = ; 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[] = {};
// 2.接收用户的数据
int value = -;
for (int i = ; i < ; i++) {
printf("请输入第%i个数据\n", i + );
scanf("%i", &value);
nums[value] = nums[value] + ;
} for (int i = ; i < ; i++) {
for (int j = ; j < nums[i]; j++) {
printf("%i\n", i); // 1, 1, 2, 3, 3, 6
}
}
return ;
}
/*
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[] = {, , , , , };
int length = sizeof(nums) / sizeof(nums[]);
for (int i = ; i < length - ; i++) {
for (int j = ; j < length - - i; j++) {
if (nums[j] > nums[j + ]) {
int temp = nums[j];
nums[j] = nums[j + ];
nums[j + ] = temp;
}
}
}
选择排序:
int nums[] = {, , , , , , , };
int length = sizeof(nums) / sizeof(nums[]);
for (int i = ; i < length - ; i++) {
for (int j = i+; 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[] = {, , , , , , , };
int length = sizeof(nums) / sizeof(nums[]);
printArray(nums, length);
bubbleSort(nums, length);
printf("----------------\n");
printArray(nums, length);
return ;
}
// 遍历数组
void printArray(int nums[], int length)
{
for (int i = ; i < length; i++) {
printf("nums[%i] = %i\n", i, nums[i]);
}
}
void bubbleSort(int nums[], int length)
{
for (int i = ; i < length - ; i++) {
for (int j = ; j < length - - i; j++) {
if (nums[j] > nums[j + ]) {
swap(nums, j, j+);
}
}
}
}
// 选择排序
void selectSort(int nums[], int length)
{
for (int i = ; i < length - ; i++) {
for (int j = i+; 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[] = {, , , , , [] = };
int key = ;
int length = sizeof(nums) / sizeof(nums[]); // 消耗了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 ;
} int findKey3(int nums[], int length, int key)
{
int min, max, mid;
min = ;
max = length - ;
// 只要还在我们的范围内就需要查找
while (min <= max) {
// 计算中间值
mid = (min + max) / ;
if (key > nums[mid]) {
min = mid + ;
}else if (key < nums[mid])
{
max = mid - ;
}else
{
return mid;
}
}
return -;
} int findKey2(int nums[], int length, int key)
{
int min, max, mid;
min = ;
max = length - ;
mid = (min + max) / ;
while (key != nums[mid]) {
// 判断如果要找的值, 大于取出的值, 那么min要改变
if (key > nums[mid]) {
min = mid + ;
// 判断如果要找的值, 小于取出的值, 那么max要改变
}else if (key < nums[mid])
{
max = mid - ;
}
// 超出范围, 数组中没有需要查找的值
if (min > max) {
return -;
}
// 每次改变完min和max都需要重新计算mid
mid = (min + max) / ;
}
return mid;
} //循环遍历查找
int findKey(int nums[], int key, int length)
{
for (int i = ; i < length; i++) {
if (nums[i] == key) {
return i;
}
}
return -;
}
进制转换查表法:
#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();
return ;
}
//转十六进制
void printfHex(int num)
{
total(num, , );
}
//转八进制
void printfOct(int num)
{
total(num, , );
}
//转二进制
void ptintBinary(int num)
{
total(num, , );
}
// 转换所有的进制
// value就是需要转换的数值
// base就是需要&上的数
// offset就是需要右移的位数
void total(int value, int base, int offset)
{
// 1.定义一个数组, 用于保存十六进制中所有的取值
char charValues[] = {'', '', '', '', '', '', '', '', '', '', 'a', 'b', 'c', 'd', 'e', 'f'};
// 2.定义一个数组, 用于保存查询后的结果
char results[] = {''};
// 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引
int pos = sizeof(results)/ sizeof(results[]);
while (value != ) {
// 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 < ; i++) {
printf("%c", results[i]);
}
printf("\n");
}