20160129.CCPP体系详解(0008天)
2016-01-30 13:53
639 查看
程序片段(01):函数.c+call.c+测试.cpp
内容概要:函数
程序片段(02):C语言函数调用实例.c+函数.c+run.c
内容概要:函数的分割+函数的划分
程序片段(03):void.c+fun.c
内容概要:函数的使用和参数
程序片段(04):输入输出.c+return.c
内容概要:return与参数
内容概要(05):过程.c
内容概要:函数执行过程
程序片段(06):go.c
内容概要:函数参数的运算顺序
程序片段(07):main.c
内容概要:CodeBlocks测试
程序片段(08):可变参数.c
内容概要:可变参数
程序片段(09):C声明.c+函数声明.c+int.c+全局与局部冲突
内容概要:C语言函数声明+全局变量与局部变量
程序片段(10):test.cpp
内容概要:声明与定义差别
程序片段(11):baidu.c+stack.c
内容概要:函数调用流程简单递归
程序片段(12):线性递归.c+树状递归.c+汉诺塔.c
内容概要:递归
内容概要:函数
///函数.c #include <stdio.h> #include <stdlib.h> //01.函数: // 1.函数的作用:重用代码,重用功能 // 表象:代码的封装,代码的重用 // 实质:功能的封装,功能的重用 int main01(void) { system("tasklist"); system("pause"); } //02.函数的声明与定义浅析: // 1.函数的声明的定义的使用时机不同: // 函数的声明用于编译时期-->编译检查 // 函数的定义用于链接时期-->链接检查 // 函数的真正使用:声明和定义缺一不可 // 2.由于编译器的不同所导致的差异: // C语言编译器:宽泛(VS2015自动配置了编译器所需要的静态库Lib的目录) // 可以既没有声明也没有定义(只是没有显式的在代码中标注,但是 // 编译器能够自动识别到),编译器当中有两个配置选项(库目录+附加依赖项) // 但是,如果编译器当中没有配置这两项(库目录和附加依赖项)就会编译报错 // 注意:编译时期需要声明,链接时期需要实体 // C++语言编译器:严格 // 必须既有声明也有定义,必须显式的在代码中进行标注 // 编译时期需要声明,链接时期需要定义 // 3.在代码当中函数声明和定义出现的时机: // 标准做法:函数声明必须出现在函数调用之前 // 函数声明的位置既可以独立形式出现,也可以出现于函数体内部,但必须出现 // 在调用之前(CCPP同时支持的规则) // 4.关于形参是否存在形参名称的问题: // 函数声明的时候可以没有形参名称, // 但是,函数实现的时候必须有函数的形参名称 int getres(int a, int b, int c);//函数的声明 int main01(void) { //代码重用 int x = 11, y = 12, z = 13; x = x*x*x; y = y*y*y; z = z*z*z; int res = x + y + z; res = getres(x, y, z); printf("%d \n", res); int a = 10, b = 12, c = 13; a = a*a*a; b = b*b*b; c = c*c*c; int res1 = a + b + c; res1 = getres(a, b, c);//函数通过代码的重用实现了功能的重用 printf("res1 = %d \n", res1); system("pause"); } //03.为了让程序能够连接成功,在函数进行声明之后就必须进行函数的定义 // 函数的声明:只是表明函数存在,你可以使用这个函数的名称(表明有,可以形式用) // 函数的定义:确切表明函数存在,你可以使用这个函数的本身(确实有,可以实际用) int getres(int a, int b, int c) { return a*a*a + b*b*b + c*c*c; }
///call.c //01.编译器的不同特点测试: // 1.VS2015的编译器,默认进行了编译器所需的静态库(LIb)的配置: // 因此,虽然没有函数的具体声明,但是C语言程序却可以静态库(Lib) // 的配置选项进行函数的定位,最终找到函数实体本身 // 注:C语言由于默认对静态库(Lib)的配置,因此C语言的编译比较宽泛 // 有静态库(LIb)的路径,可以自动定义,不以来与函数的确切声明 // 2.C++的编译器:严格控制 // 要求必须有函数的声明和定义,才能够打包成为应用程序 // 编译的时候需要检测函数的声明是否存在? // 链接的时候需要检测函数的实现是否存在? int main02(void) { system("calc");//系统库函数,标准库函数 system("pause"); }
///测试.cpp #include <stdio.h> #include <stdlib.h> int add(int a, int b);//函数声明 //01.自定义函数的声明和定义特点: // 1.刚刚那个是针对于系统函数的声明和定义的区别 // 自定义函数相对于系统函数而言,就是没有标准库(Lib)而已 // 2.由于编译器所导致的不同: // C语言编译器特点: // 没有函数声明,但是有静态库(Lib)配置-->编译通过 // 没有函数声明,有定义,且实体在任意位置-->可能编译通过,可能编译不通过 // 放在调用之前的定义,编译通过 // 放在调用之后的定义,可能通过,可能不通过 // 由于C语言编译器的特点,所以可能检测的出来,也可能检测不出来 // 函数的声明和定义都可以缺掉,只要静态库(Lib)中包含,模糊匹配 // C++语言编译器: // 没有函数声明,但是又静态库(Lib)配置-->编译不通过 // 没有函数声明,有定义,且实体在调用之前可以调用 // 要求:函数的声明和定义缺一不可,准确匹配 int main03(void) { add(1, 2); printf("%d \n", add(10, 20)); system("pause"); } int add(int a, int b) { return a + b; }
程序片段(02):C语言函数调用实例.c+函数.c+run.c
内容概要:函数的分割+函数的划分
///C语言函数调用实例.c #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> //01.函数的使用特点: // 1.C语言当中的异常处理函数abort(); // (1).用于表明某个位置出现了错误 // (提示方式:以windows弹窗作为异常提醒) // (2).函数特点: // 只是一个提示,点击弹窗之后,程序依然可以继续执行 // 不会直接中断整个应用程序(直接表明异常函数abort()的调用) // 2.C语言当中的函数特点: // 不可以进行函数的嵌套定义! // C++语言当中不允许函数的直接嵌套,但是允许间接的通过 // Lambda表达式实现函数的嵌套形式 int main01(void) { int a; int b; scanf("%d,%d", &a, &b); if (b == 0) { abort();//处理程序的异常 } printf("%d \n", a / b); //void go() //{ //} system("pause"); }
///函数.c #include <stdio.h> #include <stdlib.h>//std:表明标准静态库-->跨平台静态库-->C语言标准跨平台静态函数库(Lib) #include <Windows.h>//第三方静态库:仅仅适用于Windows的静态函数库 void run(char *path)//外部函数,C语言当中的代码重用(功能重用),主要依赖于函数的使用特点 {//被调函数 ShellExecuteA(0, "open", path, 0, 0, 1);//默认窗口打开方式 } //01.区分主调函数和被掉函数的概念: // 在那个函数代码块儿中写其他函数的调用语句,那么: // 那个函数就是所谓的主调函数 // 其他函数就是所谓的被调函数 int main02(void) {//主调函数 // run("\"C:\\Program Files\\Tencent\\QQ\\QQProtect\\Bin\\QQProtect.exe\""); // run("C:\\Users\\yincheng01\\AppData\\Roaming\\baidu\\BaiduYun\\baiduyun.exe"); system("pause");//库函数,不加头文件,C语言可以,但是为了代码规范,还是要添加上头文件的 }
///run.c #include <stdio.h> #include <stdlib.h> //01.函数的组成元素分析: // 函数的声明:int getmax(int a, int b);-->末尾的函数声明结束符(";")分号不允许省略 // 函数的实现:int getmax(int a, int b){return a > b ? a : b;}-->代码块儿("{}")当中的语句就是函数实现语句 // 返回值类型:int-->限制函数的返回值最终类型 // 函数名称:getmax-->实质即使函数指针-->函数存放函数声明的地址-->另外还有函数定义的地址(两个地址不用) // C语言当中goto语句的实现原理就如同汇编语言当中jump原理-->通过反汇编可以区分(函数声明地址和函数实现地址的不同) // C语言当中的应用程序在应用程序被加载进内存之后,就会新建一张函数表(类似于变量表)-->里面记录了函数定义的地址 // 于是我们就可以通过函数声明的地址找到具体函数定义的地址(这是实现劫持的原理:函数指针) // 改变函数指针的指向,可以让其具备不同的行为,以至于没有行为也是通过这个进行控制的 // 函数声明变量-->存储函数定义(实体变量<==>普通变量)的地址-->所以函数声明变量叫做函数指针(存放地址的变量叫做指针变量) // 所以:函数声明变量叫做函数指针 // 形式参数表:(int a, int b)-->int a,int b代表的就是实际的参数本身 // 函数执行体:{return a > b ? a : b;}-->函数实体的代码块儿内容 // 函数返回值:return a > b ? a : b;-->return语句表明函数的具体反回值 int getmax(int a, int b);//函数的语句块儿不允许声明,所以通过空语句分号(";")进行表示 int getmax(int a, int b) { //int a;//函数体内部定义变量不可以和形式参数名称重名 return a > b ? a : b; } int main03(void) { printf("%p \n", getmax); system("pause"); }
程序片段(03):void.c+fun.c
内容概要:函数的使用和参数
///void.c #include <stdio.h> #include <stdlib.h> int add(int a, int b);//遵循软件工程规范,在函数调用之前必须明确函数的声明 //01.void类型的使用特点: // 1.出现位置的不同,意义不同 // 返回值类型位置: // 表明函数不需要返回值,不用通过return关键字显式的将返回值带出 // 函数形式参数位: // 表明该函数无需参数值,明确函数不需要传入实际参数 // 2.void的使用注意事项: // 可以用来定义指针类型-->void *-->俗称干地址-->没有明确解析方式的地址 // -->但是由于地址的大小已经确定(要么4字节|要么8字节)-->编译器决定 // 所以知晓存储一个地址需要开辟多少个字节 // -->因此,指针变量的内存地址开辟成功 // 不能用来定义变量类型-->void---->因为没有明确类型,没有明确的解析方式 // -->不能描述变量所需要开辟的存储空间究竟需要多大? // -->导致开辟普通变量的内存空间失败 //02.返回值和返回值类型的使用注意事项: // 1.返回值的类型要求与返回值的类型保持一致! // 如果不一致将会发生数据类型的转换(自动类型转换+自动类型转换) // 2.如果返回值的类型采用void描述: // C语言采用其他类型的返回值进行返回,那么编译器不会报错,但是返回的值却可能是不正确的 // C++语言采用其他类型的返回值进行返回,那么编译器直接进行报错,说类型的不匹配 //03.return关键字的作用: // 1.返回值:将值从被调函数当中带出 // 2.中断多层嵌套循环的执行(区分于goto语句的实现特点) // 3.中断函数的执行 //04.所有的函数,默认的返回值类型都是int类型 // 包括特殊的main函数的默认返回值类型也是int类型 // 只是函数若是没有明确的声明返回值类型,而进行返回异常的整数 int main01(void) { printf("%d \n", add(10, 20)); //void a;//"a":非法使用"void"类型,代表任何类型 return 1;//返回值应该与返回值类型一致 //如果函数申明为void,却用return返回一个其他类型的值,那么C++编译器报错,由于类型不匹配 //但是C语言的编译器不会进行报错 system("pause"); } add(int a, int b) { return a + b; } int main02(void) { getchar();//根据函数调用找到找到函数实体-->函数声明-->函数实体 getchar();//参数即使为空,函数的调用依然需要添加上小括号("") system("pause"); }
///fun.c #include <stdio.h> #include <stdlib.h> void change(int a)//函数的副本机制:int a-->形式参数的声明 {//读取寄存器当中的整型值,构建当前函数所需使用的内存变量值 a = 3; printf("&change = %p, change = %d \n", &a, a); } int main03(void) { //主调函数当中传递给被调函数的参数叫做实际参数,简称实参 change(10);//副本,开辟内存容纳寄存器的的值-->寄存器当中的值可以直接进行读取使用 system("pause"); } //01.函数参数的特点: // 1.主调函数和被调函数当中的参数是不同的概念: // (1).所处的位置不同: // 栈内存不同,不同的函数处于不同的运行时堆栈 // 所以即使名称相同,也是不同的变量 // (2).不可以跨函数访问局部变量 // 运行时堆栈的不可见特点 // 上下层(运行时堆栈)当中的局部变量不可以夸堆栈访问 // 2.主调函数传递给被调函数的实际参数的副本可能的存储位置: // 未接收-->寄存器-->缓存器-->未经使用的常量数据 // 接收了-->栈内存-->存储普通的副本数据,栈内存容得下 // 接收了-->堆内存-->如果副本数据很大,就必须采用堆内存空间进行存储 int main04(void) { int a = 10; printf("&main = %p, main = %d \n", &a ,a); change(a); printf("%d \n", a); system("pause"); }
程序片段(04):输入输出.c+return.c
内容概要:return与参数
///输入输出.c #include <stdio.h> #include <stdlib.h> int add(int a, int b)//int a, int b这两个形式参数,只有在被调用的时候,才会涉及到自动分配和自动释放 { printf("add1 a = %d, b = %d \n", a, b); a = 19; b = 29;//修改的是当前被调函数当中的局部变量,也就是主调函数传递进来的实际参数的副本数据 printf("add2 a = %d, b = %d \n", a, b); return a + b; } //01.在我看来,传值和传址都是一样的: // 只不过一个被赋予了普通变量的解析特点->其他 // 一个呗赋予了指针变量的解析特点而已-->数组 int main01(void) { int a = 10; int b = 20; printf("%d \n", add(a, b)); printf("%d \n", add(11, 12));//函数的参数除了数组以外,都是副本(区别于指针变量接收,还是普通变量接收) printf("main a = %d, b = %d \n", a, b); system("pause"); } //02.参数传递特点: // add_debug(1, 2, 3);-->实参太多 // C语言参数过多只会发出警告,结果不保证绝对正确,参数刚好合适,能够保证结果正确 // add_debug(1);------->实参太少 // 直接发生变异报错 // 注:函数参数进栈的顺序是从右往左,提取函数参数的数据是从上往下进行提取的 // 例如:(int a, int b); // 进栈顺序:b--->a // 区分:函数形式参数的进栈顺序和函数局部变量的进栈顺序 //03.函数参数进栈的顺序严格区分:Release环境下进行的测试 // 函数形式参数的进栈顺序: // 数据进栈:从右往左,依次进栈, // 数据映射:从左往右 // 举例:传递数据1, 2, 3 // (int a, int b) // 数据进栈: 数据映射: // 栈底: 3 -> 丢掉|编译器预置数据 // 2 -> b // 栈顶:1 -> a // 函数局部变量的进栈顺序: // 从下往上-->代码进栈-->扫描局部变量的时候,变量由下往上进行声明的 int add_debug(int a, int b) { printf("a = %p, b = %p \n", &a, &b); printf("a = %d, b = %d \n", a, b); a = 1; b = 2; int x = 3; int y = 4; printf("x = %p, y = %p \n", &x, &y); printf("x = %d, y = %d \n", x, y); printf("\n"); } int main02(void) { //printf("%d \n", add_debug(1)); //printf("%d \n", add_debug(1, 2)); //printf("%d \n", add_debug(3, 12)); printf("error = %d \n",add_debug(1, 2, 3, 4, 5) ); system("pause"); } //04.参数传递的注意事项: // C语言编译器中,主调函数传递给被调函数的实际参数如果过多: // 多得数据会被忽略掉, // 参数个数如果一致,类型一致,书序一致能够保证结果正确 // C语言编译器中,实参和形参的类型要尽量一致,个数也要一致 // 由于C语言编译器过于宽泛,所以不怎么严格 int add_test(int a, int b)//int a = 11.0赋值的操作,赋值回自动完成类型转换 { return a + b; } int main03(void) { printf("%d", add_test(11.9, 2, 3, 5, 10, 12)); system("pause"); } //05.小数类型在进行整数的过程当中: // 只会进行取整运算,不涉及到四舍五入的情景 int add_test1(int a, int b)//return也会完成数据类型的转换 { return 13.9; } int main04(void) { printf("%d \n", add_test1(1, 2)); //int a = 10; //a + 1 = 9; system("pause"); }
///return.c #include <stdio.h> #include <stdlib.h> //01.C语言编译器当中的函数特点: // 如果函数表明了需要返回值类型,需要返回值 // 你如果不通过return关键字正确的返回值,那么编译器不会进行报错 // 但是程序最终的结果不正确结果自负 int addx() { return 1; } int main05(void) { printf("%d \n", addx()); system("pause"); } //02.函数形式参数和返回值详解: // (1).都存在有副本机制: // 副本数据可能的存储位置(寄存器-->缓存器-->栈内存-->堆内存) // (2).都存在数据类型转换: // 自动类型转换(小-->大)+强制类型转换(大-->小) int getnum() { int num; printf("%p \n", &num); num = 10; return 10.9;//return有副本机制,在寄存器,缓存,内存,堆内存(编译器根据数据特点决定) system("notepad");//当前的函数块儿语句,由于处于return关键字之后,所以不会有被执行到的机会 //int data = num;//副本机制模拟 //范式副本机制,都会通过赋值,赋值就会发生自动类型转换|强制类型转换特点 } void show() { system("notepad"); return; } //03.结束多层循环的方式特点: // goto:结束多层循环,但是函数并未弹栈(还未出栈) // return:结束多层循环,函数发生弹栈(直接出栈) void showx() { for (int i = 1; i < 100; i++) { if (i % 13 == 0) { printf("%d ", i); return;//循环内部,结束循环-->区别于goto循环的跳转特点 } } } int main06(void) { //return;//main函数意味着退出 printf("%d \n", getnum()); showx(); system("pause"); } //04.&getnum();所涉及到的问题分析: // 1.函数返回的副本数据的原本可能存在于: // 寄存器-->缓存器-->栈内存-->堆内存 // 1.getnum();这个函数返回的是一个数据 // 具体的一个数据,不涉及到变量概念,不涉及到内存概念 // 所以不能通过取地址符进行操作(数据:是右值,不是左值) int main07(void) { //printf("%d \n", &getnum());//不是左值,右值,右值存在于寄存器内部 system("pause"); } int add(int a, int b)//add(int a, int b)-->int { return a + b; } int main08(void) { printf("%d \n", add(add(add(1, 2), 3), 4)); system("pause"); }
内容概要(05):过程.c
内容概要:函数执行过程
#include <stdio.h> #include <stdlib.h> int main01(void) { printf("main 上 \n"); void print1();//C语言建议添加声明,添加了声明只有一定不会出错,没有声明可能会出错 print1(); printf("%d \n", add(-1, 0));//参数多了可以编译,但是不能保证结果正确,参数少了不可以编译正确 printf("main 下 \n"); system("pause"); } int add(int a, int b) { return a + b; } void print1() { printf("print1 上 \n"); printf("print1 下 \n"); }
程序片段(06):go.c
内容概要:函数参数的运算顺序
#include <stdio.h> #include <stdlib.h> void show(int a, int b) { printf("a = %d, b = %d \n", a, b); } int main01(void) { int a = 5; show(a, a++);//6,5 system("pause"); } //01.函数形式参数和局部变量特点详解: // 1.所以测试环境均为Release环境(标准) // 2.分特点详解: // 形式参数: // 数据进栈顺序:决定实参执行顺序 // 从右往左 // 数据映射顺序: // 从左往右 // 数据进栈: 数据映射: // 3 丢失(多余)|垃圾(少了) // 2 -> b // 1 -> a // 数据地址顺序: // 先进栈的形式参数位于高地址 // 后进栈的形式参数位于地地址 // 局部变量: // 由于代码是从下往上进行进栈的 // 所以变量也是从下往上进行压栈的 // 先压栈的变量位于高地址 // 后压栈的变量位于地地址 // 压栈过程当中只是第一次初始化数据 // 高地址到低地址的压栈过程 // 压栈之后的执行过程决定最终的数据特点 // 低地址往高地址的执行特点(正好适应程序的从上往下执行特点) int add(int a, int b) { printf("&a = %d, &b = %d \n", &a, &b); printf("a = %d, b = %d \n", a, b); int x = 1; int y = 2; printf("&x = %d, &y =%d \n", &x, &y); printf("x = %d, b = %d \n", x, y); return a + b; } int main02(void) { //printf("%d \n", add(1, 2)); printf("%d \n", add(1, 2, 3)); system("pause"); }
程序片段(07):main.c
内容概要:CodeBlocks测试
#include <stdio.h> #include <stdlib.h> void show(int a, int b) { printf("a=%d,b=%d", a, b); } int main() {//效果一致 int a = 5; show(a, a++); getchar(); }
程序片段(08):可变参数.c
内容概要:可变参数
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stdarg.h>//标准参数:模拟可变参数必需的头文件 int add(int num, ...)//...代表可变参数 { int res = 0;//结果 va_list argp;//存储参数开始的地址 va_start(argp, num);//从首地址开始,读取num后面的数据 for (int i = 0; i < num; i++) { res += va_arg(argp, int);//读取一个数据并且按照int类型进行二进制数据的解析 } va_end(argp);//结束读取 return res; } int main01(void) { printf("%d \n", add(3, 1, 2, 3)); printf("%d \n", add(4, 1, 2, 3, 4)); printf("%d \n", add(5, 1, 2, 3, 4, 5)); system("pause"); } int main02(void) { printf("%d, %d, %d \n", 1, 2, 3); printf("%d, %s, %c, %d \n", 1, "123", 'A', 4); system("pause"); } //01.可变参数使用方式一: // 1.将第一个参数作为确定可变参数列表当中所存储的参数总个数 // 2.可变参数使用流程总结: // (1).包含头文件: // #include <stdarg.h> // (2).确定函数声明: // void vatest(int count, ...); // (3).进行参数使用: // va_list argp;//存储可变参数列表的首地址(类似于数组的特点) // va_start(argp, count);//从首地址开始,读取count个参数 // va_arg(argp, type);//按照type类型读取当前可变参数列表当中读取到的位置所在的数据 // va_end(argp);//结束可变参数列表的读取状态 void go(int num, ...) { va_list argp;//存储可变参数列表开始的首地址 va_start(argp, num);//从首地址开始,读取num个的数据 for (int i = 0; i < num; i++) { char str[50]; //sprintf(str, "%s", va_arg(argp, char *)); //system(str);//只要调用一次va_arg就从可变参数列表当中读取一个参数 system(va_arg(argp,char *)); //读取一个二进制数据并且按照char *类型解析 } va_end(argp);//结束读取 } int main03(void) { go(3, "notepad", "calc", "tasklist & pause"); system("pause"); } //02.可变参数的使用方式二: // 1.不采用可变参数前置参数作为读取结束条件,而是按照可变参数列表的结束特点进行读取 // 2.不定长可变参数列表的使用特点: // (1).引入头文件: // #include <stdarg.h>//支持可变参数列表的使用 // (2).函数声明特点: // void vatest(int start, ...);//可变参数列表的函数声明 // va_list argp;//存储可变参数列表的首地址(类似于数组原理) // vastart(argp, start);//暂定可变参数列表当中参数的读取个数 // int argvalue = start;//确定可变参数列表的首个参数 // do // { // int value=argvalue;//使用可变参数列表当中的数据 // argvalue = va_arg(argp, int);//不断的按照指定类型进行读取 // }while(argvaue != -1); // va_end(argp);结束读取 void showint(int start, ...) { va_list argp;//存储参数开始的地址 va_start(argp, start);//从首地址开始读取数据,暂定为读取start个数据 int argvalue = start;//第一步初始化 do { printf("\n %d", argvalue); argvalue = va_arg(argp, int);//不断读取 } while (argvalue != -1); va_end(argp);//结束读取 } int main04(void) { //showint(1, 2, 3, 4, -1); showint(1, 2, 3, 4, 5, -1); system("pause"); }
程序片段(09):C声明.c+函数声明.c+int.c+全局与局部冲突
内容概要:C语言函数声明+全局变量与局部变量
///C声明.c #include <stdio.h> #include <stdlib.h> //01.使用函数的特点: // 1.()用于对函数进行标识 // 2.进行函数调用必须明确调用的类型: // 区分变量访问和函数调用("()") //02.C语言的编译器特点: // 1.由于VC2015这个编译器当中自动包含了库目录和附加依赖项 // 所以使用C语言函数的时候,可以没有声明语句,因为C语言编译器 // VC2015会自动到静态库目录和附加依赖项当中去进行查找, // 自动查找所需调用的函数,参数多了,少了都可以进行调用(前期,可以;后期,不可以) // 2.函数调用触发了C语言编译器VC2015的自动定位功能 //03.库函数的查找: // 分为系统库函数和定义库函数的查找 // C语言编译器VC2015支持自动查找 // C++编译器不支持自动查找 int main01(void) { printf("Hello China! \n");//()是个函数 printf;//引用函数必须要进行声明 system("pause"); } int main02(void) { add(2, 3); print(); //add; system("pause"); } int print(){} int add(int a, int b) { return a + b; }
///函数声明.c #include <stdio.h> #include <stdlib.h> //01.C语言当中的声明和定义特点: // 1.声明可以有多个,但是定义只能有一个 // 2.函数声明的时候可以不用指明形参的名称 // 但是定义的时候必须指定形参的名称 // 并且函数声明的形参名称和函数实现的形参名称可以不同 // 3.函数的声明和定义与变量的声明和定义类似 int add(int a, int b);//声明 int add(int x, int y);//声明 int add(int h, int j);//声明 int add(int k, int l);//声明 int main03(void) { printf("%d", add(1, 2)); system("pause"); } int add(int a, int b) { return a + b;//函数的定义 } //int add(int a, int b) //{//声明可以有多个,但是定义只能有一个 // return a + b;//函数的定义 //}
///int.c #include <stdio.h> #include <stdlib.h> //全局变量:int a = 10; //int a;//全局变量当做声明看待,如果没有初始化,将会被编译器默认赋予0 int a; //int a = 9;//int a;//全局变量声明,int a = 10;//全局变量定义,声明可以有多个,定义只能有一个 int main04(void) { printf("%d \n", a); system("pause"); } //01.局部变量和全局变量的使用总结: // 1.是否具备声明和定义之间的区别: // 函数和全局变量都有区别 // 局部变量没有区别(都当做定义来对待) // 2.全局变量的生命周期: // 程序代码一旦加载进代码区就已经存在了 // 全局变量优先于main函数的存在 // 3.全局变量的作用域: // 从当前文件的定义位置开始,到跨文件的范围 // 内都可以进行访问的到 int main05(void) { //局部变量没有声明和定义的区别 //int a = 10;//变量重名,局部变量 //int a; ////int a = 10;//局部变量 //int a; //int a; //int a; a = 9; system("pause"); } void go() { a = 11; }
///全局与局部冲突.c #include <stdio.h> #include <stdlib.h> //01.全局变量和局部变量内容总结: // 1.全局变量很容易被局部变量覆盖 // 2.全局变量可以被多个函数所共享,方便于读写操作 // 3.全局变量在如果只是进行声明了,但是没有被定义 // 那么系统会为其定制一个默认的初始化值0 // 4.全局变量可以在跨文件的情况下进行调用: // 容易出现全局变量重合(类型相同,名称相同) // 5.局部变量和全局变量重名的情况之下,会覆盖掉 // 全局变量 // 6.当局部代码块儿当中存在和全局变量相同的变量 // 那么局部代码块儿的操作将屏蔽对全局变量的操作 // 相当于对全局变量的操作无效 int a; int a; int a = 3; int main06(void) { printf("%d \n", a); system("pause"); } int main07(void) { int a = 10; printf("%d \n", a);//局部变量覆盖全局变量,重名 { printf("%d \n", a); int a = 13; printf("%d \n", a);//内部块儿语句会屏蔽外部变量 } printf("%d \n", a);//局部变量覆盖全局变量,重名 system("pause"); }
程序片段(10):test.cpp
内容概要:声明与定义差别
#include <stdio.h> #include <stdlib.h> //01.函数的声明和定义详解: // 1.函数的声明可以有多个,定义只能有一个 // 2.函数的声明可以没有参数名称,但是必须有参数类型 // 3.函数声明的参数名称可以和函数的定义的参数名称不一致 // 但是要求类型必须一一对应 int add(int a, int b);//声明要与定义相匹配 int add(int x, int y);//声明的变量名可以省略,可以和定义的变量名不一致,但是要求类型必须一致 int add(int a, int b) { return a + b; } int main01(void) { add(1, 2); system("pause"); }
程序片段(11):baidu.c+stack.c
内容概要:函数调用流程简单递归
///baidu.c #include <Windows.h> //01.动态库(Dll)知识+递归调用知识: // (1).Dll注入技术可以让任何程序挂掉 // (2).针对于像360这样的安全软件 // 需要采用sys层面的技术进行破坏 // 因为360安全卫士是基于驱动层面开发 // (3).如何导出动态库(Dll)? // 1).在原始函数声明之前添加 // _declspec(dllexport) // 2).配置项目属性(配置类型) // 动态库(.dll) _declspec(dllexport) void go() { Sleep(1); go(); }
///stack.c #include <stdio.h> #include <stdlib.h> //线性递归001:将一个整数进行逆序输出 // 递归函数的规律总结: // 是否需要返回值? // 如果有累变(加,减,乘,除)效果,就需要返回值类型,否则一般情况之下是不需要返回值类型的 // 是否需要形式参数? // 如果涉及到递归函数当中每层递归函数调用的数据使用,只是数值意义上的使用,就需形式参数 // 数据使用等同于数据关联,等同于递归函数调用层当中的数据传递,形参变量数据传递 // 是否需要类似于for循环结构的循环初始化条件?如果有,就需要形式参数,如果没有,则无需 // 是否逐渐逼近类似于for循环的循环终止条件? // 递归入口+递归出口 // 是否涉及到数据的打印显示顺序? // 打印语句如果需要顺序,就写于递归调用之前; // 打印语句如果需要逆序,就谢宇递归调用之后. void revInt(unsigned int value)//类似于for循环的循环初始化条件 { unsigned int quotient = value / 10;//空间复杂度1+时间复杂度1 if (quotient != 0)//类似于for循环的循环判断条件 revInt(quotient);//类似于重复一次for循环的循环执行体 putchar(value % 10 + '0');//打印顺序为逆序(由于运行时堆栈的即时打印特点决定)-->这样打印的原因是因为跨平台性可移植性比较好! } //线性递归002:输入9,就顺序|逆序打印从1~9之间的整数 void printInt1(int value) { //putchar(value + '0');//逆序打印 if (value - 1 > 0)//时间复杂度2 printInt1(value - 1); putchar(value + '0');//顺序打印 } //线性递归003:打印任意一个区间[value1,value2]之间的整数 // 要求一:(顺序|逆序) // 要求二:从value1-->value2|value2-->value1 void printInt2(int value1, int value2) { printf("%d \n", value1); if ((value1 + 1) <= value2) printInt2(value1 + 1, value2); } //线性递归004:打印字符串当中的每一个字符(反转效果) // 1.严格区分字符数组和字符指针数组之间的区别 // 2.putchar();和printf();函数之间的区别 // putchar();不具备处理字符指针所指向的实体的作用 // printf();具备处理字符串指针所指向的实体的作用 // 3.putchar();每次只会打印单个字符,所有的字符拼装在 // 一起之后,就是一个字符串 // 4.putchar();遇到字符就直接打印字符本身,不会出现变故 // 放在括号内与括号外是有区别的(是否具备判断效果) // 决定最后一次打印的特点 void printStr(char *str)//类似于for循环的循环初始化条件 { if (*str)//类似于for循环的循环判断条件 { //printStr(str + 1);//类似于for循环的循环趋于结束的条件 printStr(++str);//简化形式 putchar(*str);//类似于for循环的循环执行体-->putchar();不具备处理字符指针所指向的实体的作用 }//如果不将putchar(*str);放在括号的内部,那么最后一层递归函数在进行打印的时候会将NUT|('\0')|0给打印出来 //也就是最终多打印了一个不可见字符 } //线性递归005:打印任意一个整数的阶乘结果 unsigned int calFact1(unsigned int value)//int表明递归函数的一层函数调用就能返回该阶乘结果,unsigned int num表明类似于for循环的循环初始化条件,或者说要做一件事情,直接所需的参数 {//如同:我要求取某个数的阶乘,你就得给我这个数据,我就根据这个数据算出一个阶乘结果反馈给你 if (0 == value || 1 == value)//类似于无限循环的结束条件,也就是递归函数的出口,结束最后一层递归函数的调用,不用再进行递归调用,而且不用再执行最后一层递归函数剩余的语句(直接出结果) return 1;//由于return关键字的特殊性,所以最后一层递归函数的执行依赖于它-->return直接终止函数,所以不会在执行一层递归调用以及一层递归调用之后的语句 calFact1(value - 1);//让无限循环不断的执行下去,至于循环的终止条件我们无需关注,因为上面一段儿已经决定了 return value * calFact1(value - 1);//对于该行语句,不用关注其执行流程,只需关注,value=value*value!,只是用于一次递归函数的调用就能完成意向功能,剩余递推关系让计算机去做,我们不关注 } unsigned int calFact2(unsigned int value) { if (0 == value || 1 == value) return 1; else//原理:一次求解,绝对有结果,至于结果的递推关系我们无需去关注,只需要关注的是一次递归函数的调用到底能够完成什么样儿的功能,至于如何递推,如果计算,那都是计算机的事情 return value * calFact2(value - 1); } //线性递归006:将一个正整数转化为其的二进制表现形式打印出来 void printIntToBin1(unsigned long value)//使用long类型意味着更好的程序跨平台性(可移植性)-->不像int类型(16位占用2个字节(short),32位以上占用4个字节(long))-->long始终占用4个字节 { unsigned long quotient = value / 2;//空间复杂度1+时间复杂度1 if (0 != quotient) printIntToBin1(quotient);//不断的执行打印除以2之后的余数(二进制位) putchar((value % 2) + '0');//余数逆置,一次递归调用意味着逆序打印一个二进制位,即使商为0,也需要打印出这个商为0情况之下的余数0 } void printIntToBin2(unsigned long value) { unsigned long remainder = value % 2; if (value / 2 > 0)//时间复杂度2 printIntToBin2(value / 2); //putchar(0 + i); putchar(remainder ? '1' : '0'); } //线性递归007:循环转递归剖析 // 1.任何一个循环都可以转换为递归 // 2.任何一个递归都可以转化为循环+栈 void loopToRecursion(long value) { printf("%d, %p \n", value, &value); if (value < 9) loopToRecursion(value + 1); } //01.递归的分类: // 1.函数调用方式: // 直接调用自己-->直接递归-->简单递归 // 间接调用自己-->间接递归-->复杂递归 // 2数据结构模型: // 线性递归:f(n)=f(n-1)+n; // 树状递归:f(n)=f(n-1)+f(n-2); // 图状递归: //02.递归的要点: // 1.递归的满足要点: // 递归入口+递归出口 // 2.递归的函数要点: // 运行时堆栈 //03.所涉及到的知识点: // 任何一个(0~1)之间的整数加上一个('0'); // 那么该表达式所获得的最终结果就是该 // 整数所对应的ASCII码值 int main01(void) { //revInt(1234); //printInt1(9); //printInt2(-55, 55); //printStr("123456789"); //char str[10] = "123456789";//区分字符数组和字符指针数组 //printStr(str); //printf("%d \n", calFact1(10)); //printf("%d \n", calFact2(10)); //printIntToBin1(106); //printIntToBin2(106); //loopToRecursion(0); system("pause"); } //04.采用无线循环打印一段儿字符串 int main02(void) { //system("notepad");//同步函数,一次只打开一个记事本,需要等待用户结束这个记事本在往下执行 printf("12345"); main02(); } //05.输入一个整数,就打印整数个字符串 void intPrintStr(char *str, unsigned long value) { if (0 == value) return; if (value - 1 > 0) intPrintStr(str, --value); printf("%s \n", str); } //06.控制Notepad的执行次数 void printNontepad(unsigned long value)//void:只打印数据,不需要返回 value:for循环的初始化条件 { if (0 == value)//value:判断for循环是否启用循环执行体 { return; } else {//5->4->3->2->1:五个映射对-->执行五次 system("notepad");//这句话放在前面还是后面都是一样的 printNontepad(value - 1);//重复执行一次回退的递归循环层 } } //07.输入任意一个正整数N,用递归实现从1~N之间的累加 unsigned long addNum(unsigned long value) { if (1 == value) return 1; return addNum(value - 1) + value; } int main03(void) { //intPrintStr("notepad", 5); //printNontepad(5); //printf("%d \n", addNum(100)); system("pause"); }
程序片段(12):线性递归.c+树状递归.c+汉诺塔.c
内容概要:递归
///线性递归.c #include <stdio.h> #include <stdlib.h> //01.递归运算常用解析思想: // 1.数学归纳法 // 2.例如:对等差数列的描述 // f(0)=0; // f(n)=f(n-1)+x; // 注:关于关系式的递归推导我们不用关心,因为计算机内部自己会去进行推导 // 记住!计算机最大的用处不是思考,而是计算 //02.main函数的特点: // 如果为main函数定义一个int类型的变量, // 那么编译器会自动为该int类型的变量默认初始化一个1(默认初始化) // 至于手动初始化需要通过命令行对程序进行启动 void main01(void) { main01();//通过递归实现的死循环 } //03.通过递归模拟循环实现循环次数的控制: void loopToRecursion(unsigned int value) { if (value >= 5) return; else loopToRecursion(value + 1); system("notepad"); } //04.求取0到任何一个正整数的之间的所有正整数的和? // f(100)=100+f(99); // f(100)=100+99+f(98); // f(100)=100+99+98+f(97); // f(n)=n+f(n-1); unsigned long countInt(unsigned long value) { if (value == 1) return 1; return value + countInt(value - 1); } void uLongToBin(unsigned long value) { unsigned long quotient = value / 2; if (quotient != 0) uLongToBin(quotient); putchar(value % 2 ? '1' : '0'); } int main02(void) { //loopToRecursion(0); //printf("%lu \n", countInt(100)); uLongToBin(100); system("pause"); }
///树状递归.c #include <stdio.h> #include <stdlib.h> //斐波那契数列: // 实际问题: // 1对兔子,2个月以后可以生育,从可以开始生育之后,每个月都能生育一对兔子 // 数学描述: // 1------->1 // 2------->1 // 3------->2 // 4------->3 // 5------->5 // 6------->8 // f(n)=f(n-2)+f(n-1); // 数学描述的特点: // f(n-2): // 描述的是这个月较上个月能够多增加的兔子数目 // 相差2的原因是因为一对兔子只有相隔两个月才具备生育一对兔子的能力; // 也就才会生育一对兔子 // f(n-1): // 描述的是上个月所有的兔子总计数目 // f(n): // 本月总共的兔子数目 unsigned long countRabbit1(unsigned long month) { if (1 == month || 2 == month) return 1; return countRabbit1(month - 2) + countRabbit1(month - 1); } void countRabbit2(unsigned long month) { if (1 == month || 2 == month) printf("1 \n"); else { int f1 = 1; int f2 = 2; int f3 = f1 + f2; for (int i = 3; i < month; i++) {//通过循环轮替的方式向前进行推进计算(计算机处理计算问题) f3 = f1 + f2; f1 = f2; f2 = f3; } printf("f3= %lu \n", f3); } } //02.树状递归内容总结: // 1.树状递归速度很慢,递归很慢,函数的调用和返回都需要时间 // 2.任何递归都可以转换为循环加栈 // 递归=循环+栈 int main01(void) { printf("%lu \n", countRabbit1(40)); countRabbit2(40); system("pause"); }
///汉诺塔.c #include <stdio.h> #include <stdlib.h> //01.递归解决问题的思想: // 1.明确需要解决的问题是什么? // 确定递归函数的声明 // 2.明确解决问题的重复步骤是什么? // 确定递归函数的实现 // 3.明确递归的入口和出口条件? // 什么时候开始递归; // 什么时候结束递归 void hanoiTower(unsigned long num, char x, char y, char z)//类似于for循环的循环初始化条件 {//只需要打印结果,不需要累变特点-->void;函数的实际意义-->hannoTower(unsigned long num, char x, char y, char z); if (1 == num) {//类似于for循环的循环判断条件-->递归终止继续执行的条件 //printf("%c-->%c \n", 'A', 'C');//直接搬动 printf("%c-->%c \n", x, z); return; }//递归状态时刻被保留与堆栈当中-->当前函数在执行时所能访问的内容只有运行时堆栈当中的内容 //类似于for循环的循环执行体内容-->通用问题化解方式 hanoiTower(num - 1, x, z, y);//A-->B printf("%c-->%c \n", x, z);//A-->C hanoiTower(num - 1, y, x, z);//B-->C } int main04(void) { hanoiTower(4, 'A', 'B', 'C'); system("pause"); }
相关文章推荐
- 20160301.CCPP体系详解(0040天)
- 20160229.CCPP体系详解(0039天)
- 20160228.CCPP体系详解(0038天)
- 20160227.CCPP体系详解(0037天)
- 20160226.CCPP体系详解(0036天)
- 20160225.CCPP体系详解(0035天)
- 20160223.CCPP体系详解(0033天)
- 20160222.CCPP体系详解(0032天)
- 20160221.CCPP体系详解(0031天)
- 20160220.CCPP体系详解(0030天)
- 20160217.CCPP体系详解(0027天)
- 20160214.CCPP体系详解(0024天)
- 20160213.CCPP体系详解(0023天)
- 20160210.CCPP体系详解(0020天)
- 20160209.CCPP体系详解(0019天)
- 20160207.CCPP体系详解(0017天)
- 20160203.CCPP体系详解(0013天)
- 20160202.CCPP体系详解(0012天)
- 20160201.CCPP体系详解(0011天)
- 20160131.CCPP体系详解(0010天)