函数介绍
1.1函数概述
- 函数概念:是指完成某种特定功能的程序代码。
- 主次顺序:根据函数的主次顺序,可以分为主函数(唯一 main函数)和子函数(其它)。工程中只有一个main(忽略Linux进程方面知识)。
- 函数分类:根据是否由自己编写,分为库函数(系统调用函数、标准库函数、第三方库函数)和自定义函数。
注:主函数在程序中是负责整体调度,一般不参与功能性作用。
Int main() { 功能代码; add(); 功能代码;mul(); } |
凡是功能性的代码尽量不要出现在主函数中。子函数主要是负责完成功能性任务,功能性代码最好都写在子函数里面。在定义语句中,除了变量名以外的内容,都是数据类型。
1.2函数特性
- 模块化: 一个功能封装成一个函数,可以很方便的将这个功能移植到其他程序中。
- 独立性:程序当中的每个函数彼此之间都是独立的。
- 通用性:一个函数不仅是为了解决一个问题,而是为了解决一类相似的问题,函数必须具有通用性。
函数名一定要合理;
1.3字符串标准库函数—string.h
查询方式有两种:man 3函数名 函数手册:C语言函数大全语法
- 字符串长度计算函数(strlen)/sizeof对比
头 文 件: #include <string.h> 函数原型: size_t strlen(const char *s); 函数功能:函数返回字符串s的长度( 即空值结束符之前字符数目)。 函数参数:s字符串首地址 //char buff[]=”hello”; buff就是字符串hello的首地址 “abcd”编译器中提供”abcd”首地址; 返 回 值: 函数返回字符串s的长度; |
示例:
#include<stdio.h> #include<string.h> int main() { int len; char arrBuff[100]={0}; printf("请输入一串字符串\n"); gets(arrBuff); //测量字符串长度string len len = strlen(arrBuff); printf("%d\n",len); printf("*******************************************\n"); printf("\"abcd\":%d\n",strlen("abcd")); //4字节长度 求字符串长度 printf("\"ab'0'cd\":%d\n",strlen("ab\0cd")); //2字节长度 求字符串长度’\0’作为结束标志 printf("***************strlen与sizeof**************\n"); printf("strlen:%d\n",strlen(arrBuff)); //求字符串长度 printf("sizeof:%d\n",sizeof(arrBuff)); //100字节 占内存大小 return 0; } C库中采用终结地址减起始地址得到长度数值; |
- 字符串对比函数(strcmp)
头 文 件: #include <string.h> 函数原型: int strcmp( const char *str1, const char *str2 ); 函数功能: 比较字符串str1 and str2 函数参数:str1:比较的第一个字符串首地址; str2:比较的第二个字符串首地址; 返 回 值:返回数值有多种情况<0情况 =0情况 >0情况; 小于less than 0 str1 is less than str2 “abcd “-“abce” 示例:printf("%d\n",strcmp("abcd","abce")); //<0 等于equal to 0 str1 is equal to str2 示例: printf("%d\n",strcmp("abcd","abcd")); //=0 大于greater than 0 str1 is greater than str2 示例: printf("%d\n",strcmp("abce","abcd")); //>0 扩展函数: 头 文 件:同上 函数原型: int strncmp( const char *str1, const char *str2, size_t count ); 函数功能: 功能:比较字符串str1 和 str2中至多count个字符。 返 回 值:同上 返回数值有多种情况<0情况 =0情况 >0情况; |
示例:
#include<stdio.h> #include<string.h> int main() { printf("*******************strcmp********************************\n"); printf("%d\n",strcmp("abcd","abce")); //<0 printf("%d\n",strcmp("abcd","abcd")); //=0 printf("%d\n",strcmp("abce","abcd")); //>0 printf("*******************strncmp********************************\n"); printf("%d\n",strncmp("abcd","abcdef",6)); //<0 \0 -'e' printf("%d\n",strncmp("abcd","abcdef",4)); //=0 printf("%d\n",strncmp("abcdf","abcdef",5)); //>0 return 0; } |
- 字符串拼接函数(strcat)
头 文 件: #include <string.h> 函数原型: char *strcat( char *str1, const char *str2 ); 函数功能:函数将字符串str2 连接到str1的末端,并返回指针str1; 函数参数:str1:目标空间,str2为源字符串空间,st2拼接到str1后边; 返 回 值:str1首地址 注意点:str1空间应该要比原str1+str2空间大1个字节; |
示例:
#include<stdio.h> #include<string.h> int main() { char str1[100]="123456"; char str2[100]="abcd"; //进行检验长度是否有效 if(100 > strlen(str1) + strlen(str2)) //防止数组越界 优化代码/知识比较丰富 { //可以进行拼接 strcat(str1,str2); printf("%s\n",str1); } else { printf("str1空间不足无法实现拼接\n"); } return 0; } |
- 字符串复制函数(strcpy)
头 文 件: #include <string.h> 函数原型: char *strcpy( char *to, const char *from ); 函数功能: 复制字符串from 中的字符到字符串to,包括空值结束符。返回值为指针to。 函数参数: from源字符串 to目标字符串 返 回 值:目标字符串首地址 注意点:to空间要比字符串from长度大1字节; |
示例:
#include<stdio.h> #include<string.h> int main() { char str1[100]={0}; char str2[100]="abcd"; if(sizeof(str1) > strlen(str2)) { strcpy(str1,str2); printf("%s\n",str1); } else { printf("空间不足\n"); } printf("****************************************************\n"); char str3[100]="1234567891234567"; char str4[100]="abcd"; strcpy(str3,str4); printf("%s\n",str3); //它会把str4最后的\0赋值到str3中 //验证覆盖写:"1234567891234567" "abcd"-->“abcd\067891234567” for(int i =0;i < 20; i++) { printf("%c",str3[i]); } printf("\n"); return 0; } |
以上为字符串相关函数;
1.4数学标准库函数—math.h
- 计算绝对值函数(abs)
头 文 件: #include <stdlib.h> 函数原型: int abs( int num ); 函数功能: 函数返回参数num的绝对值 函数参数: num进行转换的原值 返 回 值: 函数返回参数num的绝对值 |
示例
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { int num = -2; //原始写法 if(num < 0) num = -num; printf("%d\n",num); //函数写法 printf("%d\n",abs(num)); return 0; } |
- 计算次方值函数(pow)
头 文 件: #include <math.h> 函数原型: double pow( double base, double exp ); 函数功能:函数返回以参数base 为底的exp 次幂。如果base为零或负(验证可以)和exp 小于等于零(验证可以)或非整数(.0没问题.非零有问题)时,产生域错误。如果溢出,产生范围错误。 函数参数: base底数 exp为幂 返 回 值: 函数返回运算后结果/错误 |
示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int main() { printf("%lf\n",pow(2,3)); return 0; } |
- 计算平方根函数(sqrt)
头 文 件: #include <math.h> 函数原型: double sqrt( double num ); 函数功能: 函数返回参数num的平方根。如果num为负,产生域错误。 函数参数: num为需要平方根的数值 返 回 值: 函数返回运算后结果/错误 |
示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int main() { printf("%lf\n",sqrt(9)); return 0; } |
- 余弦函数值计算函数(cos)
头 文 件: #include <math.h> 函数原型: double cos( double arg ); 函数功能:函数返回参数arg的余弦值,arg以弧度表示给出。 函数参数: 弧度数值 返 回 值: 余弦值 |
示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int main() { printf("%lf\n",cos(3.14/2)); return 0; } |
- 正弦函数值计算函数(sin)
头 文 件: #include <math.h> 函数原型: double sin( double arg ); 函数功能: 函数返回参数arg的正弦值,arg以弧度表示给出 函数参数: arg为弧度值 返 回 值: 函数返回运算后结果/错误 |
示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int main() { printf("%lf\n",sin(3.14/2)); return 0; } |
2.1函数应用
2.1.1函数组成
函数组成:函数声明(.h) 函数定义(.c) 函数调用(以上)
- 函数声明(目录)
格式:函数类型 函数名(形参列表或参数列表);
形参列表:有则按照形参格式进行编写,无则不写或者写void;
一般函数声明放在.h中,部分也会放到.c源文件中;
作用:告诉程序有这么一个函数可以调用,用的时候到这里找即可;
- 函数定义(内容)
格式:函数类型 函数名(形参列表) { 函数体(功能代码); } |
形式参数:按照形参格式进行编写,没有不写或者写void;
含义:编写实现所需功能的程序代码;(组成:运算符、九条语句)
- 函数调用(使用)
格式: 函数名(实际参数(实参))/函数名();
-->实参有就写,没有只写括号就可以了
含义:在程序中使用这个函数。
总结:函数定义:参数为形参
函数声明:形参;
函数调用:实参;
函数返回值类型: 函数定义(写) 函数声明(写) 函数调用(不写);
2.1.2函数模型
- 函数定义
格式: 函数类型 函数名(形参列表) { /* 功能代码 */ } |
- 函数类型:实际就是返回值的类型,函数内部数据的出口,决定了函数内部返回一个什么类型的数据。通常使用关键字return返回。
注:如果函数没有函数类型,程序默认是有返回值类型的,默认是int型。
格式: 函数名(形参列表) { /* 功能代码 */ } 等同于: int函数名(形参列表) { /* 功能代码 */ } |
- 函数名 :自定义标识符,命名规则同变量(大小写英文字母、数字、美元$、下划线_),建议使用突出函数功能的词汇(望文生义原则)。另外函数名是函数的入口地址,在使用函数的时候,计算机会自动到函数的所在位置。(注意:函数名是函数的入口地址;)
- 形式参数:形式参数也称为形参,是外部参数(数据)传达到函数内部的入口。外部数据必须先将它的值拷贝给形参,形参再传递给函数内部。使用“数据类型 变量名”的格式去定义形式参数。形参的个数可以是多个,也可以是一个或者没有形参。如果有多个,相邻两个之间用逗号分隔(形参个数尽量保持在5个以下)。
注:形参的数据类型不可省略,形参不可直接赋值。
举例:int add(int num1,int num2) × //不能省略int 也不能有默认参数列表
int add(int num1=1,int num2=2) × //也不能有默认参数列表
- 函数功能代码: 具体实现这个函数功能的程序代码。
- 函数声明
格式:函数类型 函数名(形参);
注:函数声明的格式跟函数定义的格式一样,由于函数声明是一条语句,所以要在最后面加上分号(;)。
- 函数调用
- 格式 :函数名(实际参数)/函数名();
- 实际参数:函数在运行过程中,实际需要的数据。
- 实参与形参关系
注:
①形参只有数据类型,没有值,是函数在声明、定义时使用的参数。
②实参是一个确定的值,不能是随机数,是函数调用时的参数。在调用函数的时候,实参向形参传递(实参向形参单向值传递),将函数实参的值赋给形参变量,然后形参变量带入函数内部程序运算,得到一个结果。
③实参和形参的个数、顺序、类型必须严格保持一致。当被调用的函数在函数调用之前(函数定义在函数调用之前),可以省略函数声明。但是,当被调用的函数在函数调用之后(函数定义在函数调用之后),则必须进行函数声明。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //当被调用的函数在函数调用之前(函数定义在函数调用之前),可以省略函数声明。但是,当被调用的函数在函数调用之后(函数定义在函数调用之后),则必须进行函数声明。 int add(int num1,int num2); /* int add(int num1,int num2) { return num1+num2; }*/ int main() { printf("%d\n",add(1,2)); return 0; } int add(int num1,int num2) { return num1+num2; } |
2.1.3 函数应用
函数应用的6个步骤:
- 函数类型:首先确定函数类型,相当于确定返回值类型。这个函数是否需要返回值:如果需要,需要什么类型的返回值;如果不需要返回值,函数类型写void;
举例:void function();
- 函数名称:自定义标识符,命名规则同变量,建议使用突出函数功能的词汇
- 形式参数:确定是否需要形参,子函数的运行过程是否需要外部数据参与辅助,如果需要,需要什么类型的,要几个;如果不需要,不写或者写void。
对于形参要合理,个数不是越多越好;
- 编写功能代码;
- 函数声明;
- 函数调用:在需要使用的地方调用即可:函数名(实参);
例:判断一个数是否是水仙花数
打印所有水仙花数。所谓水仙花是指一个三位数,其各位数字的立方和等于该数。
例:153=1*1*1+5*5*5+3*3*3=1+125+27
- 使用主函数实现
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //打印所有水仙花数。所谓水仙花是指一个三位数,其各位数字的立方和等于该数。 //例:153=1*1*1+5*5*5+3*3*3=1+125+27 int main() { for(int i = 100 ; i < 1000; i++) { int bai = i/100; int shi = i%100/10; int ge = i %10; if(bai*bai*bai+shi*shi*shi +ge*ge*ge == i) { printf("水仙花数值:%d\n",i); } } return 0; } |
- 使用子函数实现
- 确定是否需要返回值
①不需要 ②需要,需要什么样的返回值
- 确定函数名
- 确定是否需要形参(是否需要外部给出一个值到子函数内部去运算)
①不需要 ②需要,需要什么样的值(数据类型),需要多少个(数量)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //打印所有水仙花数。所谓水仙花是指一个三位数,其各位数字的立方和等于该数。 //例:153=1*1*1+5*5*5+3*3*3=1+125+27 unsigned short checkNarcissus(int num); /************************************************************* 函数名称:unsigned short checkNarcissus(int num) 函数功能:检验num值是否为水仙花数值 函数参数:num 数值 返 回 值:如果为真为水仙花数值 如果为假不是水仙花数值 **************************************************************/ unsigned short checkNarcissus(int num) { int bai = num/100; int shi = num%100/10; int ge = num %10; if(bai*bai*bai+shi*shi*shi +ge*ge*ge == num) return 1; else return 0; } //检验三位数值中的水仙花数 void check100_999_Narcissus(void) { for(int i = 100; i< 1000; i++) { if(checkNarcissus(i)) { printf("%d:水仙花数值\n",i); } } } int main() { check100_999_Narcissus(); return 0; } |
注:return的作用:
1)结束子函数;
2)带回一个返回值(只能返回一个值),返回到函数调用的地方。在函数中,只能返回一个数据,在程序的什么位置调用这个函数,这个函数的返回值,就返回到什么位置。当函数内部定义的返回值类型和函数类型不一致,则以函数类型为准。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //打印所有水仙花数。所谓水仙花是指一个三位数,其各位数字的立方和等于该数。 //例:153=1*1*1+5*5*5+3*3*3=1+125+27 unsigned short checkNarcissus(int num); /************************************************************* 函数名称:unsigned short checkNarcissus(int num) 函数功能:检验num值是否为水仙花数值 函数参数:num 数值 返 回 值:如果为真为水仙花数值 如果为假不是水仙花数值 **************************************************************/ unsigned short checkNarcissus(int num) { int bai = num/100; int shi = num%100/10; int ge = num %10; if(bai*bai*bai+shi*shi*shi +ge*ge*ge == num) return 1.0; 当函数内部定义的返回值类型和函数类型不一致,则以函数类型为准。 else return 0.0; } //检验三位数值中的水仙花数 void check100_999_Narcissus(void) { for(int i = 100; i< 1000; i++) { if(checkNarcissus(i)) { printf("%d:水仙花数值\n",i); } } } int main() { check100_999_Narcissus(); return 0; } |
2.1.4 函数两句话
- 一个函数如果有类型,那么这个函数就必须有返回值。定义函数的类型就是为了定义函数返回值的类型,函数类型和函数返回值的类型必须保持一致,如果不一致,返回值的类型会强制向函数数据类型转换。
- 函数如果有形参,那么函数调用的时候,必须有实参向形参传递。定义形参的类型、顺序就是为了定义实参的类型、顺序(如果不一致,实参的数据类型会强制向形参的数据类型转换),函数形参为空,则不需要实参传递。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> void function() { printf("function\n"); } fun() //默认函数类型为int 类型 { return 100; } int main() { int A =99; function(12); //可以传递但是用不到 有些编译器会报错 function(A); printf("%d\n",fun()); return 0; } |
练习:使用子函数编写一个200~400之间不能被3整除的数的和。
- 无返回值,无形式参数
2)有返回值,无形式参数
- 有返回值,有形式参数
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //练习:使用子函数编写一个200~400之间不能被3整除的数的和。 //1)无返回值,无形式参数 void function200_400DEV3(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } printf("%d\n",sum); } //2)有返回值,无形式参数 int function200_400DEV3_rev(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } //3)有返回值,有形式参数 int functionEV3_rev(int start,int end) { int sum = 0; for(int i = start ; i <= end; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } int main() { function200_400DEV3(); printf("%d\n",function200_400DEV3_rev()); printf("%d\n",functionEV3_rev(200,400)); return 0; } |
3.1函数和变量的作用范围
3.1.1函数作用范围
根据函数的作用范围,可以把函数分为:全局函数和局部函数
- 全局函数
全局函数是指这个函数对整个工程可见,在整个工程的任意一个地方都可以被调用。在函数类型之前加上关键字“extern(外部的)”,则表示声明该函数为全局函数。
工程文件内容 | ||
main.c | fun.c | fun.h |
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "fun.h" int main() { //调用部分 function200_400DEV3(); printf("%d\n",function200_400DEV3_rev()); printf("%d\n",functionEV3_rev(200,400)); return 0; } | #include "fun.h" //练习:使用子函数编写一个200~400之间不能被3整除的数的和。 //1)无返回值,无形式参数 void function200_400DEV3(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } printf("%d\n",sum); } //2)有返回值,无形式参数 int function200_400DEV3_rev(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } //3)有返回值,有形式参数 int functionEV3_rev(int start,int end) { int sum = 0; for(int i = start ; i <= end; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } | #include <stdio.h> //全局函数 函数定义部分:fun.c 函数声明:fun.h 函数调用:main.c extern void function200_400DEV3(void); //void function200_400DEV3(void); extern int function200_400DEV3_rev(void); //int function200_400DEV3_rev(void); extern int functionEV3_rev(int start,int end); //int functionEV3_rev(int start,int end); |
gcc main.c fun.c -o app
注:所有函数都是默认为extern型(即全局函数),可以省略关键字不写。
- 局部函数
局部函数是指这个函数只能被当前所在的程序源文件所使用,工程中的其他源程序文件是无法对这个函数进行调用。在函数类型之前加上关键字“static(固定的、静态的)”,则表示声明该函数为局部函数。
工程文件内容 | ||
main.c | fun.c | fun.h |
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "fun.h" int main() { //调用部分 function200_400DEV3(); × printf("%d\n",function200_400DEV3_rev()); × printf("%d\n",functionEV3_rev(200,400)); × return 0; } | #include "fun.h" //练习:使用子函数编写一个200~400之间不能被3整除的数的和。 //1)无返回值,无形式参数 void function200_400DEV3(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } printf("%d\n",sum); } //2)有返回值,无形式参数 int function200_400DEV3_rev(void) { int sum = 0; for(int i = 200 ; i <= 400; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } //3)有返回值,有形式参数 int functionEV3_rev(int start,int end) { int sum = 0; for(int i = start ; i <= end; i++) { if(i % 3 == 0) { sum +=i; } } return sum; } | #include <stdio.h> //局部函数 函数定义部分:fun.c 源文件中使用 函数声明:fun.h 不能在其他源文件中.c使用 static void function200_400DEV3(void); static int function200_400DEV3_rev(void); static int functionEV3_rev(int start,int end); |
3.1.2变量作用范围
根据变量的作用范围,可以把变量分为:全局变量和局部变量。
- 全局变量
全局变量是指在函数体外面定义的变量即为全局变量,变量的初始值为0。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //全局变量:是指在函数体外面定义的变量即为全局变量,变量的初始值为0。 int A; int main() { printf("%d\n",A); return 0; } |
全局变量的作用时间(释放数据空间时间)从工程运行开始,到整个工程结束。全局变量的作用范围是整个工程可见,但是如果想让工程中的其他源文件使用这个全局变量,必须使用关键字“extern(外部的)”把全局变量声明为外部全局变量,格式为:“extern 数据类型 变量名”。同时全局变量只能使用常量或常量表达式对全局变量进行初始化赋值。
外部全局变量用法 | ||
Main.c | Fun.c | Fun.h |
#include <string.h> #include <stdlib.h> #include <math.h> #include "fun.h" //全局变量:是指在函数体外面定义的变量即为全局变量,变量的初始值为0。 int B; //全局变量 未声明为外部变量仅能在当前源文件中使用 int main() { printf("%d\n",A); return 0; } | #include "fun.h" int A = 99; //定义变量 | #include <stdio.h> //将A声明为外部变量 extern int A; //声明 |
注:使用“extern”对全局变量声明仅仅是告诉编译器,这个全局变量是一个外部变量,其他的源程序文件可以对这个变量进行访问,而不是去定义一个全局变量。在声明全局变量之前,必须要保证这个全局变量已经定义。另外在声明外部全局变量的时候,不允许对变量进行赋值。
- 局部变量
局部变量是指在函数体内部定义的变量,变量的初始化为随机数(垃圾值)。局部变量的作用时间是从变量定义的位置(函数调用)开始,到所在的程序运行结束。局部变量的作用范围只有在定义的函数内有效。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> void function() { int A =88; //A从定义部分生效 printf("A:%d\n",A);//A在}前失效 作用范围定义} { //范围符号 int B =77; printf("B:%d\n",B); } //printf("B:%d\n",B);//A在}前失效 作用范围定义} } int main() { function(); return 0; } |
- 静态变量
静态变量是局部变量的一个特殊例子,在局部变量的数据类型之前加上关键字“static”,则可以把局部变量变成一个静态变量。静态变量有着全局变量的生命周期(作用时间),局部变量的作用域(作用范围)。在函数内部定义,一旦初始化,在程序运行的过程中,一直存在,程序即使跳出了该函数,也不会被释放,在下次调用时不会被重新初始化。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> void function() { //int A =88; 每次函数运行时都会运行 //静态变量 static int A =88; 只运行一次 后期不再运行 static int A =88; //开机运行初始化88次 A++; printf("A:%d\n",A); } int main() { function(); function(); function(); function(); function(); return 0; } |
注:
- 当全局变量和局部变量重名时,局部变量有效。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int A = 99; //全局变量 void function() { int A =88; //局部变量 printf("A:%d\n",A); //就近原则 局部有效 } int main() { function(); return 0; } |
- 在全局变量之前加上关键字“static”,全局变量就会被定义成一个全局静态变量,表示该全局变量只能在本源文件内使用。
- 局部变量不能使用“extern”来修饰。
3.1.3 C语言开辟内存的五大区域
五大区:堆区、栈区、全局变量区、文本常量区、代码区;比较重要
做到:了解每一个区特点,存放变量时:作用范围、作用时长;动脑筋(代码能写完->优化->中级工程->高级工程师)
栈区:在执行函数时,函数(包括main函数)内局部变量以及函数的形参的存储单元空间都是在栈区中创建,栈区中的变量控制由编译器自动开辟和在函数运行结束之后自动释放。
作用时长: 创建生效->在函数运行结束;
作用范围: 由于是局部变量作用范围为变量创建时-函数}时
操 作 者: 由编译器;
使用场景:函数(main、子函数)内部定义的变量(去除static修饰的情况)、函数的形参
注 意 点:变量使用栈区的初值一定要规定,如果不规定出现垃圾值后果不堪想象;
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int A = 99; //全局变量 void function() { int A =88; //临时开辟一块内存空间 局部变量---->栈区 printf("A:%d\n",A); //就近原则 局部有效 } int fun(int num) //栈区 临时开辟一块内存空间 { return num; } int main() { int NUM =99; //临时开辟一块内存空间 ---->栈区 在主函数中NUM 作用时长 NUM创建 main结束==工程结束 function(); return 0; } |
堆区:堆区也称动态内存分配区。堆区的空间程序在运行的时候由程序员使用“malloc”函数去申请任意大小的内存空间,空间一旦申请完成,需要程序员自己负责在适当的时候使用“free”函数去释放内存,如果开辟后没有进行释放,到整个工程结束后自动释放。
作用时长:malloc创建成功------>free释放成功时结束/整个工程结束后自动释放;
作用范围: 只要能够找到相关起始地址(看程序人员的限制)
操 作 者:程序员(调用malloc函数实现);
使用场景:动态申请->灵活度最高,以数组为例:char buff[100]长度是受到限制,如果采用malloc/calloc等等灵活的创建/修改长度,节省内存避免空间浪费;
注 意 点: 没有释放->内存泄漏(空间不足);
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int main() { char str1[] ="hello"; //栈区 char str2[] = "C/C++"; //栈区 //实现拼接 //char str3[????];//定义多长 虽然肉眼一眼能够看出,在程序中-->未知 char * p = (char *)malloc(strlen(str1)+strlen(str2)+1);//11字节(堆区) p空间栈区(了解) 节省内存 避免内存浪费 strcpy(p,str1); strcpy(p+strlen(str1),str2); printf("%s\n",p); free(p); return 0; } |
全局变量区:全局变量区也称为全局/静态区,主要是存放全局变量和静态变量。全局变量区中的变量由编译器自动开辟和当整个工程结束之后自动释放。
作用时长:(函数外定义的变量+extern外部全局变量)全局变量/静态(static)变量(第一次调用该函数)创建成功;整个工程结束后自动释放;à(工程开始->工程结束)
作用范围: 全局变量(不加extern修饰的)只对本源文件有效;外部全局变量对整个工程有效;静态变量对于函数内有效;({}) 在main中咱们不用静态的/仅仅在子函数中使用;
操 作 者:编译器;
使用场景:多个源文件/函数中使用该变量/数组可以考虑使用全局变量;对于函数中某些变量需要进行记忆的话可以考虑使用静态变量;
注 意 点: 尽量少使用全局变量/安全性不高;(地址一致不做修改 恶意修改)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> char str1[] ="hello"; //全局变量区 char str2[] = "C/C++"; //全局变量区 int fun() { printf("%s\n",str1); } int main() { fun(); //实现拼接 //char str3[????];//定义多长 虽然肉眼一眼能够看出,在程序中-->未知 char * p = (char *)malloc(strlen(str1)+strlen(str2)+1);//11字节(堆区) p空间栈区(了解) 节省内存 避免内存浪费 strcpy(p,str1); strcpy(p+strlen(str1),str2); printf("%s\n",p); free(p); return 0; } |
常量区:常量区是一块比较特殊的数据存储区,在程序运行的过程中,只允许程序去访问,不允许程序进行修改(只读)。常量区主要存放的是文字常量。
作用时长:工程开始->工程结束
作用范围: 要看接收数据首地址的变量范围(举例中p的范围)
操 作 者: 编译器决定;
使用场景: 常用字符串/字符/常量;
注 意 点: 只能读取内容不能写入内容;
程序代码区:程序代码区主要是用于存放二进制代码。程序段;只能读取不能修改; B = A+A
4.1 递归函数
4.1.1递归函数概述
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件。
当函数在一直递推,直到遇到墙后返回,这个墙就是结束条件。
在函数内部程序中,自己调用自己,这种函数我们称为递归函数。递归函数的特点是使用代码简单,但是在运行程序过程中十分耗内存。(递归的时候,每次调用一个函数,计算机都会为这个函数分配新的空间,这就是说,当被调函数返回的时候,调用函数中的变量依然会保持原先的值,否则也不可能实现反向输出。)
上面内存消耗;
4.1.2递归函数应用
- 数值列:1 2 3 4 5 6 7 8 9 ?N
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int getNum(int n) { //递归条件 if(n > 1) return getNum(n-1)+1; //结束条件 ==1 else return 1; } int main() { printf("%d\n",getNum(100)); return 0; } |
例:有一对兔子,从出生后第3个月开始,每一个月生一对兔子,新生的小兔子在长到第3个月开始每个月又生一对兔子,假如兔子不死,问每个月的兔子总数是多少。
规律:
1
1
1(1)
1(1+1)
1(1+1+1) 1(1)
1(4) 1(2) 1(1)
1(5) 1(3) 1(2) 1(1)
…….
1、1、2、3、5、8…. 斐波那锲数列;
自第三个月开始,当月的兔子的数量是前两个月兔子的数量之和
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> int getSum(int n) { if(n > 2) return getSum(n-1)+getSum(n-2); else if(n ==2) return 1; else return 1; } int main() { printf("%d\n",getSum(5)); return 0; } |
作业还没做完:赶紧做;今下午复习+作业;明天考试(作业讲完)+期中考试;
80分及格呦
好好复习
同学疑惑:
- 刚开始学习C语言/编程语言的第一难点找不到思路;作业讲解时(题中提供条件 实现什么结果 有什么规律)
- 通过九条语句/运算符去实现相关逻辑;
- 范围:考虑循环(次数/目的)
- 实现条件判断:条件选择语句 范围条件:if else 具体数值:switch
- 出现函数返回return 不达标并且在循环中则使用break 只中止该次循环continue
- 每个人都有每个人思维/代码写越简洁/bug越少 越好;
For/while 运算符三目运算符 条件选择语句效率高
变量定义一定要赋初值 int a =0; int a;
运算符:+-效率要比*/%效率高 */%程序实现的 +-通过电路实现
只有把前面知识掌握清楚à质的飞跃;
最后方法:先模仿->再自己做; 理性思维比较强 逻辑比较强
编程要有:效率观念(如果在规定时间内我一个任务做2遍 做10遍 灵敏度高??)、内存观念(数据类型 五大区变量分配);
虚拟机安装、讲解相关linux命令、原码反码补码、讲解进制转换、数据类型基本数据类型、运算符优先级结合性、九条语句、数组、函数、期中考试;