20160212.CCPP体系详解(0022天)

程序片段(01):01.二维数组.c
内容概要:二维数组

#include <stdio.h>
#include <stdlib.h>

//01.关于栈内存开辟数组:
//  诀窍:将所有维度的数组看做为一维数组,
//      然后再采用指向该数组当中首个元素的指针(变量|常量)
//  秘诀:原始数组数组名称替换法:
//      就可以直接得到指向数组的指针(将数组名称-->替换为-->(*pArr))
//  特点:指针变量可以不用最高维度,
//      但是类型转换必须加上表示最高维度的中括号!
int main01(void)
{
    //int arrArr[3][4];//位于栈内存:
    int * p1 = (int []) {0};//一维数组-->栈上开辟
    int(*p2)[4] = (int[][4]) { 0 };//二维数组-->栈上开辟
    int(*p3)[3][4] = (int[][3][4]) { 0 };//三维数组-->栈上开辟

    system("pause");
}

//02.堆栈开辟指针数组以及指针数组的回收顺序!
//  1.防止内存泄露现象的产生
//  2.动态数组可以按照静态数组的方式进行访问!
int main02(void)
{
    //堆内存开辟数组!
    int ** pp = calloc(3, sizeof(int *));//分配指针数组[一级指针数组!]
    for (int i = 0; i < 3; ++i)
    {
        pp[i] = malloc(4 * sizeof(int));//每个指针必须分配内存!
    }

    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            //&pp[i]-->pp+i
            //&pp[i][j]-->*(pp+i)+j
            //pp[i]-->*(pp+i)
            //pp[i][j]-->*(*(pp+i)+j);
            //printf("%4d", pp[i][j] = num++);
            printf("%4d", *(*(pp + i) + j));
        }
        printf("\n");
    }
    //严格注意堆内存当中数组的回收顺序!
    for (int i = 0; i < 3; ++i)
    {
        free(pp[i]);//先回收一级指针所指向的内存块儿-->防止堆内存泄露
    }
    free(pp);//再释放指针数组

    system("pause");
}

//03.栈上开辟二维数组:
//  注:在进行栈内存的类型转换的时候,需要一个准确的数组!
int main03(void)
{
    //栈上开辟二维数组
    int(*p)[4] = (int[3][4]) { 0 };//栈上开辟二维数组,自动回收
    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d", p[i][j] = num++);
        }
        printf("\n");
    }

    system("pause");
}

//04.分块儿数组的逐级回收特点!
int main04(void)
{
    //分块儿内存切忌要逐级进行内存回收!
    int(*pArr)[4] = malloc(3 * 4 * sizeof(int));//堆内存:呈线性存储的二维数组
    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d \n", pArr[i][j] = num++);
        }
        printf("\n");
    }
    free(pArr);//根据指向堆内存的连续存储的二维数组指针,进行堆内存二维数组的回收操作!

    system("pause");
}

程序片段(02):01.函数指针.c
内容概要:函数指针

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

void runmsg()
{
    MessageBoxA(0, "您好!", "天朝城管的全家!", 0);
}

void print()
{
    printf("%s, %s \n", "您好!", "天朝城管的全家!");
}

//01.严格区分函数指针变量和函数指针常量:
//  函数名的本质:函数指针常量,存储的是代码区函数实体的入口点!
//  函数指针的本质:存储函数指针常量的数据|存储代码区函数实体的入口点
//      通过修改函数指针变量所存储的入口点,让函数指针变量可以调用
//      代码区当中不同的函数实体,从而实现改变函数行为的现象!
//02.对函数名的几种操作:
//  &funName--funName--*funName
//注:这几种方式所得到的值相同,都是同一个函数实体的入口点地址!
//      区分函数声明入口点地址和函数调用地址!
int main01(void)
{
    //函数指针变量:自己存储与数据区当中,只是存储了代码区的入口点(函数指针常量)
    //runmsg = runmsg;//函数名的本质:函数指针常量,存储的是代码区函数体的入口点儿
    void(*pFun)() = runmsg;//通过函数指针变量存储函数指针产量的值|存储代码区函数实体的调用地址
    pFun();//通过函数指针变量实现函数的间接调用
    pFun = print;//修改函数指针变量所存储的函数指针常量,从而实现调用行为的改变
    pFun();
    printf("%p, %p, %p \n", &runmsg, runmsg, *runmsg);
    printf("%p \n", print);

    system("pause");
}

//03.对于代码区而言:
//  1.函数指针有类型之分-->函数指针声明格式的类型说明!
//  2.对函数名的几种取值操作所得到的值的性质一样
int main02(void)
{
    printf("%p, %p, %p \n", &runmsg, runmsg, *runmsg);
    //对于代码区而言,函数指针有类型区分
    //&runmsg = runmsg;//编译器原理:获取函数指针的地址,和函数地址一样
    //*runmsg = *(&runmsg)=runmsg

    system("pause");
}

程序片段(03):01.DllInject.c
内容概要:非法调用

//moveto(int z, int x, int y);//跨点函数:所跨越的维度为三维

_declspec(dllexport)void main()
{
    void(*pFun)(void) = 0x0134110E;//声明一个函数指针变量,用于间接调用函数实体
    pFun();//调用
}

程序片段(04):01.数组名.c+02.二维数组.c
内容概要:数组名作为函数参数

///01.数组名.c
#include <stdio.h>
#include <stdlib.h>

//数组名作为函数的参数,会退化为指针
void run01(int a[5])//一维数组没有副本机制,作为参数退化为一级指针
{
    printf("\n run=%d", sizeof(a));//大小是4
    a = a + 1;//数组名可以进行改变,因为已经变为了指针变量的类型
    //int b[5];
    //b = b + 1;
}

void run02(int *p)//一位数组名作为该函数的参数同样会退化为一级指针,所以这里采用一级指针进行接收
{
    for (int i = 0; i < 5; i++)
    {
        printf("\n %d", p[i]);
    }
}

void rev(int *p, int length)
{
    //指针法,由于下标,表现对指针的熟练度
    for (int *phead = p, *pback = p + length - 1; phead < pback; phead++, pback--)
    {
        int temp = *phead;
        *phead = *pback;
        *pback = temp;
    }
}

void show(int *p, int length)//一位数组名作为函数的参数进行传递会自动退化为一级指针
{
    for (int i = 0; i < length; i++)
    {
        printf("\n %d", p[i]);
    }
}

void main01()
{
    int a[5] = { 1,2,3,4,5 };
    //a=1;
    printf("%d", sizeof(a));
    run02(a);

    system("pause");
}

void main02()
{
    int a[5] = { 1,2,3,4,5 };
    int *p = (int[]){ 1, 2, 3, 4, 5, 6 };
    rev(a, 5);
    show(a, 5);
    printf("\n\n");
    rev(p, 6);
    show(p, 6);

    system("pause");
}
///02.二维数组.c
#include <stdio.h>
#include <stdlib.h>

//01.在C语言当中的static关键字用途:
//  限定(变量|函数)只能作用于本文件;
//  相当于缩小全局变量的作用范围!
//02.数组作为函数形参的退化指针规律:
//  将所有数组看做为一维数组;
//  那么指向该数组当中首个元素的指针(变量|常量)
//  的指针就是该数组所退化成为的指针
//03.如何快速定位堆上数组的解析方式?
//  开辟几位数组就采用指向几位数组的指针进行解析!
//  至于指针的声明格式采用上一个说明规律!
static void show(int(*pArr)[4])
{
    printf("run: sizeof(pArr) = %d \n", sizeof(pArr));
    //pArr = pArr + 1;//变量指针:数组名作为函数指针将退化成为变量指针!
    int res = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 4; j < 4; ++j)
        {
            printf("%3d", res += pArr[i][j]);
        }
        printf("\n");
    }   
    printf("平均分 = %d \n", res /= 3);//统计所用总分/人数
}

void search(int(* pArr)[4], int ifind)
{
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d", pArr[i][j]);
        }
        printf("\n");
    }
}

void get(int(*pArr)[4])
{
    int flag = -1;
    for (int i = 0; i < 3; ++i)
    {
        flag = 1;
        for (int j = 0; j < 4; ++j)
        {
            if (*(*(pArr + i) + j) < 60)
            {
                flag = 0;
                break;
            }
        }
        if (flag)
        {
            printf("%d号学生的成绩及格! \n", i);
        }
        else
        {
            printf("%d号学生的成绩不及格! \n", i);
        }
    }
}

//04.关于数组内存的开辟方式:
//  1.按照所属内存区块儿:
//      栈内存+堆内存
//  2.按照解析方式:
//      标准二维数组+分块数组模型
//  注:位于栈内存的数组都是连续存储的,位于堆内存的数组视情况而定(指针决定对该片儿内存的解析方式)
int main03(void)
{
    int arrArr[3][4] = { 89, 78, 55, 71, 82, 54, 53, 70, 100, 98, 99, 91 };
    //这样分配的二维数组是位于堆内存的连续存储空间(整体连续),而采用二级指针所指向的数组是非整体连续的
    int(*pArr)[4] = (int[][4]) { 89, 78, 65, 71, 82, 94, 93, 70, 100, 98, 99, 91 };//栈上的二维数组
    show(pArr);
    search(pArr, 2);
    get(pArr);

    system("pause");
}

程序片段(05):01.函数指针.c
内容概要:函数指针强化

#include <stdio.h>
#include <stdlib.h>

//01.在C语言当中,当形参位于函数声明位置的时候:
//  可以不用指明形参名称;但是函数实现的时候需要指明形参名称!
int add(int, int);

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int divv(int a, int b)
{
    return a / b;
}

int getmin(int a, int b)
{
    return a < b ? a : b;
}

int getmax(int a, int b)
{
    return a > b ? a : b;
}

//02.采用函数指针作为形参可以实现固化接口的作用
//  固化接口+动化逻辑!
void op(int(*pFun)(int, int), int a, int b)
{
    printf("%d \n", pFun(a, b));
}

//02.只要作为声明的格式,就可以进行函数形参的省略!
//  1.区分函数调用和读取函数指针常量所存储的函数入口点地址
//  2.对于函数指针变量没有自变的说法,因为毫无意义!
//03.区分函数指针变量和函数指针变量的类型!
//  诀窍:挖取函数指针变量声明格式当中的函数变量名就是
//      该函数指针的具体类型!
int main01(void)
{
    //错误的定义方式,因为类型不匹配
    //int(*pFun)(int, int) = add(1, 2);//不行:函数调用-->返回结果-->被当做函数调用地址!-->错误现象
    //参数名可以省略,声明的结构
    int(*pFun)(int, int) = add;
    //p1++;//函数指针变量,没有变的说法!
    //p2++;
    //p3 + n;

    //int(*pFun)(int, int);//函数指针
    //int (*)(int, int)//函数指针的类型
    int(* get() )(int, int);//一个返回值为函数指针,参数为void类型的函数常量指着

    //难度解析:规律剖析
    //  原始:int(*get(int(*y)(int, int), double))(int, int);
    //  剖析:int(*        get(        int (*y)(int, int), double      )       )(int, int)
    //  解析:一个返回值为函数指针(int (*)(int, int)),参数为函数指针(int(*y)(int, int))和双精度浮点型(double)的函数指针常量
    //  特点:函数实现的时候,所有形参必须具备形参名称!
    //      作为函数的返回值类型声明不需要进行名称说明!
    //  拓展:指向该函数指针的函数指针变量声明格式
    //  int (*(*pFun)(int(*y)(int, int), double))(int, int)-->该函数指针常量所对应的函数指针变量类型
    //  int (* (* const pFun)(int(*y)(int, int), double))(int, int)-->该函数指针常量所对应的函数指针类型
    system("pause");
}

程序片段(06):函数指针数组.c
内容概要:函数指针数组与指向函数指针的指针

#include <stdio.h>
#include <stdlib.h>

int getmin(int a, int b)
{
    return a < b ? a : b;
}

int getmax(int a, int b)
{
    return a > b ? a : b;
}

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int divv(int a, int b)
{
    return a / b;
}

//01.数组格式的规律:
//  在数组所存储元素的定义格式基础上,在数组元素名称的末尾添加中括号[数组元素个数];
//      该数组元素名称成为数组名称
//02.函数指针的规律:
//  在函数声明格式的基础之上,将函数名称直接替换为(*pFun),那么pFun
//      就是指向该函数的函数指针(*pFun)-->函数变量指针;(* const pFun)函数常量指针,如同数组名
int main01(void)
{
    //int a;
    //int a[10];
    //int *p;
    //int *arrP[10];

    int(*funP)(int, int) = getmax;
    //函数指针数组
    int(*funPArr[10])(int, int) = { getmin, getmax, add, sub, mul, divv };
    //funPArr是函数指针数组的数组名,二级函数指针可以直接存储一个一级函数指针数组的首地址
    //int (**funP1)(int, int)<=>int (*funP2[6])(int, int)
    //  funP1是二级函数变量指针+funP2是二级函数常量指着
    //注:凡是设计数组名都是常量指针
    //printf("%d \n", sizeof(funP1));
    //funP2 = funP1;//funP2是一个常量指针
    for (int i = 0; i < 6; ++i)
    {//索引遍历
        //printf("%d \n", funPArr[i](1, 2));//funPArr[i]代表函数指针变量本身
        printf("%d \n", (*(funPArr + i))(1, 2));//funPArr[i]=>*(funPArr + i)
    }
    for (int(**funPP)(int, int) = funPArr; funPP < funPArr + 6; ++funPP)
    {
        printf("%d", (*funPP)(100, 10));
    }

    system("pause");
}

//03.函数指针相关概念!
//int (*funP)(int, int)
//  funP是一级函数变量指针
//int (*funPArr[10])(int, int)
//  funPArr是二级函数常量指针
//int (**funPP)(int, int)
//  funPP是二级函数变量指针

//04.所有数组的推导特点
//int a;
//int a[10];
//int * a;
//int * p1;
//int * p1[10];
//int ** p1;
int main02(void)
{
    //int intArr[6] = { 1, 2, 3, 4, 5, 6 };
    int(*funPArr[6])(int, int) = { getmin, getmax, add, sub, mul ,divv };
    //intArr和funPArr都属于常量指针:作为数值而言,都是存储与代码区符号表当中
    //int * p = (int []){ 1, 2, 3, 4, 5, 6 }//栈上开辟一个一维数组
    //int(**funPP)(int, int);//二级函数变量指针,存储函数指针数组的数组名
    //int (*[])(int, int);//函数指针数组类型
    int(**funPP)(int, int) = (int(*[])(int, int)) { add, sub, mul, div };
    for (int i = 0; i < 4; ++i)
    {
        //printf("%d \n", funPP[i](100, 10));
        printf("%d \n",(*(funPP + i))(100, 10));
    }

    system("pause");
}

int main03(void)
{
    int(**funPP)(int, int) = malloc(4 * sizeof(int(*)(int, int)));//在堆内存开辟一个一级函数指针数组
    *funPP = add;
    *(funPP + 1) = sub;
    *(funPP + 2) = mul;
    *(funPP + 3) = divv;
    for (int i = 0; i < 4; ++i)
    {
        //printf("%d \n", funPP[i](100, 10));
        printf("%d \n", (*(funPP + i))(100, 10));
    }

    system("pause");
}

//05.变量+变量数组+指向变量的变量:
//  三种形式的推导规律
//int * p;------->int (*funP)(int, int);
//int * p[10];--->int (*funP[10])(int, int);
//int ** pp;----->int (**funPP)(int, int)

//06.typedef的强大作用:
//  某些情况之下的复杂函数指针必须通过typedef进行别名定义!
//int a;
//typedef int a;
//int b[10];
//typedef int b[10];
//double * d;
//typedef double * d;
//int (* funP )(int, int);
//typedef int (*funP)(int, int);
//int (*funP[10])(int, int);
//typedef int (*funP[10])(int, int);
//int (**funPP)(int, int);
//typedef int (**funPP)(int, int);
//typedef终极规律:
//  先定义变量名:类型+变量名
//  再使用前置typedef:于是变量名就成为了类型别名
//注:typedef并不是生产出一个新类型,而是为已有类型领取一个简洁的别名
//  编译后期操作类型-->某些情况之下的复杂函数指针不得不使用typedef关键字
//  进行别名定义!
int main04(void)
{
    //a a1;
    //b b1;
    //d d1;
    //p d1 = add;
    //px px1 = { add, sub, mul, divv };
    //pp pp1 = (px){ add, sub };
    //printf("%d \n", sizeof(px1));

    system("pause");
}

//07.函数指针终极形式!
//  原始:int(**x(int(*z)(int,int),int,double))(int);
//  返回值:int(**   x(int(*z)(int,int),int,double)   )(int)
//  形参:int(**   x(  int(*z)(int, int), int, int )   )(int)
//  解读:
//      函数名:x是一个函数的名称,属于函数常量指针
//      返回值:int(**)(int),是一个函数二级函数指针
//      形参:int (*z)(int, int), int, double,有三个形式参数
//           分别是函数指针类型+int类型+double类型
//注:区分数组指针和函数指针
//  数组指针:
//  int arrArr[a][b]-->int (*p)[a][b];
//注:指向N维数组的指针声明规律:
//  只需要将数组名称替换为(*p)

//08.数组指针和函数指针的区别:
//  1.都含有(*p)说明特点
//  2.各自的后续内容不一样:
//      数组指针跟的是中括号!
//      函数指针跟的是小括号!
//注:所有指向数组类型的指针定义绝招!
//  先写出数组的声明格式,再将数组名称替换为(*pArr);
//注:所有指向函数类型的指针定义绝招!
//  先写出函数的声明格式,再将函数名称提花为(*Fun);
int main05(void)
{
    int(*funPArr[10])(int, int);
    int(*(*pFunPArr)[10])(int, int);//指向函数数组的指针

    system("pause");
}

程序片段(07):dialog.cpp
内容概要:PingWindows

#include "dialog.h"
#include <QApplication>

void run()
{
    //01.在栈内存当中创建对话框容易自动回收掉!
    //Dialog w;
    //w.show();

    //02.在堆内存当中创建的对话框需要手动回收!
    //Dialog * p = new Dialog;
    //(*p).show();
    //p->show();

    //03.同样是在栈内存当中创建的对话框
    //Dialog ws[10];
    //for (int i = 0; i < 10; ++i)
    //{
    //    ws[i].resize(100, 100);
    //    ws[i].move(100*i, 100*i);
    //    ws[i].show();
    }

     //04.逐个在堆内存当中创建对话框,不会被自动回收掉!
     //Dialog * p[10];
     //for (int i = 0; i < 10; ++i)
     //{
     //   p[i] = new Dialog;
     //   p[i]->resize(100, 100);
     //   p[i]->move(100*i, 100*i);
     //   p[i]->show();
     //}

    //5.创建50个位于堆内存当中的对话框
    //Dialog *px[5][10];
    //for(int i=0;i<5;i++)
    //{
    //    for(int j=0;j<10;j++)
    //    {
    //        px[i][j]=new Dailog;
    //        px[i][j]->resize(100,100);
    //        px[i][j]->move(100*i,100*j);
    //        px[i][j]->show();
    //    }
    //}
}

int main(int argc,char *argv[])
{
    QApplication a(argc,argv);
    //run();
    Dialog w;
    w.show();

    return a.exec();
}

程序片段(08):01.多线程.c
内容概要:函数指针数组与多线程

///01.多线程.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>

void run01(void * p)
{
    MessageBoxA(0, "1", "1", 0);
}

void run02(void * p)
{
    MessageBoxA(0, "2", "2", 0);
}

void run03(void * p)
{
    MessageBoxA(0, "3", "3", 0);
}

//01.多线程内容:
//  1.关于模式的不同:
//      单线程模式:只能实现单线程,不能实现多线程
//      多线程模式:技能实现单线程,又能实现多线程
//  2.关于多线程的等待特点:
//      无限等待:-->如同单线程
//      有限等待:-->定时等待!
//  3.多线程情况下的主辅线程特点:
//      主线程进行全局控制
//      辅助线程进行独立控制
//  4.线程句柄数组的使用:管理线程
//      可以实现同时等待多条线程
//02.关于是否需要对栈内存数组进行强制转换的问题:
//  指向数组的指针,无需进行类型转换
//  指向指针的指针,需要进行类型转换
//03.关于多线程的调度问题:
//  需要使用到线程句柄数组进行管理
//  WaitForMutipleObjects(arg1,arg2,arg3,arg4);
//      arg1:线程句柄个数
//      arg2:线程句柄数组名
//      arg3:等待单个还是多个?-->竞赛最快的那个!-->可以等待单个!
//      arg4:等待时间
int main01(void)
{
    //run01(NULL);
    //run02(NULL);
    //run03(NULL);//-->单线程模式

    for (int i = 0; i < 3; ++i)
    {
        HANDLE hd = _beginthread(run01, 0, NULL);
        //WaitForSingleObject(hd, INFINITE);//无限同步等待
        WaitForSingleObject(hd, 3000);//有限同步等待!
    }

    //HANDLE hd[3] = {0};//声明线程句柄数组
    //HANDLE * hd = malloc(12);
    HANDLE * hd = malloc(3 * sizeof(HANDLE));
    //void(*funPArr[3])(void *) = { run01, run02, run03 };//函数指针数组
    //采用二级函数变量指针进行指向一个栈内存的函数指针数组
    void(**funPP)(void *) = (void(*[])(void *)) { run01, run02, run03 };
    for (int i = 0; i < 3; ++i)
    {
        hd[i] = _beginthread(funPP[i], 0, NULL);
    }
    //TRUE等待所有线程,false表示等待单个
    WaitForMultipleObjects(3, hd, FALSE, INFINITE);//无限等待多个线程结束
    //主线程,控制全局

    system("pause");//卡顿主线程-->让主线程不要提前执行完毕
}

////线程既可以同步,也可以异步
////WaitForSingleObject(hd,300);//3000等待3秒
////主线程,控制全局
////WaitForMultipleObjects(3,hd,TRUE,INFINITE);

程序片段(09):01.Alloca.c
内容概要:栈内存分配

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

//01.关于堆内存开辟四大函数和栈内存开辟单函数:
//  堆内存:malloc-->calloc-->realloc-->_recalloc
//  栈内存:alloca-->位于malloc.h头文件
//注:栈内存没有对应的内存回收函数,堆内存才有对应
//  的内存回收函数!!
int main01(void)
{
    int * p = alloca(10 * sizeof(int));//超过栈内存尺寸上限
    for (int i = 0; i < 10; ++i)
    {
        p[i] = i;
    }
    //free(p);//栈内存没有回收的free();函数

    system("pause");
}

void show()
{
    int * p = alloca(10 * sizeof(int));//超过栈内存大小
    for (int i = 0; i < 10; ++i)
    {
        p[i] = i;
    }
    //free(p);//栈内存没有匹配的free函数-->自动回收所属内存
    printf("\n");
}

int main02(void)
{
    show();
    printf("\n\n");//促进内存回收动作的发生
    show();//检验栈内存数组的自动回收特点
    printf("\n");//注意编译器的优化情况

    system("pause");
}

程序片段(10):main.c
内容概要:GccArray

#include <stdio.h>
#include <stdlib.h>

//01.GCC支持栈内存的动态数组的实现原理就是
//  位于malloc.h头文件当中的alloca函数!
//02.VC编译器不支持位于栈内存的动态数组!
int main01()
{
    printf("Hello world! \n");

    //GCC当中的实现原理就是C语言当中的Alloc,但是这种规则不是C语言的标准机试
    int num=20;
    int a[num];

   // a=a;
    return 0;
}

程序片段(11):01.Main.c
内容概要:获取参数

#include <stdio.h>
#include <stdlib.h>

//01.定义主函数的格式:
//  无形参形式:
//      void main(void);
//      int main(void);
//  有形参形式:
//      void main(int argc, char * args[]);
//      int main(int argc, char * args[]);  
//02.标准主函数声明格式详解:
//  int main(int argc, char * args[], char * envp[]);
//      返回值:int-->体现跨平台特性
//      参数1:argc-->说明了有效参数个数
//      参数2:args-->说明了有效参数列表
//          该字符指针数组当中的第一个字符指针所指向的字符串是当前所运行程序的程序名称(代码区常量池"字符串")
//          该字符指针数组当中的第二个字符指针开始之后的指针表示的是当前程序运行时所指定的附加参数!
//      参数3:表示环境变量列表!
//          一个字符指针描述一个环境变量!
int main01(int argc, char * args[])
{
    for (int i = 0; i < argc; ++i)
    {
        puts(args[i]);
    }

    system("pause");
}

int main02(int argc, char * args[], char * envp[])
{
    printf("%d \n", sizeof(envp));//凡是数组作为函数形参,该数组都将会被退化为指针(统统只会占用4个内存字节!)
    //for (int i = 0; i < 100; ++i)
    //{
    //  puts(envp[i]);
    //}

    char **pp = envp;
    while (NULL != *pp)
    {
        puts(*pp);
        ++pp;
    }

    system("pause");
}

程序片段(12):01.返回.c
内容概要:返回指针

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

//01.对字符数组除了定义并初始化格式之外的字符串赋值方式!
//  必须借助strcpy();函数进行对字符数组的赋值操作!
//注:所以数组的数组名都绝对是一个常量指针!
//02.被掉函数在进行返回指针的时候绝对不能够返回指向栈内存
//  的指针!切忌这一点儿
char * get()
{
    char str[100] = { 0 };//栈内存-->字符数组
    strcpy(str, "Hello ZhouRuiFu");//为字符数组赋予字符串
    printf("%s \n", str);
    return str;
}

//03.如何快速定位某个数组的两种形式:
//  1.数组的两种形式:分别用指针形式进行表述
//      普通数组形式-->指向数组的指针
//      指针数组形式-->指向指针的指针
//  2.任何数组都可以用两种形式的指针进行指向!
//      规律总结:针对任何一个维度的数组!
//      普通数组形式-->指向数组的指针:将数组名直接替换为(*arrP)
//      指针数组形式-->指向指针的指针:将数组名以及最高维度直接替换为(*pArr)
int * rungetmin()
{
    int * p = calloc(10, sizeof(int));//不会进行自动回收!
    time_t te;
    unsigned int seed = (unsigned int)time(&te);
    srand(seed);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d \n", p[i] = rand() % 100 + 1);
    }
    int minIndex = 0;
    for (int i = 1; i < 10; ++i)
    {
        if (p[i] < p[minIndex])
            minIndex = i;
    }
    //p + minIndex = &p[minIndex];
    //p[minIndex] = *(p + minIndex)
    return p + minIndex;
}

//04.printf();函数针对于字符串的解析特点:
//  字符数组:按照字符数组的长度+直接解析到'\0'
//      如果没有解析到'\0'就会出现烫烫...
//  字符指针:解析字符指针所指向的字符串本身+直接解析到'\0'
//      分字符指针所指向的内存空间
//          如果是栈内存和堆内存-->有可能字字符串没有字符串结尾标识符'\0'
//          如果是代码区常量池-->就一定会有字符串结尾标识符'\0'
int main01(int argc, char * args[], char * envp[])
{
    char str[100] = { 0 };
    strcpy(str, "Hello ZhouRuiFu");
    printf("%s \n", str);

    system("pause");
}

//05.迷途指针
int main02(int argc, char * argv[], char * envp[])
{
    char * p = get();
    printf("%s \n", p);//函数在返回指针的时候,不可以返回指向栈内存空间的指针
    //因为有可能出现迷途指针

    system("pause");
}

int main03(int argc, char * argv[], char * envp[])
{
    printf("最小的是%d \n", *rungetmin());

    system("pause");
}

程序片段(13):01.函数返回.c
内容概要:函数返回值

//#include <stdio.h>
//#include <stdlib.h>
//
////01.函数返回值,存在副本机制!
////    高端CPU:CPU-Cache
////    底端CPU:MEM-Cache
////注:副本数据和副本本身!
////    副本数据不可以获取地址!
////    副本本身没有操作权限!
////    原本只是在已经被回收的栈内存!
//02.原本本身-->原本数据-->副本本身-->副本数据!
//int get()
//{
//  int a = 5;
//  return a;
//}
//
//int main(int argc, char * args[], char * envp[])
//{
//  //&get();//get();该操作所对应的返回值位于寄存器高速缓存(高级CPU),(低级CPU)会将内存某段儿空间当中存储
//  //  但是同样不允许进行访问操作!-->针对对于数值本身而言就是副本数据!
//  printf("%d \n", get());//返回的是副本数据
//
//  system("pause");
//}

程序片段(14):Mem.c
内容概要:左值右值与内存实体

#include <stdio.h>
#include <stdlib.h>

//01.左右值:
//  左值:
//      1.能够出现在赋值号左边的值
//      2.存在有效的内存实体
//      3.变量
//  右值:
//      1.只能够出现在赋值号右边的值
//      2.不存在有效的内存实体
//      3.常量+表达式(寄存器操作)
int main01(int argc, char * args[], char * envp)
{
    int a = 1;
    a = a + 10;
    a = a + 8;
    //a = 3;
    //&5;
    //&(a + 1);
    a = a;//左值都存在内存实体
    //右值一般是出于寄存器当中,左值也可以当做右值进行使用
    //a + 1 = 3;

    system("pause");
}

int main02(int argc, char * args[], char * envp)
{
    int a = 10;
    int b = 20;
    printf("%p, %p \n", &a, &b);
    int * p;//指针变量
    p = &a;
    p = &b;

    system("pause");
}

//02.取地址符(&)取值之后的地址数值具备类型特点!
//  指针常量(数值不可变)+常量指针(指向不可变)
int main(int argc, char * args[], char * envp)
{
    //int num;//num是左值
    //const int num;//num是左值-->存在内存实体-->常变量
    const int num = 10;//&num-->int const * num:
    *(int *)(&num) = 4;//取地址之后的所得到的地址数值存在类型特点
    printf("%d \n", num);

    system("pause");
}

int main04(int argc, char * args[], char * envp[])
{
    //注意指针类型的统一!
    int * p = (int *)3;//地址意义!
    *p = 3;
}

//03.区分两个概念:(空类型的指针+空指针)
//  空类型的指针:
//       实质:空类型的指针变量
//  空指针:
//      用于标识指针变量没有任何指向!
int main05(int argc, char * argv[], char * envp[])
{
    int * p = NULL;//标识P这个指针变量没有任何指向
    int num = 20;
    void * pv = &num;//任何类型的地址(只是用于存储地址,但是不知道如何进行解析!)
    *(int *)pv;

    system("pause");
}

程序片段(15):01内存.c
内容概要:Malloc.Calloc.Realloc

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

//01.内存五大开辟函数总结:
//  堆内存:
//      malloc:不会初始化内存+整体内存尺寸
//      calloc:会初始化内存+元素个数+单个内存尺寸
//      realloc:不会初始化内存+原始指针+拓展后的总体大小
//      _recalloc:会初始化内存+原始指针+拓展后的元素个数+拓展后的单个内存尺寸
//  栈内存:
//      alloca:
//          位于malloc.h头文件
//          实际占用的内存总尺寸!
int main01(int argc, char * args[], char * envp)
{
    int * p = calloc(25, sizeof(int));//在堆内存开辟一个普通类型的一维数组,会自动初始化为0
    printf("%p \n", p);
    for (int i = 0; i < 25; ++i)
    {
        p[i] = i;
    }
    p = _recalloc(p, 50, sizeof(int));//内存推展+内存清零操作
    for (int i = 25; i < 50; ++i)
    {
        p[i] = i;
    }

    system("pause");
}

int main02(int argc, char * args[], char * envp)
{
    int * p = malloc(10 * sizeof(int));//只能使用这片儿内存
    int * p_p = malloc(100);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d \n", p[i] = i);
    }
    printf("p = %p \n", p);
    int * px = realloc(p, 200);//拓展内存,内存不会清零
    //返回值为内存地址,拓展成功,后续地址拓展,拓展不成功,将会重新开辟
    //原始的内存空间将会自动回收
    printf("px= %p \n", px);
    for (int i = 0; i < 50; ++i)
    {
        px[i] = i;
        printf("%d \n", px[i]);
    }
    p[2398787] = 10;

    system("pause");
}

int main03(int argc, char * args[], char * envp)
{
    //int * p = malloc(100);//malloc不会初始化内存,是整体内存尺寸!
    int * p = calloc(25, sizeof(int));//calloc会初始化参内存,个数+尺寸
    printf("%p \n", p);
    for (int i = 0; i < 25; ++i)
    {
        p[i] = i;
    }
    int * p = alloca(123);

    system("pause");
}

程序片段(16):01.c
内容概要:Test

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

//01.在栈内存开辟4个字节的内存空间:
//  注:指针可以指向四区的任何位置,只是指针的类型不一样而已!
int * run()
{
    int * p = alloca(4);
    *p = 4;
    printf("%d \n", *p);

    return p;
}

int main01(int argc, char * args[], char * envp[])
{
    int * px = run();//返回指向栈内存的指针是错误的做法!
    printf("\n");
    printf("%d \n", *px);//栈内存在函数出栈之后,立即进行回收!

    system("pause");
}

//02.关于多维数组获取其所存储数据的规律:
//  一维数组:一颗星
//  二维数组:两颗星
//  三维数组:三颗星
//  N维数组:N颗星...
//注:获取的对象既可以是常量指针,也可以是变量指针!
//  无论常量指针还是变量指针都会被当做数组名对待!
int main02(int argc, char * args[], char * envp[])
{
    int arrArr[3][4] = {
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12
    };//栈内存-->二维数组-->静态初始化!-->代码块儿直接初始化!
    //printf("%d \n", arrArr[1][1]);
    printf("%d \n", *(arrArr + 1)[1]);//[]的优先级既大于星号("*")又大于点号(".")
    printf("%d \n", *(*(arrArr + 1) + 1));
}

程序片段(17):01.Search.c
内容概要:内存模型大数据

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define srcPath "E:\\Resource\\TestData\\BigDB\\1EQQBig.txt"

//01.内存架构:文件缓冲于内存当中,将内存用作数据临时缓冲!
char ** g_pp;//全局二级指针变量
int imax = 84357147;//文件总行数
int jmax = 20027;//文件最宽列数

int getimax()
{
    int hang = -1;
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("文件读取失败! \n");
        return -1;
    }
    else
    {
        printf("开始读取! \n");
        hang = 0;
        while (!feof(pf))//到了文件末尾返回1,没有到文件末尾返回0
        {
            char readStr[1024] = { 0 };
            fgets(readStr, 1024 - 1, pf);//一次读取一样进缓冲区
            ++hang;//自增
        }
        fclose(pf);
    }
    return hang;
}

int getjmax()
{
    int width = -1;
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("文件读取失败! \n");
        return -1;
    }
    else
    {
        printf("开始读取! \n");
        while (!feof(pf))
        {
            char readStr[30000] = { 0 };
            fgets(readStr, 30000 - 1, pf);//每次读取一行的数据到手动字符数组缓冲区当中
            int strLength = strlen(readStr);
            if (strLength > 50)//字符串筛选
            {
                //pus(readstr);
                if (strLength > width)
                {
                    width = strLength;
                }
            }
        }
        fclose(pf);
        printf("结束读取! \n");
    }
    return width;
}

//02.字符串函数的处理特点:
//  不仅可以处理字符数组还可以处理字符指针!
//  兼容C语言当中的两种字符串表示方式!
//加载待读取的文件数据进内存,构建内存架构模型
void loadFromFile()
{
    //声明并初始化指针数组
    g_pp = (char **)malloc(imax * sizeof(int *));//分配指针数组
    memset(g_pp, '\0', imax * sizeof(char *));//清空指针数组
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("文件读取失败! \n");
        return -1;
    }
    else
    {
        for (int i = 0; i < imax; ++i)
        {
            char str[1024] = { 0 };
            fgets(str, 1024 - 1, pf);//(按行+个数)进行字符串读取
            int strLength = strlen(str);
            if (strLength < 50)
            {
                g_pp[i] = malloc((strLength + 1)* sizeof(char));
                strcpy(g_pp[i], str);
            }
        }
        fclose(pf);
    }
}

//03.在内存架构模型当中执行检索操作!
void search(char * str)
{
    if (NULL == g_pp)
        return;
    for (int i = 0; i < imax; ++i)
    {
        if (NULL != g_pp[i])
        {
            char * p = strstr(g_pp[i], str);
            if (NULL != p)
            {
                puts(g_pp[i]);
            }
        }
    }
}

int main01(int argc, char * args[], char * envp[])
{
    //printf("imax = %d \n", getimax());
    //printf("jmax = %d \n", getjmax());

    loadFromFile();
    while (1)
    {
        char str[100] = { 0 };
        scanf("%s", str);
        search(str);
    }

    system("pause");
}

程序片段(18):01.KaiFang.c
内容概要:开放数据检索

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>

#define srcPath "E:\\Resource\\TestData\\BigDB\\KaiFang.txt"
char ** g_pp;
int imax = 20151574;

int getimax()
{
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("读取文件失败! \n");
        return -1;
    }
    printf("读取文件成功! \n");
    int hang = 0;
    while (!feof(pf))
    {
        char readStr[1024] = { 0 };
        fgets(readStr, 1023, pf);
        ++hang;
    }
    fclose(pf);
    return hang;
}

void loadfromfile()
{
    g_pp = (char **)malloc(imax * sizeof(int *));
    memset(g_pp, 0, imax * sizeof(char *));
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("打开文件失败! \n");
        return -1;
    }      
    printf("打开文件成功! \n");
    for (int i = 0; i < imax; ++i)
    {
        char str[1024] = { 0 };
        fgets(str, 1023, pf);
        int strLength = strlen(str);
        g_pp[i] = (char *)malloc((strLength + 1) * sizeof(char));
        if (NULL != g_pp[i])
            strcpy(g_pp[i], str);
    }
    fclose(pf);
}

void searchStr(char * str)
{
    char strPath[100] = { 0 };
    sprintf(strPath, "E:\\Resource\\TestData\\Test\\%s.txt", str);
    FILE * pf = fopen(strPath, "w");
    if (NULL == g_pp)
        return;
    for (int i = 0; i < imax; ++i)
    {
        if (NULL == g_pp[i])
            continue;
        char * p = strstr(g_pp[i], str);
        if (p == NULL)
            continue;
        puts(g_pp[i]);
        fputs(g_pp[i], pf);
    }
    fclose(pf);
    system(strPath);
}

int main01(void)
{
    //printf("imax = %d \n", getimax());
    loadfromfile();
    printf("内存数据库架构完成! \n");
    while (1)
    {
        char str[100] = { 0 };
        scanf("%s", str);
        searchStr(str);
    }
    system("pause");
}

posted on 2016-03-01 13:52  三少爷的剑123  阅读(212)  评论(0编辑  收藏  举报

导航