20160129.CCPP体系详解(0008天)

程序片段(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;//全局变量当做声明看待,如果没有初始化,将会被编译器默认赋予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");
}

posted on 2016-02-16 21:46  三少爷的剑123  阅读(231)  评论(0编辑  收藏  举报

导航