C语言基础--数组及相关

时间:2022-01-10 03:44:42

概念:

  一堆相同类型的数据的有序集合

格式:

  元素类型  数组名称[ 元素个数 ]

定义数组:

    // 定义了一个名称叫做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] 的空间

       C语言基础--数组及相关

数组练习:

    

    // 从键盘录入当天出售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. 然后对数组排序输出
*/

数组排序:

  冒泡排序

    C语言基础--数组及相关

    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;
}
}
}

  选择排序:

    C语言基础--数组及相关

        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;
}

折半查找:

  思路:

    在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;

    若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;

    若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。

    不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败。

      C语言基础--数组及相关

      

  #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");
}