资讯专栏INFORMATION COLUMN

C语言第三期(1万字函数-数组-操作符详解)

Jeff / 1716人阅读

摘要:形式参数当函数调用完成之后就自动销毁了。函数调用传值调用函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。函数的声明一般出现在函数的使用之前。它其实就数组访问的操作符。

前言

  • 本期是C语言的第三期,需要看前面的几期框架可以点到我博客里看一下。
  • 下一期博主就要对指针进行总结了。同时我还会不定时分享一些干货。期待你的回访哦!
    ❤️ :热爱编程学习,期待一起交流!
    ?:博主水平有限,如有发现错误,求告知,多谢!

函数

函数的定义

数学中我们常见到函数的概念。但是你了解C语言中的函数吗?

  • 维基百科中对函数的定义:子程序
  • 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码,
    由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
  • 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

函数的分类

  • 库函数
  • 自定义函数

库函数 为什么会有库函数?

  • 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上,用到printf函数。
  • 或者输入一个数的时候,用到scanf函数。
  • 向上面的描述的基础功能,我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员
    进行软件开发。
  • 简单的总结,C语言常用的库函数都有:
    • IO函数
    • 字符串操作函数
    • 字符操作函数
    • 内存操作函数
    • 时间/日期函数
    • 数学函数
    • 其他库函数
  • 当我们使用一个库函数的时候不知道头文件怎么用?
    • 这里强烈推荐中文版的一个文档http://zh.cppreference.com
    • 用的时候只需要查找一下就可以了,不需要把库函数全部记住,只需要学会如何查找就好了。

自定义函数

  • 如果库函数能干所有的事情,那还要程序员干什么?
  • 所以更加重要的是自定义函数。
  • 自定义函数和库函数一样,有函数名,返回值类型和函数参数。
  • 但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。
  • 函数的组成格式:
返回类型 函数名(参数类型 形式参数1,参数类型 形式参数2) {    函数体;    返回值;}
ret_type fun_name(para1, * ){ statement;//语句项}//ret_type 返回类型//fun_name 函数名//para1    函数参数
  • 我们举个例子
  • 写一个函数可以找出两个整数中的最大值。
#include <stdio.h>//get_max函数的设计int get_max(int x, int y)//这里是get_max函数的定义,这里的x和y是形参,用来接收实参传来的值。{ 	return (x>y)?(x):(y);//返回值}int main(){ 	 int num1 = 10; 	 int num2 = 20;	 int max = get_max(num1, num2);//这里是调用get_max函数,num1和num2均为实参。	 printf("max = %d/n", max);	 return 0}

函数参数

实际参数(实参)

  • 真实传给函数的参数,叫实参。
  • 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形
  • 实参可以是:常量、变量、表达式、函数等。

形式参数(形参)

  • 像上面的实参(num1,num2)和形参(x,y)使用的不是同一空间。即他们的值所在的地址不同。
  • 形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内
    存单元),所以叫形式参数。
  • 形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有
    效。
  • 我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。

函数调用

传值调用

  • 函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
  • 写一个函数可以判断一个数是不是素数。
#include<stdio.h>#include<math.h>int is_prime(int n){	int i = 0;	for (i = 2; i < sqrt(n); i ++)	{		if (n % i == 0)		{			return 0;		}	}	return 1;}int main(){	int n = 0;	scanf("%d", &n);	int ret = is_prime(n);	if (ret == 1)	{		printf("是素数");	}	else if(ret == 0)	{		printf("不是素数");	}	return 0;}

传址调用

  • 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
  • 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
    作函数外部的变量。
  • 传址调用需要特别注意传数组名。

函数的嵌套调用和链式访问

嵌套调用

  • 函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
#include <stdio.h>void new_line(){ printf("hehe/n");}void three_line(){    int i = 0; for(i=0; i<3; i++)   {        new_line();第二次调用   }}int main(){ three_line();//第一次调用 return 0; }
  • 函数可以嵌套调用,但是不能嵌套定义。

链式访问(了解)

  • 把一个函数的返回值作为另外一个函数的参数。
#include <stdio.h>#include <string.h>int main(){    char arr[20] = "hello"; int ret = strlen(strcat(arr,"bit"));//这里介绍一下strlen函数 printf("%d/n", ret); return 0; }#include <stdio.h>int main(){    printf("%d", printf("%d", printf("%d", 43)));    //结果是啥?    //注:printf函数的返回值是打印在屏幕上字符的个数    return 0; }

函数的声明和定义

函数的声明

  • 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
    声明决定不了。
  • 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
  • 函数的声明一般要放在头文件中的。(.h结尾的文件)
    ###函数的定义
  • 函数的定义是指函数的具体实现,交待函数的功能实现。
  • 一般放在源文件中(.c结尾的文件)

函数递归(汉诺塔)

什么是递归?

  • 程序调用自身的编程技巧称为递归( recursion)。
  • 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  • 递归的主要思考方式在于:把大事化小

递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件
  • 下面是递归的典型问题汉诺塔问题。图片来自百度。
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>int Hanio_twice(int num){	if(1 == num)		return 1;	else		return 2 * Hanio_twice(num - 1) + 1;}int main(){	int num = 0;		scanf("%d", &num);//塔数	int ret = Hanio_twice(num);	printf("完成%d层的汉诺塔需要%d步/n", num, ret);	return 0;}

数组

一维数组的创建和初始化

  • 数组是一组相同类型元素的集合。

数组的创建

  • 数组的创建方式:
type_t   arr_name   [const_n];//type_t 是指数组的元素类型//const_n 是一个常量表达式,用来指定数组的大小。
  • 数组创建的实例:
//代码1int arr1[10];//代码2int count = 10;int arr2[count];//数组时候可以正常创建?//代码3char arr3[10];float arr4[1];double arr5[20];

数组的初始化

  • 数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
  • 代码如下
int arr1[10] = {1,2,3};int arr2[] = {1,2,3,4};int arr3[5] = {12345}char arr4[3] = {"a",98, "c"};char arr5[] = {"a","b","c"};char arr6[] = "abcdef";
  • 数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
  • 但是对于下面的代码要区分,内存中如何分配
char arr1[] = "abc";//内存里存放了四个元素 分别是"a""b""c""/0"char arr2[3] = {"a","b","c"};//而这里面存放的是"a""b""c"
  • 字符串的结束标志是‘/0’

一维数组的使用

  • 对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符。它其实就数组访问的操作符。
    我们来看代码:
#include <stdio.h>int main(){ int arr[10] = {0};//数组的不完全初始化    int sz = sizeof(arr)/sizeof(arr[0]);//计算数组的元素个数 //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以: int i = 0;//做下标 for(i=0; i<10; i++) { arr[i] = i;//这里i写10,行吗?当然不行,写10意味着访问的是第11个元素,数组会越界。 }  for(i=0; i<10; ++i) { printf("%d ", arr[i]); //输出数组的内容 } return 0;}
  • 总结:
    • 数组是使用下标来访问的,下标是从0开始。
    • 数组的大小可以通过计算得到。
int arr[10];int sz = sizeof(arr)/sizeof(arr[0]);//40/4=10;所以数组的长度是10

一维数组在内存中的存储

#include <stdio.h>int main(){ int arr[10] = {0}; int i = 0;    int sz = sizeof(arr)/sizeof(arr[0]);     for(i=0; i<sz; ++i) { printf("&arr[%d] = %p/n", i, &arr[i]);//我们将10个元素的地址打印出来。 } return 0; }
  • 打印结果如下

  • 由图片可知。

  • 每个地址相差四位数,9C和A0差四位,后面都是四位(这些数字都是16进制)。

    • 数组在内存中是连续存放的。
    • 用数组名指向整个存储空间最小的地址

二维数组的创建和初始化

//数组创建int arr[3][4];char arr[3][5];double arr[2][4];
//数组初始化int arr[3][4] = {1,2,3,4};int arr[3][4] = {{1,2},{4,5}};int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略

二维数组的使用

  • 二维数组的使用也是通过下标的方式。
  • 代码如下,创建一个3*4的二维数组,并对数组初始化输入和输出。图形如下。
#include <stdio.h>int main(){	 int arr[3][4] = {0};//创建一个三行四列的二维数组	 int i = 0; for(i=0; i<3; i++) {	 	 int j = 0;		 for(j=0; j<4; j++) 		{		 arr[i][j] = i*j;//循环赋值	  } } for(i=0; i<3; i++) {	 int j = 0;	 for(j=0; j<4; j++)	 {	 	printf("%d ", arr[i][j]);//循环输出 	 } }	 return 0; }

二维数组在内存中的存储

  • 像一维数组一样,这里我们尝试打印二维数组的每个元素。
#include <stdio.h>int main(){ int arr[3][4]; int i = 0; for(i=0; i<3; i++) {	 int j = 0;	 for(j=0; j<4; j++)	 {		 printf("&arr[%d][%d] = %p/n", i, j,&arr[i][j]);	 }//将3*4的二维数组的每个元素的地址打印出来,来研究其在内存中的存储。 }	 return 0; }
  • 打印结果如下。
  • 和上面的一维数组一样,每次跳过四位数,说明二维数组在内存中也是连续的。

数组越界

  • 数组的下标是有范围限制的。
  • 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
  • 所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
  • 但是编译器本身并不会报错,所以此时需要程序员多加留意。
#include <stdio.h>int main(){ int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义了10个元素。    int i = 0;    for(i=0; i<=10; i++)   {        printf("%d/n", arr[i]);//当i等于10的时候,这时候访问的是第11个元素!   } return 0; }

数组作为函数参数(传址调用)

  • 我们下面以错误的冒泡排序算法来举例子。
#include <stdio.h>void bubble_sort(int arr[]){ int sz = sizeof(arr)/sizeof(arr[0]);//这一行代码代码错了.错误思维是应该是40/4=10    int i = 0;//但结果是4/4原因是因为sizeof(arr)算出来的是首元素的地址,首元素的地址大小看机器,以32位的来说是4字节。 for(i=0; i<sz-1; i++)   {        int j = 0;        for(j=0; j<sz-i-1; j++)       {            if(arr[j] > arr[j+1])           {                int tmp = arr[j];                arr[j] = arr[j+1];                arr[j+1] = tmp;           }       }   }}int main(){    int arr[] = {3,1,7,5,8,9,0,2,4,6};    bubble_sort(arr);//传过去的是数组名,默认是数组首元素的地址。    for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)   {        printf("%d ", arr[i]);   }    return 0; }
  • 所以我们得出一个结论:数组作为函数参数的时候,不是把整个数组的传递过去。而传递的是数组首元素的地址。所谓传调用

什么是数组名

#include <stdio.h>int main(){    int arr[10] = {1,23
                 
               
              

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123733.html

相关文章

  • 【零基础趣学C语言】- 史上最全C语言函数详解万字图文+代码演示+图解)

    摘要:语言在设计中考虑了函数的高效性和易用性两个原则。在语言中,最常见的当属函数了。以上就是一个函数,它被称为语言的入口函数,或者主函数。例如和都是函数名。形式参数当函数调用完成之后就自动销毁了。 ...

    468122151 评论0 收藏0
  • 怒肝1.5万字——史上最全C语言文件操作详解

    摘要:二什么是文件磁盘上的文件就是文件。文件指针变量定义是一个指向类型数据的指针变量。表示向何种流中输出,可以是标准输出流,也可以是文件流。文件结构体指针,将要读取的文件流。 ...

    Alfred 评论0 收藏0
  • ❤️整理2万字带你走进C语言(详细讲解+代码演示+图解)❤️(强烈建议收藏!!!)

    目录 一、什么是C语言? 二、第一个C语言程序 代码 程序分析  程序运行 一个工程中出现两个及以上的main函数 代码 运行结果 分析 三、数据类型 数据各种类型 为什么会有这么多的数据类型? 计算机单位  各个数据类型的大小  注意事项 数据类型的使用 四、变量和常量 变量的分类 变量的使用 变量的作用域和生命周期  常量 五、字符串+转义字符+注释 字符串  转义字符 注释 六、选择语句  ...

    邱勇 评论0 收藏0
  • 爆肝1万字c++入门综合》

    摘要:大家好,今天屁孩君给大家带来入门综合。年,标准委员会发布了语言的第一个国际标准,该标准即为大名鼎鼎的。年,标准委员会发布了一份技术报告,详细说明了计划引入的新特性。年月日,经过标准委员投票,标准获得一致通过。 ...

    _Dreams 评论0 收藏0

发表评论

0条评论

Jeff

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<