5、函数
函数就是程序中独立的功能,其实就是将程序打包,取一个名字,方便后面重复使用。函数的使用提高了代码的复用性和可维护性。
/*
函数的定义:
返回值类型 函数名(形参1, 形参2……)
{
函数体;
return 返回值;
}
*/
首先先定义一个简单的不带参数、不带返回值(其返回值其实为空)的函数例子,求10和20之和:
#include<stdio.h>
// 函数的定义
void sum()
{
int num1 = 10, num2 = 20, sum = 0;
sum = num1 + num2;
printf("%d\n",sum);
}
int main()
{
// 函数的调用
sum();
return 0;
}
需要注意的是1、函数名在定义过程中不能重复,2、函数与函数之间是平级关系,不能嵌套定义,3、函数不调用就不执行,4、自定义函数写在main函数下面要在上方再次申明。这里再定义一个带参数的函数,求任意两数之和。
#include<stdio.h>
// 函数的定义
void sum(int num1,int num2)
{
int sum = num1 + num2;
printf("%d\n",sum);
}
int main()
{
// 函数的调用
sum(20, 30); //50
return 0;
}
其中int num1 和 int num2 为形式参数,20 和 30 为实际参数,形参和实参必须一一对应。函数除了参数之外,还有返回值的使用。返回值使用关键字 return ,它的作用包括结束函数和把后面的数据交给调用处。return下面不能编写代码,因为永远执行不到。如果书写 return 的后面没有跟具体的数据仅表示结束函数,还有一点要注意的时,返回值必须和函数名前返回值的类型相对应,如果没有返回值即为空,使用 void 。函数有返回值之后,函数的调用处使用变量接受返回值。求任意两数之和,并返回结果判断大小。
#include<stdio.h>
// 函数的定义
int sum(int num1,int num2)
{
int sum = num1 + num2;
return sum;
}
int main()
{
// 函数的调用
int result_1 = sum(20, 30);
int result_2 = sum(30, 40);
if(result_1 == result_2){printf("结果相等\n");}
else if(result_1 > result_2){printf("结果1较大\n");}
else{printf("结果2较大\n");}
return 0;
}
6、数组
6.1、数组的知识
数组是一种容器,可以存储同种数据类型的多个值。数组的定义为
数据类型 数组名 [长度]
int arr [3];
数组一旦定义之后,长度不可变,并且是连续的空间。数组在定义之后,还需要进行初始化,即给数组进行赋值,其格式为:
数据类型 数组名[长度] = {数据值,数据值……}
int arr[3] = {1,2,3};
如果长度省略,数据值的个数就是数组长度。如果长度未省略,但是数据值的个数 <= 长度,空缺的位置将由默认值填充,整数用 0 填充,小数用 0.0 填充,字符用'\0'填充 (其实就是空白字符),字符串用NULL填充(就是什么都没有)。
数组里面元素的访问包括获取和修改,其操作都利用到了索引。索引是数组的一个编号,也叫角标、下标、编号,从0开始,连续+1不间断。元素的获取一般赋值给一个变量,其格式为
变量 = 数组名[索引];
int num = arr[5];
元素的修改格式为
数组名[索引] = 数据值;
arr[5] = 10;
#include<stdio.h>
int main()
{
//定义数组并初始化
int arr[5] = {1,2,3,4,5};
//获取索引为2,4的元素相加
int sum = arr[2] + arr[4];
printf("%d\n",sum);//8
//修改最后一个元素为10
arr[4] = 10;
return 0;
}
遍历是依次获取数组的每一个元素,对数组进行遍历:
#include<stdio.h>
int main()
{
//定义数组并初始化
int arr[5] = {1,2,3,4,5};
//遍历数组进行打印
for(int i = 0; i < 5; i++){printf("%d\n",arr[i]);}
return 0;
}
那么数组在内存中是怎样存在的?首先简单介绍一下内存,当软件运行时,临时存储数据的就叫内存。内存中的数据只有点击保存之后,程序到对应位置取出数据,保存到硬盘,如果不保存再次打开会消失,比如记事本。那么如何把数据保存到内存中,又怎么把内存的对应位置数据取出来?
操作系统为了方便快速管理内存空间,把内存以字节(8个bit)为单位划分成很多小格子,每个字节都有自己的独立编号,这个编号就是内存地址(连续、唯一、不重复)。内存地址在不同位数操作系统不一样,在32位操作系统,内存地址以32位二进制表示,地址范围从0000 0000 0000 0000 0000 0000 0000 0000 到 1111 1111 1111 1111 1111 1111 1111 1111(2的32次方),最大内存4294967296字节,即4GB。在64位操作系统,内存地址以64位二进制表示,最大内存地址2的64次方,最大支持内存为17179TB。
C语言中的内存地址,假如申请一个 int 变量,就是在内存中申请4个小格子(字节)。以第一个格子的地址为准,是变量的地址,也叫首地址,再结合变量类型就可以获取所有的数据。在代码中,通过取地址符 &变量名; 获取变量的地址。
#include<stdio.h>
int main()
{
//获取变量的内存地址
int a = 10 ;
printf("%p\n",&a); //0000 0000 0061 FE1C
return 0;
}
int 类型变量 a 的内存地址为 0000 0000 0061 FE1C ,即为64位操作系统的内存地址,因为这里用16进制进行表示的,内存地址有16位,即16*4 = 64个二进制位。数组在内存中的表示如图,其中arr表示第一个字节的首地址,索引代表偏移量,索引结合数据类型可以确定后面数据的地址。
在实际的代码中运行可以看到内存的规则和上面表示的相同。数组的首地址和第一个元素的第一个格子的内存地址相同,索引就是偏移量,索引每加一,由于是int数据,内存地址加4。数据长度的计算也可以得出:总长度/数据类型占用的字节个数(判断数据所占字节的大小可以使用sizeof函数 )。
#include<stdio.h>
int main()
{
//获取数组的内存地址
int arr[] = {1,2,3} ;
printf("%p\n",&arr); //0000 0000 0061 FE14
printf("%p\n",&arr[0]);//0000 0000 0061 FE14
printf("%p\n",&arr[1]);//0000 0000 0061 FE18
printf("%p\n",&arr[2]);//0000 0000 0061 FE1C
int len = sizeof(arr) / sizeof(arr[0]);
printf("%d\n",len); // 3
return 0;
}
需要注意的一点是,数组作为参数传入函数时,传入的是数组的首地址。简单来说,在下面代码中 main 函数中定义的 arr 代表的是完整的数组,其字节长度为20,是五个 int 类型数据的长度。arr 作为参数传入函数时,传入的是 arr 的首地址,其字节长度为8,是64位操作系统的内存地址长度。
#include<stdio.h>
void printfun(int arr[])
{
printf("%zu\n",sizeof(arr));
}
int main()
{
int arr[] = {1,2,3,4,5} ;
printf("%zu\n",sizeof(arr)); //20
printfun(arr);//8
return 0;
}
再做一个简单的实验,对 main 函数中定义的数组进行取地址,并打印;再对传入函数的arr进行打印,这里没有取地址,原因是传入参数arr本身就是地址。发现两者相同,说明传入的参数就是数组的首地址。
#include<stdio.h>
void printfun(int arr[])
{
printf("%p\n",arr);
}
int main()
{
int arr[] = {1,2,3,4,5} ;
printf("%p\n",&arr); //000000000061FE00
printfun(arr);//000000000061FE00
return 0;
}
最后用一张图简单地说明了两者的关系。
当知道这些机制之后,再编写一个函数遍历整个数组,注意的是知道首地址无法进行遍历,要知道数据的长度(就是内部每个数据的地址偏移量),首地址结合地址偏移量从数组中取出每一个数据:
#include<stdio.h>
void printfun(int arr[],int len)
{
for(int i = 0; i < len; i++){printf("%d ",arr[i]);}
}
int main()
{
int arr[] = {1,2,3,4,5} ;
int len = sizeof(arr) / sizeof(arr[0]);
printfun(arr, len);//1 2 3 4 5
return 0;
}
6.2、数组的练习
练习1:求包括五个元素的数组的最大值
#include<stdio.h>
int main()
{
int arr[] = {25,11,15,55,9};
//max的默认值一定是数组里面的数
int max = arr[0];
int len = sizeof(arr) / sizeof(arr[0]);
//遍历数组进行比较
for(int i = 1; i < len; i++)
{max = max > arr[i] ? max : arr[i];}
printf("%d\n",max); //55
return 0;
}
练习2:生成10个1到100的随机数存入数组,求出所有数据的和
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
// 1.定义数组
int arr[10] = {0};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.生成10个1~100的随机数
srand(time(NULL));
for(int i = 0; i < len; i++)
{
int num = rand() % 100 + 1;
arr[i] = num;
printf("%d\n",arr[i]);
}
// 3.累加求和
int sum = 0;
for(int i = 0; i < len; i++){sum += arr[i];}
printf("%d\n",sum);
return 0;
}
生成10个1到100的随机数存入数组,要求数据不能重复,求出所有数据的和,求出所有数据的平均数,统计有多少个数据比平均值小。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int contain(int arr[], int len, int num)
{
for(int i = 0; i < len; i++ )
{
if(num == arr[i]){return 1;}
}
return 0;
}
int main()
{
// 1.定义数组
int arr[10] = {0};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.生成10个1~100的随机数,并且不相同
srand(time(NULL));
for(int i = 0; i < len; )
{
int num = rand() % 100 + 1;
int flag = contain(arr, len, num);
if (!flag)
{
arr[i] = num;
i++;
}
}
for(int i = 0; i < len; i++)
{
printf("%d\n",arr[i]);
}
// 3.累加求和
int sum = 0;
for(int i = 0; i < len; i++){sum += arr[i];}
printf("和为:%d\n",sum);
// 4.求平均数
float avg = sum / 10.0;
printf("平均数为:%.2f\n",avg);
// 5.统计比平均数少的个数
int count = 0;
for(int i = 0; i < len; i++)
{
if(arr[i] < avg){ count++; }
}
printf("比平均数少的个数为:%d\n",count);
return 0;
}
练习3:键盘录入5个数据并存入数组,要求遍历数组,反转数组,再遍历数组。
#include<stdio.h>
void printfun(int arr[], int len)
{
for(int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
}
int main()
{
// 1.定义数组
int arr[5] = {0};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.键盘录入数据
for(int i = 0; i < len; i++)
{
printf("请输入第%d个数字", i + 1);
scanf("%d",&arr[i]);
}
printfun(arr, len);
// 3.反转数组
for(int i = 0; i < len; i++)
{
if(i < len-1-i)
{
int temp = arr[i];
arr[i] = arr[len-1-i];
arr[len-1-i] = temp;
}
else{break;}
}
/*
int i = 0;
int j = len - 1;
while(i < j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
*/
printfun(arr, len);
return 0;
}
6.3、数组的常见算法
常见算法分为两种,一个是查找算法,另一个就是排序算法。
6.3.1、查找算法
查找算法分为七种,分别是基本查找、二分查找、插值查找、分块查找、哈希查找、树表查找和斐波那契查找,只介绍前四种。基本查找也叫顺序查找,是有一堆数据,要查找其中的某一个数据:将数据放到数组中,遍历数组查找,如果找到返回数据索引,如果没有返回-1(因为不存在-1索引)。
#include<stdio.h>
int order(int arr[], int len, int num)
{
for(int i = 0; i < len; i++)
{
if(num == arr[i]){return i;}
}
return -1;
}
int main()
{
// 基本查找
// 1、定义数组
int arr[] = {15,9,88,54,16};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.定义查找数据
int num = 54;
// 3.数据查找
int result = order(arr, len, num);
printf("%d\n",result);
return 0;
}
二分查找也叫折半查找,提高了查找的效率,前提条件是数组中的数据必须是有序的,如果数据是无序的,先排序再用二分查找得到的索引没有实际意义,只能确定当前数字在数组中是否存在,因为排序之后数字的位置就可能发生变化了,其核心逻辑是每次排除一半的查找范围:
1.定义min和max表示当前要查找的范围 ;
2.定义mid在min和max之间,一般来说;
3.如果要查找的元素在mid的左边,缩小范围时,min不变,max等于mid减1;
4.如果要查找的元素在mid的右边,缩小范围时,max不变,min等于mid加1;
不断循环一直到找到数据为止,即min = max。
#include<stdio.h>
int binarySearch(int arr[], int len, int num)
{
//1.确定查找的范围
int min = 0, max = len-1;
//2.利用循环不断进行查找
while(min <= max)
{
int mid = (min + max) / 2;
if(arr[mid] > num){max = mid - 1;}
else if(arr[mid] < num){min = mid + 1;}
else{return mid;}
}
//3.如果min>max,表示数据不存在,返回-1
return -1;
}
int main()
{
// 二分查找
// 1、定义数组
int arr[] = {9,15,16,33,38,42,48,59,68,75};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.定义查找数据
int num = 59;
// 3.数据查找
int result = binarySearch(arr, len, num);
printf("%d\n",result); //7
return 0;
}
二分查找的mid每次都是在最中间定义的,所以能不能让mid更加靠近要查找的数据?所以提出插值查找,将mid的计算换成下面公式:
代码其他的部分保持不变,但是这种方法要求数据尽可能发布要均匀。
已经介绍的算法基本查找是数据没有如何顺序,二分查找和插值查找是要求数据一定要有顺序,这两种数据的类型比较极端,更多的是无序中存在有序的数据。对于这种数据使用分块查找,其核心思路是先确定要查找的元素在哪一块,然后在块内挨个查找,分块的原则:
1、前一块中的最大数据,小于后一块中的所有数据(块内无序,块间有序);
2、块数数量一般等于数字的个数开根号,比如:16个数字一般分为4块左右。
6.3.2、排序算法
排序算法就是把乱序数据排成有序算法,可以从小到大,也可以从大到小。排序算法包括冒泡排序和选择排序。冒泡排序就是相邻的数据两两比较,小的放前面,大的放后面。如图,第一轮循环结束后,最大值已经找到,在数组最右边,第二轮循环只要在剩余元素找到最大值就可以了,即第二轮少循环一次。以此类推,注意的是如果数组有n个数据,总共只要执行 n-1 轮代码就行。
#include<stdio.h>
int main()
{
// 冒泡排序
// 1、定义数组
int arr[] = {3,5,2,1,4};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.冒泡排序升序
for(int j = 0; j < len-1; j++)
{
for(int i = 0; i < len-1; i++)
{
if(i == len -1 - j){continue;}
if(arr[i] > arr[i+1])
{
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
for(int i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
// 1 2 3 4 5
return 0;
}
选择排序是从0索引开始,拿着每一个索引上的元素跟后面的元素进行一一比较,小的放前面,大的放后面,以此类推。第一轮如下图所示,0索引数据和后面每一个元素进行一一比较,小的放前面,大的放后面,第一轮结束后,最小的数据已经确定,第二轮循环从1索引开始以此类推。同样地,注意的是如果数组有n个数据,总共只要执行 n-1 轮代码就行。
#include<stdio.h>
int main()
{
// 冒泡排序
// 1、定义数组
int arr[] = {3,5,2,1,4};
int len = sizeof(arr) / sizeof(arr[0]);
// 2.冒泡排序升序
for(int j = 0; j < len-1; j++)
{
for(int i = j+1; i < len; i++)
{
if(arr[j] > arr[i])
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for(int i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
// 1 2 3 4 5
return 0;
}
简单来说,以升序排序为前提,冒泡排序在末端依次确定最大值,顺序排序在前端依次确定最小值。
标签:03,arr,int,len,C语言,语法,查找,数组,printf From: https://blog.csdn.net/m0_62883696/article/details/144948251