C函数的全面解析和知识点梳理(七)
绪论
有时候,如果程序的功能比较多。规模比较大,把所有的程序代码都写在一个主函数中,就会使得主函数太庞杂,所以为了方便阅读和维护程序,就引进了 组装程序 的概念,把某些功能都在其他分支完成,然后需要哪个功能的时候就组装那个分支到主函数,这些分支就叫它函数,组装就叫调用,这样就会使主程序简化了,哪个函数是做什么也都很清楚。哪里用得到这个功能就在哪个调用就可以了 C 语言的函数可以直观地辨别出面向过程和面向对象的区别,C 语言的函数有一个特点,就是它有固定的格式和固定的模型。对于一个 C 程序而言,它所有的命令都包含在函数内。每个函数都会执行特定的任务。每个函数都只能被定义一次。但一个函数可以根据需要被多次的声明和调用。
函数的定义
在使用函数之前必须先定义,后使用。 定义函数要包括: 1、指定函数的数据类型,以便后续返回值的调用 2、指定函数的名字,以便后续调用 3、指定函数形参的类型和名字,以便后续传递数据,对于无参(void)可不用
函数类型+函数名+(参数表){
函数体
}
参数值的()起到了表示函数调用的重要作用即使没有参数也需要(),如果有参数,则需要给出正确的数量和顺序,这些值会按照一定的顺序(看编译环境一般是从右到左)依次来初始化函数中的参数 注意 如果调用函数时给的值与参数的类型不匹配有些编译环境可能会帮你类型强制转换好,但是有可能不是你想要的那样。所以 建议传递给函数的值要与声明一致
#include<stdio.h>//这里声明的是int的形参
int a(int a);//但是如果换成浮点类型输出将会不一样
int main(void){
double t = 23.8;
a(t);//这里传进去的是浮点数(实参)
}
int a(int a){//这里接收到的是整型的实参
int t = a;
//所以即使换成double t 也一样
printf("%d", t);//输出23
}
//但是更高级的语言会检查比较严格如 java C++
函数的传值
每个函数都有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
值的传递:传递给函数的值可以是表达式的结果(包括):字面量、变量、函数的返回值、计算的结果。但是在调用函数时,永远只能传 值 给函数,在传值的时候实际上只是把实参的值传递到形参处,做的只是一个复制的过程(但是指针就不一样了)
在函数定义的形参中,它们是不占用内存的,在主函数内调用形参的值时才会被临时分配内存。实参给形参传递的是值的传递,属于单向传递。把实参的值传递给形参,在调用结束后,形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值,实参的存储单元仍保留并维持数值不变。
地址的传递:形参为指针变量时函数之间的数据传递,如果函数的形参为指针类型时,对应的实参类型必须与形参保持一致
这种方式使用数组名或者指针作为函数参数,传递的是该数组的首地址或指针的值,而形参接收到的是地址,即指向实参的存储单元,形参和实参占用相同的存储单元,这种传递方式称为“参数的地址传递”。 地址传递的特点是形参并不会占用存储空间,数组名或指针就是一组连续空间的首地址。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该首地址之后,与实参共同存在一个存储单元,形参的变化也就是实参的变化。
函数的返回值
在定义的时候,如果不需要返回值的时候定义void类型,其他时候定义其他数据类型。但是使用void类型的时候,不能使用带值的return(可以没有return),调用的时候不能做返回值的赋值
int 和void的不同: 前面加有void 的函数,不能返回任何数据,这类函数应该将所有应该实现的功能在本函数内全部实现。但是并不是不能与外部交换数据,仍然可以通过引用型参数传递数据,只是调用时不能直接接受返回值,因为就没有返回值。 前面有int的函数,返回值是整型数,可能是结果是整数的数,也可能是运行状态,成功或失败的标识,函数调用时可以直接利用返回信息,实现一些功能。
main()函数的揭秘
main()函数原形:int main(int argc,char const*argv [ ] )
里边的两个参数,允许从执行环境中传递任意的多字节字符串 (argv[0])是命令本身,命令行参数是保存在argv[ ]里的,C/C++语言规定,可执行程序程序本身的文件名和地址 ,其中的一个描述了命令行参数的个数,通常称为argc;另一个是命令行参数的数组,通常称为argv。命令行参数都是字符串,所以argv的类型是char * [argc+1]。该程序的名字也作为argv[0]传进来,这个参数的表总以0结束,也就是说,argv[argc]==0。(argv数组的最后一个元素存放了一个NULL的指针)
#include <stdio.h>
int main(int argc, char * argv[]){
//argv[0]== 调用函数时使用的程序名和地址
//argv[1]==参数1
//argv[2]==参数2
//argv[3]==参数3
//依次类推...
//argc 就是计算并保存总共有多少个参数的
int i;
for (i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
return 0;
}
给main函数传递的这两个参数,argc和argv。argc是int类型的,它表示的是命令行参数的个数。不许要用户传递,它会根据用户从命令行输入的参数个数,自动确定。argv是char**类型的,它的作用是存储用户从命令行(黑窗口输入)传递进来的参数。它的第一个成员是用户运行的程序名字。
main函数的返回值,用于说明程序的退出状态。如果返回0,则代表程序正常退出;返回其他数字的含义则由系统决定,通常,返回非零代表程序异常退出。 (一般是由return返回)
return 的妙用
(return 是C语言的关键字)函数定义为什么样的返回类型,该函数中return后就应该是相应类型的值。
在函数中,如果碰到return 语句,那么程序就会返回调用该函数的下一条语句执行,也就是说跳出函数的执行,回到原来的地方继续执行下去。但是如果是在主函数(main)中碰到return语句,那么整个程序就会停止。return表示从被调函数返回到主调函数或其他函数继续执行,返回时可 附带一个返回值,返回值可以是一个常量,变量,或是表达式。 传指针形式:直接传给函数的是变量的地址,由于被调函数在参数指针的作用域之内,此时直接改变变量的本体。
返回值: 计算结果表示函数执行的顺利与否(-1、0) 返回值可以为各种数据类型,如:int,float,double,char,a[数组],*a(指针),结构或类。写return是一种清晰的风格,可以防止一些意外的错误。有时候也是想中断函数执行,返回调用函数处。
返回本地变量的地址是危险的,返回全局变量或静态本地变量的地址是安全的,返回在函数malloc的内存是安全的,但是容易造成问题,最好的办法是返回传入的指针。
在函数中调用函数自己 :即自己return自己 = 递归
#include<stdio.h>//输出直到n项的斐波那契数列
int add(int n);int i;
int main(void) {
int n;
scanf("%d", &n);
int c;
for (i = 1; i <=n; i++) {//输出数列
c = add(i);//调用函数
printf("%d ", c);
}
return 0;
}int add(int n) {//递归
if (n == 1) {//第一位数是1
return 1;
}
else if (n == 2) {//第二位数也是1
return 1;
} else {
return (add(n-1) + add(n - 2));//第三位开始 等于它前两位相加 1+1=2 所以第三位是2
}
}
本地变量和全局变量的补充
-
本地变量的规则:
本地变量是定义在块内的:它可以是定义在函数的块内,也可以是定义在语句的块内,甚至可以随便拉一对大括号来定义变量。 但是 程序进入这个块之前,在这个块内定义的变量它是不存在的,离开这个块,它也随之消失。 所以 在块外面定义的变量在变量仍然有效,然而在块里边定义的变量出去块外边就无效了 注意 如果在块外边定义和里边同名的变量,里边的变量会覆盖外边的值(小覆盖大的) 不能再同一个块内定义同名变量,本地变量不会被初始化,参数再进入函数的时候就被初始化了
-
本地变量的规则:
没有做初始化的全局变量会得到零值,指针会得到NULL,只能用编译时刻已知道的值来初始化全局变量,它们的初始化再main函数之前。 注意 尽量不要使用全局变量来在函数之间传递参数和结果。尽量避免使用全局变量(丰田的案子),使用全局变量和静态的本地变量是线程不安全的
推荐阅读
-
全面解析 C# LINQ 的 From、Where、Select、Group、Into、OrderBy 和 Let Join
-
理解DVB数字电视系统:DVB-C、DVB-S和DVB-T的全面解析
-
玩转C语言基础:sizeof函数的全面解析
-
全面了解 C 语言函数:从创建到使用,包括参数传递和返回值解析
-
全面了解 C 语言函数:从创建到使用,包括参数传递和返回值解析
-
C函数的全面解析和知识点梳理(七)
-
玩转Unity3D游戏开发:C#中的委托、事件、匿名函数和Lambda表达式的全面指南(第7篇)
-
全面解析MySQL存储过程:变量、游标、存储函数、循环和判断语句,以及参数传递的方法
-
玩转C++:理解类与对象 - 这些关键知识点:对象模型、this指针的秘密、成员变量和成员函数独立存放、深入解析this指针、以及如何安全地使用空指针调用成员函数,还有const修饰的成员函数的作用
-
【2022新手指南】Java编程进阶之路 - 六、技术架构篇 ### MySQL索引底层解析与优化实战 - 你会讲解MySQL索引的数据结构吗?性能调优技巧知多少? - Redis深度揭秘:你知道多少?从基础到哨兵、主从复制全梳理 - Redis持久化及哨兵模式详解,还有集群搭建和Leader选举黑箱打开 - Zookeeper是个啥?特性和应用场景大公开 - ZooKeeper集群搭建攻略及 Leader选举、读写一致性、共享锁实现细节 - 探究ZooKeeper中的Leader选举机制及其在分布式环境中的作用 - Zab协议深入剖析:原理、功能与在Zookeeper中的核心地位 - RabbitMQ全方位解读:工作模式、消费限流、可靠投递与配置策略 - 设计者视角:RabbitMQ过期时间、死信队列与延时队列实践指南 - RocketMQ特性和应用场景揭示:理解其精髓与差异化优势 - Kafka详细介绍:特性及广泛应用于实时数据处理的场景解析 - ElasticSearch实力揭秘:特性概述与作为搜索引擎的广泛应用 - MongoDB认知升级:非关系型数据库的优势阐述,安装与使用实战教学 - BIO/NIO/AIO网络模型对比:掌握它们的区别与在网络编程中的实际应用 - Netty带你飞:理解其超快速度背后的秘密,包括线程模型分析 - 网络通信黑科技:Netty编解码原理与常用编解码器的应用,Protostuff实战演示 - 解密Netty粘包与拆包现象,怎样有效应对这一常见问题 - 自定义Netty心跳检测机制,轻松调整检测间隔时间的艺术 - Dubbo轻骑兵介绍:核心特性概览,服务降级实战与其实现益处 - Dubbo三大神器解读:本地存根与本地伪装的实战运用与优势呈现 ----------------------- 七、结语与回顾