20160129.CCPP体系详解(0008天)

来源:互联网 发布:杭州java培训机构 编辑:程序博客网 时间:2024/06/03 17:59

程序片段(01):函数.c+call.c+测试.cpp
内容概要:函数

///函数.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;//全局变量当做声明看待,如果没有初始化,将会被编译器默认赋予0int 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-->value1void 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");}
1 0
原创粉丝点击