前言
本章将对于C语言函数的定义和用法进行讲解,并且对比较难的递归部分进行详细画图解析,并对栈和栈溢出进行一个简单的叙述。同样,考虑到目前处于基础阶段,本章配备练习便于读者巩固。
一、函数
0x00 函数的定义
📚 数学中,f(x) = 2*x+1、f(x, y) = x + y 是函数...
在计算机中,函数是一个大型程序中的某部分代码,由一个或多个语句块组成;
它负责完成某项特定任务,并且相较于其他代码,具备相对的独立性;
📌注意事项:
1. 函数设计应追求“高内聚低耦合”;
(即:函数体内部实现修改了,尽量不要对外部产生影响,否则:代码不方便维护)
2. 设计函数时,尽量做到谁申请的资源就由谁来释放;
3. 关于return,一个函数只能返回一个结果;
4. 不同的函数术语不同的作用域,所以不同的函数中定义相同的名字并不会造成冲突;
5. 函数可以嵌套调用,但是不能嵌套定义,函数里不可以定义函数;
7. 函数的定义可以放在任意位置,但是函数的声明必须放在函数的使用之前;
📜 箴言:
1. 函数参数不宜过多,参数越少越好;
2. 少用全局变量,全局变量每个方法都可以访问,很难保证数据的正确性和安全性;
0x01 主函数
( 这里不予以赘述,详见第一章)
📌 注意事项
1. C语言规定,在一个源程序中,main函数的位置可任意;
2. 如果在主函数之前调用了那些函数,必须在main函数前对其所调用函数进行声明,或包含其被调用函数的头文件;
0x02 库函数
❓ 为什么会有库函数?
📚 “库函数虽然不是业务性的代码,但在开发过程中每个程序员都可能用得到,为了支持可移植性和提高程序的效率,所以C语言基础库中提供了库函数,方便程序员进行软件开发”
📌 注意事项:库函数的使用必须要包含对应的头文件;
📜 箴言:要培养一个查找学习的好习惯;
💡 学习库函数
1. MSDN;
2. c++:www.cplusplus.com;
3. 菜鸟教程:C 语言教程 | 菜鸟教程;
🔺 简单的总结:
IO函数、字符串操作函数、字符操作函数、内存操作函数、时间/日期函数、数学函数、其他库函数;
💬 参照文档,学习几个库函数:
“strcpy - 字符串拷贝”
#include <stdio.h> #include <string.h> // Required Header; int main() { char arr1[20] = {0}; // strDestination; char arr2[] = "hello world"; // strSource; strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
🚩 >>> hello world
💬 参照文档,试着学习几个库函数:
“memset - 内存设置”
#include <stdio.h> #include <string.h> // Requested Header int main() { char arr[] = "hello world"; // dest memset(arr, 'x', 5); // (dest, c, count) printf("%s\n", arr); return 0; }
🚩 >>> xxxxx world
0x03 自定义函数
❓ 何为自定义函数?
“顾名思义,全部由自己设计,赋予程序员很大的发挥空间”
📚 自定义函数和其他函数一样,有函数名、返回值类型和函数参数;
1. ret_type 为返回类型;
2. func_name 为函数名;
3. paral 为函数参数;
💬 自定义函数的演示
“需求:写一个函数来找出两个值的较大值”
int get_max(int x, int y) { // 我们需要它返回一个值,所以返回类型为int; int z = 0; if (x > y) z = x; else z = y; return z; // 返回z - 较大值; } int main() { int a = 10; int b = 20; // 函数的调用; int max = get_max(a, b); printf("max = %d\n", max); return 0; }
🚩 >>> max = 20
0x04 函数的参数
📚 实际参数(实参)
1. 真实传给函数的参数叫实参(实参可以是常量、变量、表达式、函数等);
2. 无论实参是何种类型的量,进行函数调用时,必须有确定的值,以便把这些值传送给形参;
📚 形式参数(形参)
1. 形参实例化后相当于实参的一份临时拷贝,修改形参不会改变实参;
2. 形式参数只有在函数被调用的过程中才实例化;
3. 形式参数在函数调用完后自动销毁,只在函数中有效;
📌 注意事项:
1. 形参和实参可以同名;
2. 函数的形参一般都是通过参数压栈的方式传递的;
3. “形参很懒”:形参在调用的时才实例化,才会开辟内存空间;
0x05 函数的调用
📚 传值调用
1. 传值调用时,形参是实参的一份临时拷贝;
2. 函数的形参和实参分别占用不同内存块,对形参的修改不会影响实参;
3. 形参和实参使用的不是同一个内存地址;
📚 传址调用
1. 传址调用时可通过形参操作实参;
2. 传址调用是把函数外部创建的变量的内存地址传递给函数参数的一种调用函数的方式;
3. 使函数内部可以直接操作函数外部的变量(让函数内外的变量建立起真正的联系);
💬 交换两个变量的内容
// void,表示这个函数不返回任何值,也不需要返回; void Swap(int x, int y) { int tmp = 0; tmp = x; x = y; y = tmp; } int main() { int a = 10; int b = 20; // 写一个函数 - 交换2个整形变量的值 printf("交换前:a=%d b=%d\n", a, b); Swap(a, b); printf("交换后:a=%d b=%d\n", a, b); return 0; }
🚩 >>> 交换前:a=10 b=20 交换后:a=10 b=20
❓ “为何没有交换效果?是哪里出问题了吗?”
🔑 解析:Swap在被调用时,实参传给形参,其实形参是实参的一份临时拷贝。因为改变型形参并不能改变实参,所以没有交换效果;
💡 解决方案:使用传址调用(运用指针)
// 因为传过去的是两个整型地址,所以要用int*接收; void Swap2(int* pa, int* pb) { // 传址调用; int tmp = *pa; // *将pa解引用; *pa = *pb; *pb = tmp; } int main() { int a = 10; int b = 20; printf("交换前:a=%d b=%d\n", a, b); Swap2(&a, &b); // 传入的是地址; printf("交换后:a=%d b=%d\n", a, b); return 0; }
0x06 函数的嵌套调用
📚 函数和函数之间可以有机合成的;
void new_line() { printf("hehe "); } void three_line() { int i = 0; for (i=0; i<3; i++) new_line(); // three_line又调用三次new_line; } int main() { three_line(); // 调用three_line; return 0; }
🚩 >>> hehe hehe hehe
0x07 函数的链式访问
📚 把一个函数的返回值作为另外一个函数的参数
int main() { /* strlen - 求字符串长度 */ int len = strlen("abc"); printf("%d\n", len); printf("%d\n", strlen("abc")); // 链式访问 /* strcpy - 字符串拷贝 */ char arr1[20] = {0}; char arr2[] = "bit"; strcpy(arr1, arr2); printf("%s\n", arr1); printf("%s\n", strcpy(arr1, arr2)); // 链式访问 return 0; }
💭 面试题
“结果是什么?”
int main() { printf("%d", printf("%d", printf("%d", 43))); return 0; }
🚩 >>> 4321
🔑 解析: printf函数的作用是打印,但是它也有返回值,printf的返回值是返回字符的长度;printf调用printf再调用printf("%d", 43),首先打印出43,返回字符长度2,打印出2,printf("%d", printf("%d", 43)) 又返回字符长度1,打印出1;所以为4321;
“我们可以试着再MSDN里查找printf函数的详细介绍”
0x08 函数的声明和定义
📚 函数的声明
1. 为了告诉编译器函数名、参数、返回类型是什么,但是具体是不是存在,无关紧要;
2. 函数必须保证“先声明后使用”,函数的声明点到为止即可;
3. 函数的声明一般要放在头文件中;
📚 函数的定义:是指函数的具体实现,交代函数的功能实现;
int main() { int a = 10; int b = 20; /* 函数的声明 */ int Add(int, int); int c = Add(a, b); printf("%d\n", c); return 0; } /* 函数的定义 */ int Add(int x, int y) { return x + y; }