函数
关于模块化编程(C语言的程序模块称为函数),我们需要注意的几点。
1,没有子函数的概念。函数之间都是并列的关系,不能把一个函数定义到另一个函数的函数体中。
2,当定义在下面的函数,而上面代码已经调用了,此时需要函数原型的声明。如果函数定义在上面,而使用在下面,可以不用函数原型声明。
3,函数的返回值类型就是告诉编译器函数运算完成后,返回值的数据类型,当调用函数时,就可以定义相应类型的变量来接收函数的返回值了。当函数被调用时,形参比照类型,开辟空间用来接收实参的值。函数内部定义的变量叫做局部变量。是保存程序中间运算结果的。
4,每个函数都是独立的,在内存中,函数调时都会开辟一个栈帧,在一个函数体中,不能直接使用另外一个函数体中定义的变量,如果想要用到其它函数内定义的局部变量,只能用到参数传递。函数调用时产生栈帧,函数调用结束后,栈帧内存将释放(也就是所有的形参和局部变量分配的空间将全部释放)。
5,按数目一致,类型相同,顺序相同这样的原则进行函数调用,进行实参和形参的结合,正确的结合是函数调用的关键。
一、函数概述
函数定义:函数头+函数体
函数头: 说明函数的类型、名字、参数及参数的类型。如: int max( int x, int y )
函数体: 由“{ }”括起,包括变量声明和执行部分
分类: 标准库函数和用户定义的函数。
说明:(1)一个C程序可由一个主函数和若干个其他函数组成
(2) 程序执行时从main函数开始, 根据需要, main函数调用其他函数, 其他函数也可以互相调用。
(3)同一个函数可以被一个或多个函数调用任意多次。最后由main函数结束程序的运行。
(4)不能调用main函数
举例:用上面的设计原则,重新实现pmp程序,大于35 输出你好成熟,小于35 ,输出你好年轻。
#include <stdio.h>
void welcome();//因为C语言是先声明再使用的语言,所以对函数原型先声明一下。
int ask();
void pmp(int age);
void main()
{
int age = 0;
welcome(); //函数调用,一串字母后面跟着小括号,然后还一个分号,我可以基本判断出来它是函数调用。
age = ask(); //询问年龄,需要得到年纪值,那么我需要定义个一个变量,保留这个值。
//ask()也是一个函数调用,它是把ask()返回的值赋值给age。
pmp(age); //小括号里面是实际参数(实参),函数调用。
}
void welcome() //因为welcome,就是一个欢迎而已,所以我定义一个无参数,无返回值这样的函数原型。
{
printf("欢迎来蚂蚁软件学习
");
printf("今天天气太好了。
");
}
int ask() //因为 ask(),实现问询年纪的功能,所以不需要提供参数,所以根据需求来讲,就应该设计成无参数。
//但需要问出来年纪值,所以函数可以设计一个带返回值的函数原型。
{
int age = 0; //函数内部定义的变量叫做局部变量。是保存程序中间运算结果的。
printf("您今年多大了?
"); //因为每个函数都是独立的,在内存中,函数调用后都会开辟一个栈帧,所以此处age
scanf("%d",&age); //和main 函数的age,是两个不同的存储单元,只是名字相同而已。
return age //在一个函数体中,不能直接只用另外一个函数体中定义的变量,如果想要用到其它函数
} //内定义的局部变量,需要用到参数传递。
//因为PMP函数,根据需求来讲,对于这个函数来讲,肯定需要一个年纪值,我才可以拍。所以,就需要设计
//一个形式参数了,返回值就是void,形式参数干什么用的呢?当函数被调用时,形参比照类型,开辟空间用来
//接收实参的值。
void pmp(int age)
{
if(age > 35) //形参的这个AGE可以取任何名字都可以,他仅仅是形参分配空间的名字而已。
{
printf("您好成熟啊"); //它属于被调用函数的变量。所以此处的AGE和main函数里面的AGE,是不同的内存块。
}
else //这里仅仅名字相同而已。你也可以取不同的名字。
{
printf("您好年轻啊");
}
}
二、函数返回值
格式: return ( 表达式 ); return 表达式 ;
作用: (1)将表达式的值返回给调用函数 结束被调用函数的执行, 并把程序的控制返回到调用它的函数。
(2)结束函数执行
注意:(1)函数的返回值的类型应与函数的类型一致。如不一致, 以函数类型为准, 对返回值进行类型转换, 然后传送给调用函数。
(2)一个函数可以有多个return语句, 但只可能执行其中一个。
三 函数参数
正确地进行结合是函数调用的关键。结合时应注意: 实参与形参的个数相等, 顺序一致, 类型应相同。
1、实参与形参结合的原则是:
当实参为常量、变量、表达式或数组元素时,对应的形参只能是变量名。
当实参为数组名时, 所对应的形参必须是同类型的数组名或指针变量。
2、数组名作为函数参数
实参与形参之间的数据传递是地址传递。
可用多维数组名作实参和形参。在被调用函数中对形参数组定义时, 可以指定每一维的长度,也可省略第一维的长度。
四、函数原型
用户自定义函数在调用前, 必须对该函数进行声明。函数声明就是函数原型
返回值类型:决定函数运行的结果 接收数据时 数据类型要一致
函数名:功能
参数个数及类型
所有的数学库中的函数 返回值类型都是double,也可以传入整型 用整型变量接收结果
五 递归函数
概念:函数的递归调用是指在一个函数调用过程中又直接或间接调用自己,
语法:
(1)控制递归的深度(一个if选择)约束条件可以有多个
(2)通项(必然包括调用函数自身)
注意:递归函数只有最后一次调用执行完才开始释放 因为栈是先进后出
作用:其实递归是为了解决一些不好写的逻辑,简化程序。(可以用递归解决的问题,用循环也可以 但是难度不在一个等级)
六、作用域
复合语句内定义的变量 作用范围仅限于本复合语句 如其上层有同名的变量 互不影响
全局变量:也叫外部变量,是指定义再函数外的变量 通常定义再文件首部,作用范围指从定义的位置到文件末尾皆可使用 下方的函数内都可以使用
局部变量: 函数内定义的变量称谓局部变量。仅限于本函数使用
以上三种作用域都属于就近原则
七 操作系统的内存分
栈区:是存储函数相关的区域 2M 内存较小
特性: 执行时分配,使用完即释放 由系统操控 结合面向过程来的先进后出
关键字 auto(自动变量:相当于局部变量) register(寄存器)
堆区:是malloc函数分配的空间 较大 以G来说
特性: 由程序员手动分配,手动释放,空间大小可控 由程序员操控 一般用分配大量的空间或指定大小的空间时使用
全局/静态区: 存放全局变量 静态变量
特性:在程序编译后 运行前空间已经分配 程序运行结束后释放空间
关键字: static 静态 extern 外部声明
常量文本区/:(二进制代码)
特性: 不可改
数值常量和字符串常量分配到常量文本区,他的特点是不能修改。你如果修改如果编译时编译器识别出来了,就会编译不通过。如果编译器没有识别出来你在修改常量,那么程序运行时就会崩溃。常量是坚决不能修改的。他的内存也是始终存在的,等到程序结束后,由操作系统统一释放。
程序代码区:保存代码逻辑。
库函数学习
(1)使用库函数: 1.包含库函数的头文件 2.函数的功能 3.函数的参数个数及类型 4.返回值类型
(2). 函数原型 返回值类型:决定函数运行的结果 接收数据时 数据类型要一致
函数名:功能
参数个数及类型
所有的数学库中的函数 返回值类型都是double,也可以传入整型 用整型变量接收结果
(3)、标准库函数
C标准库由在15个头文件中声明的函数、类型定义和宏组成,每个头文件都代表了一定范围的编程功能。
合格:<stdio.h>、<ctype.h>、<stdlib.h>、<string.h>
熟练:<assert.h>、<limits.h>、<stddef.h>、<time.h>、<rand.h>
优秀:<float.h>、<math.h>、<error.h>、<locale.h>、<setjmp.h>、 <signal.h>、<stdarg.h>
1.字符串处理函数 (https://www.cnblogs.com/si-lei/p/9459209.html)
#include “string.h”
strcat(字符数组1, 字符数组2) ;
strcpy(字符数组1, 字符串2) ;
strlen(字符串) ;
strlwr(字符串) ;
strupr(字符串) ;
strcmp(字符串1, 字符串2) ;
2、malloc函数
作用:动态分配内存。堆中分配一个长度为size的连续空间,内存的大小由参数决定
函数原型:#include<stdlib.h>
void *malloc(num); //num
说明:(1)函数的返回值是一个指向所分配内存起始地址的指针,类型为void *型。说得简单点就是,malloc函数的返回值是一个地址,这个地址
就是动态分配的内存空间的起始地址。如果此函数未能成功地执行,如内存空间不足,则返回空指针NULL。
(2)如何用malloc动态分配内存
int *p = (int *)malloc(4);//请求系统分配4字节的内存空间,并返回第一字节的地址,然后付给指针变量 p。
realloc()函数
作用:(1)更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。
(2)把第一个参数指针指向的之前分配的内容复制到新配的内存中
函数原型:#include<stdlib.h>
realloc(void *__ptr, size_t __size) //两个参数:一个是包含地址的指针(该地址由之前的malloc()函数返回),另一个是要新分配的内存字节数。
说明:(1)新内存大于原内存,则原内存所有内容复制到新内存,如果新内存小于原内存,只复制长度等于新内存空间的内容。
(2)realloc()函数的第一个参数若为空指针,相当于分配第二个参数指定大小的新内存空间
(3)如果是将分配的内存扩大,则有以下3种情况:
如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
如果申请失败,将返回NULL,此时,原来的指针仍然有效。
calloc函数
作用:根据类型来分配空间
函数原型:#
void* calloc(size_t num,size_t size); num为元素个数,size为每个元素的字节长度
说明:在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
跟malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
3、itoa()函数和atoi()
itoa():将整型值转换为字符串
函数原型,char * itoa(int a,char * output,int radix)
第一个参数,表示要转换成字符串的那个数字。例如15
第二个参数,表示转换后的字符串,
第三个参数,表示按多少进制来转换。
返回值,就是存放转换后的字符串内存的首地址。
例如调用 itoa(15,output,16) 得到f
i itoa(15,output,8) 得到21字符串
itoa(15,output,2) 得到1111字符串
atoi():将字符串转换为整型值。
函数原型: int atoi(const char *str ); //str a要进行转换的字符串
函数功能:把字符串转换成整型数。
返回值:每个函数返回 int 值,此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atoi的返回值为 0