20160208.CCPP体系详解(0018天)
程序片段(01):main.c
内容概要:PointWithOutInit
#include <stdio.h>
#include <stdlib.h>
//01.野指针详解:
// 1.野指针:没有进行初始化操作的指针-->由于该指针变量内部所存储的地址是个随机值,因此是野地址(类型含义:指针)
// 注:指针类型的变量必须在其被创建的时候就需要进行初始化操作,否则就成了野指针,所谓野指针就是乱指向的指针,形成的就是一个随机垃圾地址
// 2.胡乱使用野指针所造成的现象:
// (1).指向驱动程序所属内存,将会出现蓝屏现象
// (2).指向普通的应用程序,会将本身的这个应用程序结束掉
int main01(void)
{
int * p;
printf("%d \n", *p);//
system("pause");
}
程序片段(02):01.地址.c+02.内容.c+03.指针实战.c+04.指针副本机制.c
内容概要:指针地址与取值运算符
///01.地址.c
#include <stdio.h>
#include <stdlib.h>
int get()
{
return 10;
}
//01.严格进行变量区分:
// 1.普通变量和指针变量
// 2.严格的变量类型:
// 常规类型+特殊类型
//02.对变量取地址的操作发起于寄存器当中
// 因此地址也生成于寄存器变量当中,C语言无法直接访问
//03.关于取地址符不能操作的两种情况:
// &&intNum;-->C语言无法直接操作寄存器变量
// &get();-->返回值存储与寄存器的(cache)区,或者程序的(备份内存),这两块儿内存都不可直接访问
int main01(void)
{
int intNum;
&intNum;//这里获取一个声明但没有初始化的普通变量的所属地址可以,但是如果是指针类型的变量就不行了
//&&intNum;//&intNum这个表达式所代表操作执行于寄存器当中,在寄存器当中使用寄存器变量进行存储,因此C语言无法进行寄存器变量的操作
// 而且&intNum所获得的数据来自于CPU-->寄存器-->代码区-->符号表中,因此不能通过取地址运算符进行操作(C语言相对于汇编语言的缺点)
//&get();//get();的返回值,也就是函数的返回值可能存储于两个常见位置:寄存器的缓冲区(cache),应用程序的备份内存,因此返回值不可取地址
system("pause");
}
///02.内容.c
#include <stdio.h>
#include <stdlib.h>
//01.取值运算符("*")与取地址运算符("&"):
// 1.星号("*")表示取值运算符,也就是根据指针访问内存块儿
// 2.与号("&")表示取地址运算符,也就是根据变量的内存实体获取变量的地址
//02.对指针变量使用取值运算符(星号:"*")的使用规律:
// 用原始指针类型的指针级别减去取地址运算符的个数;
// 就是当前所访问的具体变量(普通变量和指针变量)
// 结果为0-->普通变量
// 结果为1-->一级指针变量
// 结果为2-->二级指针变量
// 结果为n-->N级指针变量
int main02(void)
{
//*(001) = 1;//由于常量读取于代码区符号表,产生于CPU寄存器,因此C语言不能够直接进行访问
int intVar = 10;
*(&intVar) = 3;//星号(*)表示取值运算符,也就是根据指针访问内存区块儿
printf("%d \n", intVar);
system("pause");
}
//03.取值运算符(星号:"*")的操作对象实质必须是指针
// 注:*&intVar-->严格区分执行流程
int main03(void)
{
int intVar = 10;
*&intVar = 3;//*&这两个符号可以同时进行出现,操作流程为:取地址运算符(与号:"&")先进行取地址操作,再根据取值运算符(星号:"*")根据指针获取该指针所指向的内存实体|数据实体
//**&intVar;//编译报错-->由于*&intVar之后的结果为普通变量类型(int)-->取值运算符的操作对象必须是指针类型,也就是含有(星号:"*")的类型
printf("%d \n", intVar);
system("pause");
}
//04.凡是涉及到的操作和运算的步骤都是由CPU在寄存器当中进行执行的,这里的强制类型转换属于"读取"并"转换"的操作
// 因此属于寄存器操作,要进行间接修改数据,必须得先获取到相应的指针,而且指针类型必须是可以修改的,这样就有可能涉及到
// 指针变量的类型转换操作
//05.*(int *)&num这段儿代码的详细解释:
// 0.这些符号都是用于对变量名进行操作的
// 注:对变量进行的操作实质其实是对内存实体(数据实体)直接进行操作
// 1.取地址运算符(与号:"&")最接近于变量名称,所以优先执行
// 2.强制类型转换操作(int *)次接近于变量名称,所以其次执行
// 3.取值运算符(星号:"*")再次接近于变量名称,所以再其次执行
int main04(void)
{
const int num = 3;//在C语言中,Const关键字在处理普通变量的时候,只能够避免对变量的直接修改,但是避免不了间接修改(普通变量适用,指针变量稍有差别)
//num = 1;//直接修改,编译报错
(int)num;//对任何变量的强制类型转换操作,强制转换之后的数值产生于CPU的寄存器
*(int *)&num = 10;//int *类型转换,这里要特别注意,const int需要被看做为整体的一个类型进行处理
printf("%d \n", num);
system("pause");
}
//06.关于空类型的指针与空指针的区分:
// 空类型的指针:
// 严格说是空类型的指针变量-->该指针变量没有具体类型,只是用于存储地址(没有类型含义)而已
// 因此不能直接对空类型的指针变量进行数据实体的访问操作,没有明确解析步长和解析方式
// 空指针:
// 就是用于标识指针变量没有存储任何有实际意义的地址-->空置的指针变量而已
// 实质形式:NULL-->(void *)0
// 区分:空类型的指针是左值,空指针属于右值
int main05(void)
{
int intVar = 10;
intVar = 3;
void * pIntVar = &intVar;//空类型的指针多用于多线程的数据传送处理
//printf("%d \n", *p);//不可以直接操作空类型的指针
system("pause");
}
void change01(int * pNum)//副本机制,数组除外
{
*pNum = 10;
}
int main06(void)
{
int num = 20;
change01(&num);
printf("%d \n", num);
system("pause");
}
///03.指针实战.c
#include <stdio.h>
#include <stdlib.h>
//01.如何快速确定一个变量的所属类型?
// 变量名称对称法(直接看声明变量的代码块儿,进行直观对称得出)
int main07(void)
{
double db = 10.8;
double dbl = 10.9;
double * p = &db;
printf("%d \n", sizeof(p));//指针变量-->类型(double *)-->占用4个内存字节
printf("%d \n", sizeof(*p));//数据实体-->类型(double)-->占用8个内存字节
*p = 10.6;
printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 10.9 10.6
p = &dbl;
printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 10.9 10.9
*p = 9.2;
printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 9.2 9.2
dbl = 2.1;
printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 2.1 2.1
system("pause");
}
///04.指针副本机制.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//01.函数形式参数的副本机制:
// 1.在C语言当中,函数形式参数传参的副本机制只有数组例外,因为提升效率,节约内存拷贝
// 2.跨函数改变变量(普通变量|指针变量)的传参要素
// 被掉函数修改主调函数当中的普通变量,需要普通变量的地址
// 被掉函数修改主调函数当中的指针变量,需要指针变量的地址
// 注:总之在被掉函数当中修改主调函数的变量,无论是普通变量还是指针变量,必须传递变量的地址
// 否则绝对不行!!!
//void change02(int a, int b);//副本机制,被掉函数需要修改主调函数当中的变量,需要由主调函数向被掉函数传递该待修改变量的所属地址
void change02(int * pa, int * pb)
{
printf("change02:&pa = %p, &pb = %p \n", &pa, &pb);
//int * pTemp;//某些编译器会编译不通过!
int * pTemp = pa;
pa = pb;
pb = pTemp;
}
//02.区分指针变量和指针:
// 指针变量:含内存块儿
// 用于存储指针的变量
// 指针:不含有内存块儿
// 是一个具有类型含义的地址
// 是一个具有类型含义的数值
// 注:使用指针变量的实质就是使用指针!
int main08(void)
{
int a;
int b;
int * pa = &a, *pb = &b;
//scanf("%d%d", &a, &b);//scanf();函数后面所需的是"指针"
scanf("%d%d", pa, pb);
printf("main: &pa = %p, &pb = %p \n", &pa, &pb);
if (*pa > *pb)
{
change02(pa, pb);//间接修改无法成功
int temp = a;//直接修改
a = b;
b = temp;
}
printf("a = %d, b = %d \n", a, b);
system("pause");
}
int main09(void)
{
int a;
int b;
int * pa = &a, *pb = &b;
scanf("%d%d", pa, pb);
if (*pa > *pb)//对应指向的数据实体
{
int * pTemp = pa;//交换指针
pa = pb;
pb = pTemp;
}
printf("a = %d, b = %d \n", a, b);
printf("pa= %d, pb = %d \n", *pa, *pb);
system("pause");
}
void change03(int * p1, int * p2)
{
printf("change03: &p1 = %p, &p2 = %p \n", &p1, &p2);
int pTemp = *p1;
*p1 = *p2;
*p2 = pTemp;
}
void change04(int * p1, int * p2)
{
printf("change04: &p1 = %p, &p2 = %p \n", &p1, &p2);
//int * pTemp;//野指针:没有经过初始化的指针-->某些编译器直接编译报错
int * pTemp = NULL;//使用空指针来标识指针变量没有指向任何位置
*pTemp = *p1;//错误:访问了不该访问的地址0
*p1 = *p2;
*p2 = *pTemp;
}
int main10(void)
{
int a;
int b;
int * pa = &a, * pb = &b;
scanf("%d %d", pa, pb);
if (*pa > *pb)//对比指向的数据实体
{
//change03(pa, pb);
change02(*pa, *pb);//函数传递数据,传递的都只是副本而已
}
printf("a = %d, b = %d \n", a, b);
printf("*pa = %d, *pb= %d \n", *pa, *pb);
system("pause");
}
void change05(int * p1, int * p2)
{
printf("change05: &p1 = %p, &p2 = %p \n", &p1, &p2);
int * pTemp = p1;
p1 = p2;
p2 = pTemp;
}
//03.无论是指针变量的操作还是对指针的操作:
// 都必须明确其类型
int main11(void)
{
int a;
int b;
int * pa = &a, *pb = &b;
scanf("%d%d", pa, pb);
printf("main: &pa =%p, &pb = %p \n", &pa, &pb);
printf("main pa = %p, pb= %p", pa, pb);
system("pause");
}
程序片段(03):01.线程.c
内容概要:多线程参数传递
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
//01.空类型的指针-->空类型的指针变量:
// 只是标识该类型的变量具备存储地址的能力,严格区分地址与指针的不同
void run(void * p)//采用空类型的一级指针变量可以存储任何类型的地址:只是具备存储地址的能力
{
int * pTemp = p;//为该地址赋予实际的意义(地址-->指针)-->具备(解析步长+解析方式)的能力
char str[100] = { 0 };
sprintf(str, "锄禾日当午%d \n", *pTemp);
MessageBoxA(0, str, "天朝很苦!", 0);
}
//02.关于单线程和多线程的实现问题:
// 1.要看CPU到底有多少个逻辑内核?
// 逻辑内核只有一个无法实现多线程的
// 2.要看使用的的线程技术?
// 使用单线程技术无法实现多线程访问
// 使用多线程技术既可以实现单线程访问也可以实现多线程访问
//03.明确多线程情况下的一个应用程序的堆栈使用情况:
// 1.单进程情况之下的堆栈使用情况
// 2.各条线程共享单进程的堆内存
// 3.各条线程独享各自的栈内存
// 注:当主线程开启辅助辅助线程的时候!
// 主线程所占用的栈内存相对于其他辅助线程就是共享的堆内存!
int main01(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (int i = 0; i < 10; ++i)
{
//多线程同一时刻访问同一个变量的冲突问题(详解:多条线程全部开启,大家都读取到了变量i的地址,然后同一时刻访问了变量i的数据,因此相同)
// 此时的主线程数据相对于其它辅助线程的数据就如同堆内存当中的数据,各条线程共享该数据-->因此多线程访问冲突
HANDLE hd = _beginthread(run , 0 , &i);
WaitForSingleObject(hd, INFINITE);//无限等待,意思就是知道直到上一条工作线程执行完毕之后,再继续执行此行代码之后的代码
//_beginthread(run, 0, &intArr[i]);//多线程传递参数的方式-->此时每条线程当问的不是同一个数据,因此不会出现多线程访问冲突问题
}
system("pause");
}
程序片段(04):01.指针运算.c+02.数组.cpp+03.数组实战.c
内容概要:指针与数组
///01.指针运算.c
#include <stdio.h>
#include <stdlib.h>
//01.指针变量的运算符特点:
// 1.指针变量的运算只有在所属数组的情况下才具备实际意义
// 2.指针变量的运算与其所指向的数据实体的类型密切相关
// 3.没有明确类型的指针变量完全无法进行访问(读取|修改)
// 只是具备这存储地址这么一个唯一的功能
//02.指针变量的运算规律总结:
// 1.p+1<==>p+sizeof(*p)*1;
// 2.空类型的指针变量必须明确数据类型之后才能进行具体操作
// 否则只是具备容器的特点
int main01(void)
{
int intNum = 10;
int * pIntNum = &intNum;
//pIntNum+1;<>pIntNum+sizeof(*pIntNum)*1;
printf("%p, %p \n", pIntNum, pIntNum + 1);
char ch = "X";
char *pCh = &ch;
printf("%p, %p, %p \n", pCh, pCh + 1, pCh - 1);//指针变量的运算与类型密切相关
void * ppCh = &ch;
//pv+1;//没有明确指针类型的指针变量,不能进行指针变量的算术运算
system("pause");
}
int main02(void)
{
int intNum = 10;
int * pIntNum = &intNum;
printf("%p, %p, %p \n", pIntNum - 1, pIntNum, pIntNum + 1);
printf("%d, %d, %d \n", *(pIntNum - 1), *pIntNum, *(pIntNum + 1));
//如果指针变量所存储的指针所描述的地址不在数组当中,那么指针变量的加减运算法则没有任何意义
// 因为运算结果可以和数组索引相互挂钩
system("pause");
}
//02.关于指针变量的运算特点:
// 1.参与运算的指针变量所存储的指针必须指向同一个数组内存情况下
// 2.指针变量的运算范围:
// (1).指针变量既可以加上一个整数或者也可以减去一个整数(指针结果不要越过数组)
// (2).指着变量值可以减去一个指针变量,但不可以加上一个指针变量
// (3).指针变量既不可以执行乘法运算也不可执行除法运算,因为运算结果毫无实际意义!
int main03(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int * p1 = &intArr[0];
int * p2 = &intArr[6];
printf("%d \n", p2 - p1);//在同一个数组当中的两个指针变量(指针变量p1-指针变量p2<=>(指针1-指针2)/sizeof(*p1)<=>相差的元素个数)
//printf("%d \n", *(p1 -2));
system("pause");
}
///02.数组.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
//01.关于数组名和&数组名的内容详解:
// 1.数组名的本质:将所有数组看作为一维数组,那么数组名就是该一维数组当中首个数组元素的地址
// 常量指针!-->含有内存实体,是个伪常量,可以避免直接修改,但是无法避免间接修改
// 2.&数组名的本质:获取指向整个数组整体的指针类型
// 指针常量!-->没有内存实体,是个真常量,既不可以直接修改,也不可以间接修改
// 因此:无论是常量指针还是指针常量所对应的值都是指针(是具备类型含义的地址)
// 数组名+1<=>常量指针+1<=>常量指针的值+sizeof(*常量指针)
// &数组名+1<=>指针常量+1<=>指针常量的值+sizeof(*指针常量)
// 诀窍:对任何类型的变量(无论是真变量还是伪变量)进行取地址操作,都属于指针类型
// 也就统统占用4个字节的内存空间
int main04(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("intArr = %p, &intArr = %p \n", intArr, &intArr);
printf("%d, %d \n", intArr, intArr + 1);//数组的第一个元素的地址,长度为int类型的字节数
printf("%d ,%d \n", &intArr, &intArr + 1);//整个数组的类型,长度为整个数组的长度(4*10),这里说明取地址运算符在进行运算的时候同时获取了指针的地址和类型
printf("sizeof(intArr) = %lu, sizeof(&intArr) = %lu \n", sizeof(intArr), sizeof(&intArr));
printf("%s ,%s", typeid(intArr).name(), typeid(&intArr).name());
system("pause");
return 1;
}
///03.数组实战.c
#include <stdio.h>
#include <stdlib.h>
//01.函数的形式参数如果是数组名称,就取消了副本机制!
// 取消副本机制的原理-->将数组名称当做了指针变量的名称进行处理
// 该指针变量具备着实际参数数组的所有信息(包括类型信息)
// 注:这里的指针变量的类型是将所有数组类型看做为一维数组,那么
// 该一维数组当中的所有元素的类型就是该指针变量所指向的内存
// 实体的类型
void testFun(int * p)//借助指针变量访问外界数组
{
printf("testFun sizeof(p) = %d \n", sizeof(p));
for (int * px = p; px < p + 10; px++)
{
*px = 12;
printf("%d ,%p \n", *px, px);
}
}
//02.sizeof(对象)运算诀窍:
// sizeof(*&对象);<==>绝对正确的一个结果
int main05(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("main: sizof(intArr) = %d \n", sizeof(intArr));//sizeof(数组名)<=>sizeof(*&数组名);
testFun(intArr);
printf("原生数组: \n");
for (int * p = intArr; p < intArr + 10; ++p)
{
printf("%d ,%p \n", *p, p);
}
system("pause");
}
//03.对于数组的变量需要借助指针变量进行:
// 指针变量作为循环变量进行运算
int main06(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//int * pIntArr = &intArr;//类型不匹配:&intArr-->指向数组的指针
//intArr++;//数组名称intArr作为一个常量指针,是不能够直接进行自增自减运算的
for (int * p = intArr + 2; p < intArr + 10; ++p)
{
printf("%d, %p \n", *p, p);
}
for (int * p = intArr + 10; p > intArr - 1; --p)
{
printf("%d, %p \n", *p, p);
}
system("pause");
}
//04.sizeof();诀窍:
// sizeof(ob);<=>sizeof(*&obj);
int main07(void)
{
double * pDb;//在C语言当中,指针变量必须进行初始化,否则就会成为野指针,使用的时候容易出现问题,某些编译器不会通过的
printf("%d \n", sizeof(*pDb));//对称法则:double | * pDb-->8个字节
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("intArr = %p, &intArr = %p \n", intArr, &intArr);
//printf("sizeof(intArr) = %lu, sizeof(&intArr) = %lu \n", sizeof(intArr), sizeof(&intArr));
//printf("sizeof(*intArr) = %lu, sizeof(*&intArr) = %lu", sizeof(*intArr), sizeof(*&intArr));
printf("%d \n", sizeof(intArr));//sizoef();使用诀窍
system("pause");
}
程序片段(05):thread.c
内容概要:多线程切割
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <time.h>
#include <process.h>
typedef struct
{
int * pStart;//待检索首地址
int sLength;//待检索长度
int dFindValue;//待检索值
int tID;//当前检索线程ID
} thTask;
int isFind = 0;//线程检索成功标识,用于多线程之间的通信
//01.多线程检索原理:
// 1.将多个线程任务放置到线程队列当中去
// 2.然后调度多条线程执行线程队列当中的任务代码
// 3.线程随机调度:
// 那条线程最闲置,那么该线程最容易优先执行到
#define M 100//待检索的数据量
#define N 4//总共所使用的检索线程数目(最好是CPU逻辑内核的倍数,负载均衡原理)
//02.多线程检索数据最容易遇到的问题:
// 数据量并不是单条线程所处理数据量的整数倍?-->需要解决这个负载均衡问题
// 让N-1线程所处理的数据量一致,让最后一条线程处理量不一致
// 举例说明:
// 1.当线程处理数据量刚好平分的情况:
// 100%4=0
// 2.当线程处理数据量不好平分的情况:
// 100%7!=0
// 100/(7-1)=16...4-->前6条线程,每条的数据处理量是16条数据
// 100%(7-1)=4----->第7条线程的数据处理量为4条
void thSearchTask(void * pThTask)
{
thTask * thTaskBlock = pThTask;//地址->指针
printf("线程%d开始查找! \n", thTaskBlock->tID);
for (int * pf = (*thTaskBlock).pStart; pf < (*thTaskBlock).pStart + (*thTaskBlock).sLength; ++pf)
{
if (1 == isFind)
{
printf("线程%d结束查找,其他线程已经找到! \n", (*thTaskBlock).tID);
return;
}
if ((*thTaskBlock).dFindValue == *pf)
{
printf("结束查找状态!线程%d已经找到数据%d,地址为%p",(*thTaskBlock).tID, (*thTaskBlock).dFindValue, pf);
isFind = 1;
return;
}
Sleep(500);
}
printf("线程%d结束查找,没有找到! \n", (*thTaskBlock).tID);
}
int main01(void)
{
int intArr[M] = { 0 };
srand((unsigned int)time(NULL));
for (int i = 0; i < 100; ++i)
{
intArr[i] = rand() % 100 + 1;
printf("%4d", intArr[i]);
if (0 == (i + 1) % 100)
{
printf("\n");
}
}
int num = 0;
scanf("%d", &num);//待查找的数据
thTask thTaskArgS[N];//线程任务参数集
if (0 == M%N)
{
for (int i = 0; i < N; ++i)
{
thTaskArgS[i].pStart = intArr + M / N *i;
thTaskArgS[i].sLength = M / N;
thTaskArgS[i].tID = i;
thTaskArgS[i].dFindValue = num;
HANDLE hd = _beginthread(thSearchTask, 0, &thTaskArgS[i]);//开启线程
//WaitForSingleObject(hd, INFINITE);
}
}
else
{
for (int i = 0; i < N - 1; ++i)
{
thTaskArgS[i].pStart = intArr + M / (N - 1)*i;
thTaskArgS[i].sLength = M / (N - 1);
thTaskArgS[i].tID = i;
thTaskArgS[i].dFindValue = num;
HANDLE hd = _beginthread(thSearchTask, 0, &thTaskArgS[i]);
//WaitForSingleObject(hd, INFINITE);
}
int i = N - 1;
thTaskArgS[i].pStart = intArr + M / (N - 1)*i;
thTaskArgS[i].sLength = M % (N - 1);
thTaskArgS[i].tID = i;
thTaskArgS[i].dFindValue = num;
HANDLE hd = _beginthread(thSearchTask, 0, &thTaskArgS[i]);
}
system("pause");
}
程序片段(06):多线程检索.c
内容概要:多线程检索
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <time.h>
#include <process.h>
typedef struct
{
int tID;
int * pStart;
int sLength;
int sValue;
} thTaskInfo;
int isFind = 0;
void searchTaskFun(void * pTemp)
{
thTaskInfo * thTaskArg = pTemp;
printf("ID为%d的线程开始进行检索任务! \n", (*thTaskArg).tID);
for (int * pf = (*thTaskArg).pStart; pf < (*thTaskArg).pStart + (*thTaskArg).sLength; ++pf)
{
if (1 == isFind)
{
printf("ID为%d的线程结束检索状态,其他线程已经检索到%d! \n", (*thTaskArg).tID, (*thTaskArg).sValue);
return;
}
if ((*thTaskArg).sValue == *pf)
{
isFind = 1;
printf("ID为%d的线程已检索到数据%d,该值所在地址为%p \n", (*thTaskArg).tID, (*thTaskArg).sValue, pf);
return;
}
Sleep(500);
}
printf("ID为%d的线程未能检索到数据%d,结束检索状态! \n", (*thTaskArg).tID, (*thTaskArg).sValue);
}
int main01(void)
{
int intArr[100] = { 0 };
srand((unsigned int)time(NULL));
for (int i = 0; i < 100; ++i)
{
printf("%4d", intArr[i] = rand() % 100 + 1);
if (0 == (i + 1) % 10)
printf("\n");
}
int value = 0;
scanf("%d", &value);
thTaskInfo thTaskArgS[10];
for (int i = 0; i < 10; ++i)
{
thTaskArgS[i].tID = i;
thTaskArgS[i].pStart = intArr + 10 * i;
thTaskArgS[i].sLength = 10;
thTaskArgS[i].sValue = value;
HANDLE hd = _beginthread(searchTaskFun, 0, &thTaskArgS[i]);
}
system("pause");
}
程序片段(07):二级指针.c
内容概要:二级指针
#include <stdio.h>
#include <stdlib.h>
//01.指针变量规律:
// 1.指针变量必须注意指针的类型
// 2.指针变量必须注意指针所指向实体的类型
// 3.针对于指针变量的(加减一个整数)规律:
// 实质:必须根据指针变量所存储的指针所指向的实体类型来定断
// 形式:p+1<=>p+sizeof(*p);-->必需带上内存实体才能准确!
// 4.指着变量的级数规律:
// (1).如何表明当前指针变量存储的是什么类型的变量的地址?
// double **pp-->二级指针变量PP存储的是(double * p)这个一级指针变量的地址
// 规律:指针变量的声明级数减去一级就是该指针变量所存储的变量的地址
// (2).如何确定取值运算符(星号:"*")当前所访问的内存实体是什么?
// double **pp-->**pp-->(指针变量声明级数-当前取值运算符操作的级数)
// 就是该取值运算符所访问的变量的数据
// 例如:**-**=0-->变量的数据
int main01(void)
{
double db = 10.8;
double * p = &db;
//double * pT = &p;//指针类型不匹配
double ** pp = &p;
//printf("%p, %p \n", pT, pT + 1);//PT结果不正确
printf("%p, %p \n", pp, pp + 1);
printf("%lf \n", **pp);//普通变量
system("pause");
}
int a = 10;
int b = 20;
//02.函数形参的副本机制要点:
// 只是针对于数组类型没有副本机制,其他所有类型的形参都有副本机制
// 原理:就是将数组类型进行退化为指针("指向数组元素的指针"),以避免副本机制产生
// 注:指向数组元素的指针,明确该数组当中所存储的到底是什么类型的数据!
void changeP(int * p)//指针变量也有副本机制
{
printf("change: &p = %p \n", &p);
p = &b;
}
void changePP(int ** pp)
{
*pp = &b;
}
//03.要想让被掉函数修改主调函数当中的变量数据:
// 诀窍:至少必须传递主调函数当中的变量地址(至少得是直接地址)
int main02(void)
{
int * p = &a;
printf("main: &p = %p \n", &p);
printf("%d \n", *p);//10
//p = &b;
//changeP(p);
changePP(&p);
printf("%d \n", *p);
system("pause");
}
//04.集团体系架构关系:
// 职称体系可以通过指针级数进行描述
// 例如:司令采用的是10级指针描述,里面存储的军长的地址
int main03(void)
{
int a = 10;
int * p = &a;
int ** pp = &p;
int *** ppp = &pp;
printf("%p \n", &ppp);
//集团体系架构关系可以通过指针级数进行描述
system("pause");
}
//05.字符类型的指针如果指向的是代码区符号表实体:
// 那是不允许通过字符指针间接修改代码区符号表内容的!
// 注:代码区符号表的内容只能够进行读取操作,不能进行写入操作
int main04(void)
{
char * p = "123";//"123"存储于代码区符号表
*p = 'A';//*p所访问的内存实体位于代码区符号表,因此不能直接进行修改
printf("%p \n", p);
system("pause");
}
void run(char * arr[5])
{
printf("run: sizeof(arr) = %d \n", sizeof(arr));//sizeof(char **)
for (int i = 0; i < 5; ++i)
{
system(arr[i]);
}
}
//06.数组作为形参究竟退化为什么类型的指针?
// 实质:退化为指向该数组当中所有元素的指针!
//int arr[5]-->arr作形参,退化为-->int * arr
//char * arr[5]-->arr作形参,退化为-->char ** arr
void runT(char **arr)//指针数组作为形参,将会退化成为二级指针
{
printf("runT: sizeof(arr) = %d \n", sizeof(arr));
for (int i = 0; i < 5; ++i)
{
system(arr[i]);//arr[i]的实质其实是一级指针
}
}
//07.数组作为形参的时候,会具体转化为指向该数组当中每个元素的指针类型:
// 在访问的时候,采取该指针类型变量名加上中括号("[]")等同于数组访问方式!
// 实质:arr[i]<=>*(arr+i)-->效果等价
int main05(void)
{
//int arr[10];
char * arr[5] = { "calc", "notepad", "mspaint", "tasklist", "pause" };
printf("main: sizeof(arr) = %d \n", sizeof(arr));//诀窍:sizeof(varName)<=>sizeof(*&varName)
run(arr);
//runT(arr);
system("pause");
}
程序片段(08):数组.c
内容概要:指针结合数组
#include <stdio.h>
#include <stdlib.h>
//01.几组概念区分:
// 1.普通变量和指针变量:
// 是否具有地址意义
// 2.变量指针和常量指针
// 指针变量和数组名的区分
// 3.常量指针和指针常量
// 数组名和地址意义上的数值
// 注:通常意义上的指针实质上是指着变量
//02.函数形参使用数组声明形式以及指针声明形式
// 其访问实参的方式都是相同的(数组形式<=>指针形式)
int main01(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int * p = arr;//变量指针接收常量指针的值
for (int i = 0; i < 10; ++i)
{
printf("%d, %d, %d, %d \n", arr[i], *(arr + i), p[i], *(p + i));
}
//接收同样一个数组实参
// 采用数组声明形式和采用指针声明形式的访问方式完全一样
// 例如:arr[i]<=>p[i];*(p+i)<=>*(arr+i)
system("pause");
}
//03.数组作为形参的函数标准声明形式!
// 指向数组元素的指针+数组元素个数
void revArr(int * p, int n)
{
for (int i = 0; i < n / 2; ++i)//节省效率的反转方式
{
p[i] = p[i] ^ p[n - 1 - i];
p[n - 1 - i] = p[i] ^ p[n - 1 - i];
p[i] = p[i] ^ p[n - 1 - i];
}
}
//04.通过指针方式来对数组进行遍历!
void revArrT(int * p, int n)
{
for (int * pHead = p, *pTail = p + n - 1; pHead < pTail; ++pHead, --pTail)
{//奇数:重合退出+偶数:交叉退出
*pHead = *pHead ^ *pTail;
*pTail = *pHead ^ *pTail;
*pHead = *pHead ^ *pTail;
}
}
//05.传递实质是什么就可以以何种形式进行数据访问!
// 这里传递的实质是数组,因此可以采用数组方式访问
void showArr(int * p, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%2d", p[i]);
}
}
int main02(void)
{
int arrA[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };//10/2=5..0--><=5-->0~4(前0~4与后0~4)对称
int arrB[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//9/2=4..1--><=4-->0~3(前0~3与后0~4)对称
revArr(arrA, sizeof(arrA) / sizeof(arrA[0]));
showArr(arrA, sizeof(arrA) / sizeof(arrA[0]));
putchar('\n');
revArrT(arrB, sizeof(arrB) / sizeof(arrB[0]));
showArr(arrB, sizeof(arrB) / sizeof(arrB[0]));
system("pause");
}
//06.特殊常量指针访问数组方式:
// 原理:arr[i]<=>*(arr+i)
// 因此:i[arr]<=>*(i+arr)
// 注:所有数组的解析方式都是按照arr[i]-->*(arr+i)-->
// 所有荒唐用法i[arr]也可以进行替换为-->*(i+arr)
int main03(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
printf("% \n", 8[arr]);
//arr[1]<==>*(arr+1)
system("pause");
}
程序片段(09):queue.h&queue.c+01.多线程.c+02.多线程排队.c
内容概要:多线程并行计算
///queue.h
#define EN 100
typedef struct
{
int data[EN];
int head;
int tail;
} Queue;
void initQueue(Queue * pQueue);
int isEmpty(Queue * pQueue);
int isFull(Queue * pQueue);
void enQueue(Queue * pQueue, int value);
void deQueue(Queue * pQueue);
int getLast(Queue * pQueue);
void showQueue(Queue * pQueue);
//#undef N//结束宏名为N的作用域
///queue.c
#include "queue.h"
#include <memory.h>
#include <stdio.h>
void initQueue(Queue * pQueue)
{
memset((*pQueue).data, 0, sizeof(int)*EN);//数据清零,模拟回收
(*pQueue).head = (*pQueue).tail = -1;
}
int isEmpty(Queue * pQueue)
{
if ((*pQueue).head == (*pQueue).tail)
return 1;
return 0;
}
int isFull(Queue * pQueue)
{
if (EN - 1 == (*pQueue).tail)
return 1;
return 0;
}
void enQueue(Queue * pQueue, int value)
{
if (1 == isFull(pQueue))
return;
if (1 == isEmpty(pQueue))
{
(*pQueue).data[0] = value;
(*pQueue).tail = 0;
}
else
{
for (int i = (*pQueue).tail; i > 0; --i)
{
(*pQueue).data[i] = (*pQueue).data[i - 1];
}
(*pQueue).data[0] = value;
++((*pQueue).tail);
}
}
void deQueue(Queue * pQueue)
{
if (isEmpty(pQueue))
return;
--((*pQueue).tail);
}
int getLast(Queue * pQueue)
{
return (*pQueue).data[((*pQueue).tail)--];
}
void showQueue(Queue * pQueue)
{
for (int i = 0; i <= (*pQueue).tail; ++i)
{
printf("%2d", (*pQueue).data[i]);
}
printf("\n");
}
///01.多线程.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <process.h>
#define N 1024
int main01(void)
{
srand((unsigned int)time(NULL));
int data[N] = { 0 };
for (int i = 0; i < N; ++i)
{
data[i] = rand() % 1000 + 1;
}
int sum = 0;
for (int i = 0; i < N; ++i)
{
sum += data[i];
}
printf("单线程串行结果为%d! \n", sum);
system("pause");
}
typedef struct
{
int tID;
int * pStart;
int sLength;
int cSum;
} thTaskInfo;
int main02(void)
{
thTaskInfo thTask;
thTaskInfo * pThTask = &thTask;
printf("%d \n", (*pThTask).sLength = 100);
//结构体访问的等价关系!
// (*thTask).sLength <=>thTask->sLength
printf("%d \n", (*pThTask).sLength);
system("pause");
}
void thTaskFun(void * p)
{
thTaskInfo * thTask = p;
for (int i = 0; i < (*thTask).sLength; ++i)
{
(*thTask).cSum += (*thTask).pStart[i];
}
printf("ID为%d的线程计算结果为%d! \n", (*thTask).tID, (*thTask).cSum);
}
int main03(void)
{
srand((unsigned int)time(NULL));
int data[N] = { 0 };
for (int i = 0; i < N; ++i)
{
data[i] = rand() % 1000 + 1;
}
int sum = 0;
for (int i = 0; i < N; ++i)
{
sum += data[i];
}
printf("单线程串行计算结果为%d \n", sum);
thTaskInfo thTaskArgs[4] = { 0 };
for (int i = 0; i < 4; ++i)
{
thTaskArgs[i].tID = i;
thTaskArgs[i].pStart = data + (N / 4) * i;
thTaskArgs[i].sLength = N / 4;
thTaskArgs[i].cSum = 0;
_beginthread(thTaskFun, 0, &thTaskArgs[i]);
}
system("pause");//等待所有并行计算线程完成计算操作
int lastNum = 0;
for (int i = 0; i < 4; ++i)
{
lastNum += thTaskArgs[i].cSum;
}
printf("多线程并行计算结果为%d! \n", lastNum);
system("pause");
}
///02.多线程排队.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "queue.h"
#include <process.h>
#define N 1024
typedef struct
{
int tID;
int * pStart;
int sLength;
int cSum;
} thTaskInfo;
Queue thQueue;//管理线程的队列,全局变量,所有线程共享
//01.C语言当中的static关键字使用:
// static关键字用于限定函数或者变量只能作用域当前文件!
static void thTaskFun(void * p)
{
thTaskInfo * thTask = p;
for (int i = 0; i < (*thTask).sLength; ++i)
{
(*thTask).cSum += (*thTask).pStart[i];
}
enQueue(&thQueue, (*thTask).tID);//线程ID进队,便于顺序打印结果
printf("ID为%d的线程计算结果为%d! \n", (*thTask).tID, (*thTask).cSum);
}
int main(void)
{
srand((unsigned int)time(NULL));
int data[N] = { 0 };
for (int i = 0; i < N; ++i)
{
data[i] = rand() % 1000 + 1;
}
int sum = 0;
for (int i = 0; i < N; ++i)
{
sum += data[i];
}
printf("单线程串行计算的结果为%d! \n", sum);
initQueue(&thQueue);
thTaskInfo thTaskArgs[4] = { 0 };
for (int i = 0; i < 4; ++i)
{
thTaskArgs[i].tID = i;
thTaskArgs[i].pStart = data + (N / 4) *i;
thTaskArgs[i].sLength = N / 4;
thTaskArgs[i].cSum = 0;
_beginthread(thTaskFun, 0, &thTaskArgs[i]);
}
system("pause");//让主线程等待所有辅助线程完成并行计算
showQueue(&thQueue);
int lastNum = 0;
for (int i = 0; i < 4; ++i)
{
lastNum += thTaskArgs[i].cSum;
}
printf("多线程并行运算的结果为%d! \n", lastNum);
system("pause");
}
程序片段(10):main.c
内容概要:TestC
#include <stdio.h>
#include <stdlib.h>
//01.格式字符串规律:
// 格式字符串当中的两个%等同于一个非输入格式控制符
// 注:严格区分格式控制符与非格式控制符以及转义字符
int main01(void)
{
int a = 2, b = 5;
printf("a = %%d, b = %%d \n", a, b);
system("pause");
}
程序片段(11):test.c
内容概要:Test
#include <stdio.h>
#include <stdlib.h>
//01.自变运算符的特殊性:
// 相同的自变运算符如果在表达式当中同时出现;
// 那就需要进行同时整体取值运算操作!
// 例如:
// 出现几个(i++),就同一时间提出几个(i++)的值
// 但是提取的是第一个(i++)的值
// 出现几个(++I),就同一时间提出几个(++i)的值
// 但是提取的是最后一个(++i)的值
//02.溢出规律特点:
// 原来99块钱,两位数
// 99
// 加上1块钱,三位数
// 100
// 但是,只能有两位数
// 所以剩下两位是00
int main01(void)
{
float x = 1, y;
y = ++x*++x;
printf("%f \n", y);
float i = 1, j;
j = i+++i++;
printf("%f \n" ,j);
unsigned short num = 65535 + 1;
printf("%d \n", num);
int a, b;
float c;
//c = a = b;
system("pause");
}