C++封装随笔
1接口的封装和设计思想入门
接口的封装和设计思想入门 |
第一套api函数 |
#ifndef _CLT_SOCKET_H__ #define _CLT_SOCKET_H__
//客户端初始化环境 int cltSocket_init(void **handle); //5day
//客户端发报文 int cltSocket_senddata(void *handle, unsigned char *buf, int buflen);
//客户端收报文 int cltSocket_resvdata(void *hanle , unsigned char *buf, int *buflen);
//4 客户端销毁环境 int cltSocket_destory(void *handle);
#endif
|
第二套api函数 |
//第二套api函数 #ifndef _CLT_SOCKET_H__ #define _CLT_SOCKET_H__
//客户端初始化环境 int cltSocket_init2(void **handle); //5day
//客户端发报文 int cltSocket_senddata2(void *handle, unsigned char *buf, int buflen);
//客户端收报文 int cltSocket_resvdata2(void *hanle , unsigned char **buf, int *buflen); //为什么这个地方换成了一个二级指针,而且又增加了一个接口 4day int cltSocket_resvdata_Free2(unsigned char *buf);
//4 客户端销毁环境 //为什么这个地方又加了一个* 4day int cltSocket_destory2(void **handle);
#endif |
我们找到了一套标准,我们可以高效的学习,更重要的是,我们能务实的,集中话题学习这套api函数 |
Socket动态库业务模型思路分析 |
3学习标准热身
排序 |
选择法排序思想总结 |
核心排序代码 |
printfArray3(a);
for(i=0; i<10; i++) { for (j=i+1;j<10; j++) { if (a[i] < a[j]) { tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } }
printf("\n排?序¨°之?后¨®:");
|
结论 //1数组做函数参数,会退化为指针 //2在形参里面出现的char buf[30] int a[10] c/c++编译器会把它当做指针,也不会主动的多分配内存,c、c++编译器会自动优化 // int i = 0; int num1 = sizeof(a); int num2 = sizeof(*a); int num = sizeof(a)/sizeof(*a); int num1 = sizeof(a); //数据类型不一样 //3 sizeof(a)大小不一样的实质是a的数据类型不一样 |
|
4数据类型本质剖析
数据类型问题抛出 |
压死初学者的三座大山 1、数组类型 2、数组指针 3、数组类型和数组指针的关系 |
void main() { int a[10] = {1, 3, 44, 2, 3, 44, 5, 5,6, 67}; printf(“a:%d &a:%d \n”, a, &a); //a &a大小一样 printf(“a+1:%d &a+1:%d \n”, a+1, &a +1 ); //+1 大小不一样 //a &a数据类型不一样 步长不一样 //压死出血 system(“pause”); } |
数据类型分为两种,简单、一个复杂,思考复杂数据类型的时候,不能用简单数据类型思考之。。。。。抛砖 |
//写一个函数 把内存地址给传出被调用函数,方法有2种 |
|
结论: 数据类型本质:固定大小内存的别名 数据类型取别名 typdedef 抛出问题: 如何表达:int array[10] int add(int a, int d);//15 |
|
5变量的本质
修改变量的方法 两种+引用 |
变量的本质:连续内存块的别名,变量是一个标号。再抛砖 程序的内存四区图,画变量示意图时,主要把变量放在外面,内存空间留出来 |
内存空间可以再取给别名吗? |
|
6内存四区概念
基本概念 |
函数1调用函数2,函数1称为主调函数 函数2称为被调用函数 |
规则1:Main(主调函数)分配的内存(在堆区,栈区、全局区)都可以在被调用函数里使用吧。 |
规则2:在被调用函数里面分配的内存 1、如果在被调用函数里面的临时区(栈)分配内存,主调用函数是不能使用的。 |
char * getstring3() { char buf[30]; strcpy(buf, "abcde"); return buf; } |
|
|
|
|
铁律1
铁律1:指针是一种数据类型 1) 指针也是一种变量,占有内存空间,用来保存内存地址 测试指针变量占有内存空间大小 2)*p操作内存 在指针声明时,*号表示所声明的变量为指针 在指针使用时,*号表示 操作 指针所指向的内存空间中的值 *p相当于通过地址(p变量的值)找到一块内存;然后操作内存 *p放在等号的左边赋值(给内存赋值) *p放在等号的右边取值(从内存获取值) 3)指针变量和它指向的内存块是两个不同的概念 //含义1 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容;p = p +1; //p++ //含义2 给*p赋值*p='a'; 不会改变指针变量的值,只会改变所指的内存块的值 //含义3 =左边*p 表示 给内存赋值, =右边*p 表示取值 含义不同切结! //含义4 =左边char *p //含义5 保证所指的内存块能修改 4)指针是一种数据类型,是指它指向的内存空间的数据类型 含义1:指针步长(p++),根据所致内存空间的数据类型来确定 p++=è(unsigned char )p+sizeof(a); 结论:指针的步长,根据所指内存空间类型来定。
|
|
|
|
不断地修改指针变量的值含义场景建立
//不断地修改指针变量的值含义 //需要建立场景,解决指针乱指问题 void main() { int i = 10; char buf[20]; char *p = NULL;
strcpy(buf, "a234567899987654");
p = &buf[0]; p = &buf[1]; p = &buf[2]; p = &buf[3]; p = &buf[4];
for (i=0; i<10; i++) { p = &buf[i]; } } |
|
2经验话语
#ifndef _CLT_SOCKET2_H__ #define _CLT_SOCKET2_H__ #endif
// 避免在.c里面 重复包含多次头文件 #include "cltsocket.h" #include "cltsocket.h" #include "cltsocket.h"
|
Shift+del建组合删除一行 |
Ctlr+u 让单词从小写变大写 Shift+ctrl+u 从大小变小写 |
|
//避免多次重复包含 整个思路分析 |
C语言中的灰色地带这种问题,往后放 |
|
有关字面量
有关字面量的理解 |
{ //10 字面量 放在不能取地址 没有放在堆栈、全局区,可以按照放在代码区之类的区域内理解它。 int *a = &10; } |
怎么样理解(多级)指针做函数参数,
//对参数的指针类型应该怎么理解 |
//理解角度需要从两个角度出发 //第一个角度:站在c/c++编译器的角度 对形参,如果是指针类型,c编译器只会把它当作一个指针变量来看。(配四个自己的内存)。 ////char *p7 形参 是变量 //第二个角度:我们只有在使用指针所指向的内存空间的时候,我们才去关心内存是一维的,还是二维的。 |
/* void senddata01(char *p1); void senddata01(char* p1); void senddata02(char ** p1); void senddata02(char * *p1); void senddata02(char **p1); void senddata03(char ***p1); void senddata04(char *p[]); void senddata04(char * p[]); void senddata04(char *p []); void senddata05(char (*p)[10]); void senddata05(char (*p) [10]); void senddata05(char *****p4); */
|
内存块数据打包
//(unsigned char *buf +len)内存块的数据打包
有关[] *
//buf[i]-->buf[0+i];--->*(p+i) ---> p[i] //站在c++编译器的角度, *p 相当于我们程序员手工(显示)利用间接赋值,去操作内存 //[]怎么理解,只不过是c++编译器帮我们程序员做了一个*p的操作
|
char buf[100] = "abcdefg"; char *p = NULL;
for (i=0; i<strlen(buf); i++) { printf(" %c", p[i]); }
printf("\n"); for (i=0; i<strlen(buf); i++) { printf(" %c", *(p+i)); } |
|
间接赋值成立的是三个条件
间接赋值成立的是三个条件 |
/* 间接赋值成立的三个条件 条件1:定义了一个变量(实参)定义了一个变量(形参) 条件2:建立关联,//实参取地址传给形参 条件3://*p形参,去间接的修改实参的值 main --->func */ |
//间接赋值成立的三个条件,应用起来。。。。 |
//123 写在一个函数里面,那么成了第一个应用场景 //12 3 //间接赋值是指针存在的最大意义 //1 23 //抛砖,,,,到时候,要认识啊。 |
//间接赋值的应用场景有2个 |
//场景1:在函数指针 *p1++ = *p2++ //场景2:指针做函数参数,通过*p形参求间接的修改实参的值,这才是指针存在的最大意义、。 //这才是C语言特有的现象,才是指针的精华 |
//*p间接赋值是指针存在的最大意义(现实意义) |
接口的封装和设计 //函数调用时,形参传给实参,用实参取地址,传给形参,在被调用函数里面用*p, //来改变实参,把运算结果传出来。 //指针作为函数参数的精髓。 |
推论 |
//指针做函数参数 //函数调用过程中, //用1级指针(通常是形参,)去修改0级指针(通常是实参)的值 //用2级指针(通常是形参,)去修改1级指针(通常是实参)的值 //用3级指针(通常是形参,)去修改2级指针(通常是实参)的值 //用8级指针(通常是形参,)去修改7级指针(通常是实参)的值 //用n级指针(通常是形参,)去修改n-1级指针(通常是实参)的值 |
/* 整个C/C++领域值得我们做技术推演的领域 0--1 1->2 2->3
c++多态(函数指针做函数参数)
Aop切面编程(2-3) */ |
03字符串专题讲座
内存模型
//1在c中没有字符串这种类型,是通过字符串数组(char buf[100])去模拟 //2 字符串和字符串数组的区别 是不是 带有\0 |
//print函数是c库函数,它就是按照C风格字符串进行输出数据
//通过字符串常量初始化字符串数组 //通过这种方法它会自动给你\0 char buf4[] = "abcdefg"; printf("%s\n", buf4);
|
// //strlen() 是一个函数 求字符串的长度(不是求字符数组的长度),它的长度不包括\0 //sizeof() 是一个操作符,求数据类型(实体)的大小 |
int main13() { char buf[20]="aaaa"; int a = 10; //字面量
char buf2[] = "bbbb"; //char ***********************************************p1 = "111111"; char *p1 = "111111";
char *p2 = malloc(100); strcpy(p2, "3333"); //&a; //&a表达式 表达式的运算结果放在寄存器里 } |
//buf[i]-->buf[0+i];--->*(p+i) ---> p[i] //站在c++编译器的角度, *p 相当于我们程序员手工(显示)利用间接赋值,去操作内存 //[]怎么理解,只不过是c++编译器帮我们程序员做了一个*p的操作 |
//因为后缀++的优先级,高于,*p; void copy_str4(char *from , char *to) { while((*to ++ = *from++) != '\0') { ; } } |
|
|
重要概念概念
在C语言中使用字符数组来模拟字符串 C语言中的字符串是以’\0’结束的字符数组 C语言中的字符串可以分配于栈空间,堆空间或者只读存储区 |
03 字符串操作 数组法,下标法 字符数组名,是个指针,是个常量指针; 字符数组名,代表字符数组首元素的地址,不代表整个数组的。 如果代表这个数组,那需要数组数据类型的知识!
|
字符串做函数参数
void copy_str01(char *from, char *to) { for (; *from!='\0'; from++, to++) { *to = *from; } *to = '\0'; }
void copy_str02(char *from, char *to) { while(*from!='\0') { *to++ = *from++; } *to = '\0'; }
void copy_str03(char *from, char *to) { while( (*to=*from) !='\0') { to++; from++; } }
void copy_str04(char *from, char *to) { while( (*to++=*from++) !='\0') { ; } }
int copy_str05_good(const char *from, char *to) { if (from==NULL || to==NULL) { printf("func copy_str05_good() err. (from==NULL || to==NULL)\n"); return -1; }
while( (*to++=*from++) !='\0') { ; } return 0; } |
|
字符串操作典型错误
建立一个思想:是主调函数分配内存,还是被调用函数分配内存; |
越界 语法级别的越界 |
char buf[3] = "abc"; |
|
越界
越界 语法级别的越界 |
char buf[3] = "abc"; |
|
不断修改内存指针变量
越界 |
void copy_str_err(char *from, char *to) { char *fromtmp = from; for (; *from!='\0'; from++, to++) { *to = *from; } *to = '\0'; printf("to:%s", to); printf("from:%s", from); }
|
|
4、你向外面传递什么
1、临时str3内存空间 |
// char *str_cnct(x,y) /*简化算法*/ // char *x,*y; char *str_cnct(char *x, char* y) /*简化算法*/ { char str3[80]; char *z=str3; /*指针z指向数组str3*/ while(*z++=*x++); z--; /*去掉串尾结束标志*/ while(*z++=*y++); z=str3; /*将str3地址赋给指针变量z*/ return(z); } 2、经验要学习 while(*z++=*x++); z--; /*去掉串尾结束标志*/
|
char *str_cnct(char *x, char* y) /*简化算法*/ { char * str3= (char *)malloc(80) char *z=str3; /*指针z指向数组str3*/ while(*z++=*x++); z--; /*去掉串尾结束标志*/ while(*z++=*y++); z=str3; /*将str3地址赋给指针变量z*/ return(z); }
|
char *str_cnct(char *x, char* y) /*简化算法*/ { If (x == NULL) { Return NULL; } char * str3= (char *)malloc(80) char *z=str3; /*指针z指向数组str3*/ while(*z++=*x++); z--; /*去掉串尾结束标志*/ while(*z++=*y++); z=str3; /*将str3地址赋给指针变量z*/ note: return(z);
} Main () { Char *p = str_cnct(“abcd”, “ddeee”); If (p != NULL) {Free(p) ;p = NULL}//yezhizhen } |
int getKeyByValude(char *keyvaluebuf, char *keybuf, char *valuebuf, int * valuebuflen) { int result = 0; char *getbuf = new char[100]; memset(getbuf, 0, sizeof(getbuf));
char *trimbuf = new char[100]; memset(trimbuf, 0, sizeof(trimbuf));
int destlen = strlen(keyvaluebuf);
if (keybuf == NULL || keyvaluebuf == NULL || valuebuf == NULL/* || valuebuflen == NULL*/) { result = -1; return result; }
if (strstr(keyvaluebuf, keybuf) == NULL) { result = -1; return result; } else { for (int i = 0; i < destlen; i++) { if (*keyvaluebuf == '=') { *keyvaluebuf++; break; } keyvaluebuf++; } while(*keyvaluebuf != '\0') { *valuebuf = *keyvaluebuf; valuebuf++; keyvaluebuf++; } *valuebuf = '\0'; }
int len = strlen(valuebuf); return result; } |
字符串项目开发模型
strstr+while语句进行符合条件字符串查找
demo04_两头堵
demo05_字符串反转
const专题讲座
1、 const基础知识(用法、含义、好处、扩展)
int main() { const int a; // int const b;
const int *c; int * const d; const int const *e ;
return 0; }
Int func1(const ) 初级理解:const是定义常量==》const意味着只读 |
含义: //第一个第二个意思一样 代表一个常整形数 //第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改) //第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改) //第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改) |
Const好处 //合理的利用const, //1指针做函数参数,可以有效的提高代码可读性,减少bug; //2清楚的分清参数的输入和输出特性 |
结论: //指针变量和它所指向的内存空间变量,是两个不同的概念。。。。。。 //看const 是放在*的左边还是右边 看const是修饰指针变量,还是修饰所指向的内存空变量 |
04二级指针输入模型
二级指针内存模型图
|
数组名
1数组名
//二维数组也是线性排列的 |
void printArray(int *a, int size) { int i = 0;
printf("printArray: %d\n", sizeof(a));
for(i=0; i<size; i++) { printf("%d\n", a[i]); } }
int main() { int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; char cc[10][30];
int* p = &a[0][0];
printf("sizeof(&a):%d \n", sizeof(&a)); printf("sizeof(a):%d \n", sizeof(a)); printf("sizeof(*a):%d \n", sizeof(*a));
printf("sizeof(&cc):%d \n", sizeof(&cc)); printf("sizeof(cc):%d \n", sizeof(cc)); printf("sizeof(*cc):%d \n", sizeof(*cc));
//printArray(p, 6);
getchar();
return 0; } |
2、本质分析 //int a[5] 一维数组名代表数组首元素的地址 //int a[5] ===> a的类型为int*
//二维数组名同样代表数组首元素的地址 //int b[2][5]===>b的类型为int(*)[5]
//测试如何测试:指针也是一种数据类型,它的数据类型是指它所执行的内存空间的数据类型 //如何测试b的步长? //推导。。。。。。。
|
//结论:二维数组名 char cc[10][30] 是一个数组指针,char (*)[30] |
|
2多维数组做函数参数退化
1、 C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参) |
int fun(char a[20], size_t b) |
原因1:高效 |
原因2: |
|
2、二维数组参数同样存在退化的问题 |
二维数组可以看做是一维数组 二维数组中的每个元素是一维数组 二维数组参数中第一维的参数可以省略 void f(int a[5]) ====》void f(int a[]); ===》 void f(int* a); void g(int a[3][3])====》 void g(int a[][3]); ====》 void g(int (*a)[3]); |
3、等价关系 |
数组参数 等效的指针参数
一维数组 char a[30] 指针 char* 指针数组 char *a[30] 指针的指针 char **a 二维数组 char a[10][30] 数组的指针 char(*a)[30] |
char * a[30] char(*a)[30] char(*a)(30) 怎么区分:指针数组、数组指针 |