20160212.CCPP体系详解(0022天)
Posted 尹成
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>
以上是关于20160212.CCPP体系详解(0022天)的主要内容,如果未能解决你的问题,请参考以下文章