Table of Contents
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函数
#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);
//为什么这个地方换成了一个二级指针,而且又增加了一个接口
int cltSocket_resvdata_Free2(unsigned char *buf);
//4 客户端销毁环境
//为什么这个地方又加了一个* 4day
int cltSocket_destory2(void **handle);
#endif
Socket动态库业务模型思路分析
2、标准热身
排序思想:
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void PrintfArray(int *a)
{
for (int i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
}
//1数组名做函数参数,会退化为指针
void PrintfArray02(int *a,int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
}
//2 在形参里面出现的char buf[30],int a[20], c/c++编译器,会把它当成指针,也不会主动地多分配内存
//day2后
void PrintfArray03(int a[10])
{
int len1 = sizeof(a);
int len2= sizeof(*a);
int len = sizeof(a) / sizeof(*a);
printf("len1=%d,len2=%d,len=%d,", len1, len2, len);
for (int i = 0; i < len; i++)
{
printf("%d ", a[i]);
}
}
void main()
{
int a[10] = { 16, 21, 42, 26, 71, 19, 24, 34, 21, 54 };
int len1 = sizeof(a);//数据类型不一样
printf("排序前 %d:\n",len1);
//for (int i = 0; i < 10; i++)
//{
// printf("%d ", a[i]);
//}
PrintfArray03(a);
for (int i = 0; i < 10; i++)
{
for (int j = i + 1; j < 10; j++)
{
if (a[i] < a[j])
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
printf("\n排序后:\n");
//for (int i = 0; i < 10; i++)
//{
// printf("%d ", a[i]);
//}
PrintfArray03(a);
system("pause");
}
结论:
//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的数据类型不一样
3、 数据类型本质剖析
数据类型问题抛出,压死初学者的三座大山
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种
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//typedef 对数据类型重新取个别名
typedef int u32;
//C 语言的灰色地带(C语言可以定义全局变量,但是JAVA语言不可以)
//int g_add;
//int g_add=10;
void main02()
{
printf("%d \n", sizeof(u32));
system("pause");
}
void main01()
{
int b = 10;
int a[10] = { 16, 21, 42, 26, 71, 19, 24, 34, 21, 54 };
printf("a:%d,&a=%d\n", a, &a);//a和&a一样
printf("a+1:%d,&a+1=%d", a+1, &a+1);//+1大小不一样
//a和&a数据类型不一样,步长不一样
system("pause");
}
//写一个函数,把内存地址传出给被调用函数,方法有两种
//void *getTeacher()
//{
// void *p = malloc(99);
// return p;
//}
void main03()
{
int a = 10;
int *p;
p = &a;
//int a = 11;//直接修改
printf("修改之前:%d \n", a);
printf("%d \n", &a);
*p = 20;//间接修改
printf("修改之后:%d \n", a);
system("pause");
}
void main04()
{
int a = 10;//分配4个字节的内存,栈区也叫临时区
int *p;//分配4个字节的内存,
p = &a;//CPU执行的代码,在代码区
*p = 20;
{
char *p = NULL;//分配4个字节的内存,栈区也叫临时区
p = (char *)malloc(100);//内存泄露概念
if (p != NULL)
{
free(p);
}
}
system("pause");
}
结论:
数据类型本质:固定大小内存的别名
数据类型取别名 typdedef 抛出问题:
如何表达:int array[10] int add(int a, int d);
4、变量的本质
修改变量的方法(三种) 第一种 直接赋初值, 第二种,找到变量的地址,在内存中修改 第三种,引用 |
变量的本质:连续内存块的别名,变量是一个标号。再抛砖 程序的内存四区图,画变量示意图时,主要把变量放在外面,内存空间留出来 |
总结:1对内存 可读可写; 2通过变量往内存读写数据,3不是向变量读写数据。4向变量代表的数据空间读写数据
|
5、内存四区概念
基本概念
函数1调用函数2,函数1称为主调函数 函数2称为被调用函数
规则1:Main(主调函数)分配的内存(在堆区,栈区、全局区)都可以在被调用函数里使用吧。
规则2:在被调用函数里面分配的内存
1、如果在被调用函数里面的临时区(栈)分配内存,主调用函数是不能使用的。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *getstring1()
{
char *p1 = "abcde1";
return p1;
}
void *getstring2()
{
char *p2 = "abcde";
return p2;
}
//void *getstring3(char **buf);
//
//void *getstring3(char(*buf)[30]);
void *getstring3(char buf[10][30])
{
// char buf[30];
char s[] = "abc";
char *s1 = "abc";
char **p2;
char ***p4;
//strcpy(buf, "abcde");
return buf;
}
//void *getstring4()
//{
// char *p2 = "abcde";
// return p2;
//}
void main41()
{
int i = 0;
char *p1 = getstring1();
char *p2 = getstring2();
char ********p3 = NULL;//p3也是分配4个字节,它也是一个变量
//char *p4 = getstring3();
//printf("p1=%s\n", p1);
//printf("p2=%s\n", p2);
//printf("p1=%d\n", p1);
//printf("p2=%d\n", p2);
//printf("p4=%d\n", p4);
//指针变量和她所指向的内存空间变量是两个不同的概念
strcmp(p1, p2);
system("pause");
}
void main42()
{
//main41();
system("pause");
}
6、指针铁律
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);
结论:指针的步长,根据所指内存空间类型来定。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void getData01(char *p1)//char *p1 是变量 是形参
//形参具有对外的属性
{
printf("getData01() begin\n");
return;
}
void getData02(char **p2)//char **p2 是变量 形参
//形参具有对外的属性
{
printf("getData01() begin\n");
return;
}
void getData03(char ***p3)//char ***p3 是变量 是形参
//形参具有对外的属性
{
printf("getData01() begin\n");
return;
}
//下面的变形,怎么写都无所谓,关键是理解的角度从两个角度出发
//站在C/C++编译器的角度出发,对形参,如果是指针类型(不管有几个*),C编译只会分配四个字节的内存
//指针的数据类型,到底是什么
//指针的数据类型是指他所指向的的内存的空间的数据类型,,具有依附属性
//指针的步长使用指针的数据类型的来决定的,比如char *p ;p++这样的步长就是1个字节
//int *p;p++ 步长就是4,以此类推,若
//void senddata01(char *p1); void senddata01(char* p1);
//void senddata02(char **p1); void senddata02(char * *p1);
//void senddata03(char ***p1); void senddata03(char * **p1); void senddata03(char*** p1);
//void senddata04(char p[]);
//void senddata05(char *****p4);
void main61()
{
char *p1 = NULL;
char **p2 = NULL;
int a = 10;
int *p3 = NULL;
a = 20;//直接通过变量修改地址
p3 = &a;
//*p放在等号的左边,去修改内存空间的值
//*p放在等号的右边,从内存空间拿值(取值)
*p3 = 30;//通过指针间接修改内存空间的值
int c = *p3;
//*p的意义: *就像一把钥匙,根据一个指针变量的值,去修改门后的内存空间
printf("p1:%d\n", sizeof(p1));
system("pause");
}
void main62()
{
char *p1 = NULL;
char *p2 = NULL;
char buf1[100] = {0};
char buf2[100] = {0};
strcpy(buf1, "abcdefg");
p1 = buf1;
p2 = buf2;
while (*p1 != '\0')
{
*p2 = *p1;
p1++;
p2++;
}
system("pause");
}
void main63()
{
//{
// //向NULL空间写数据
// char *p = NULL; //写入位置 0x00000000 时发生访问冲突。0是操作系统用的地方
// *p = 100;
//}
//{
// char *p = 0x77;写入位置 0x00000077 时发生访问冲突。
// *p = 100;
//}
//{
// char *p = "abcdefg";//写入位置 0x01256960 时发生访问冲突。这个地址就是字符串的常量的首地址
// *p = 'z';
//}
//{
// //int *a = &10;// 10是字面量,不能取地址,,放在不能取地址,没有放在堆区,栈区,全局区,可以按照放在代码区之类的区域去理解他
//
//}
//{
// char *p = NULL;
// strcpy(p, "abcdef");
//}
system("pause");
}
//不断地修改指针的的值的含义
//需要建立场景,解决指针乱指的问题
void main()
{
char buf[20];
char *p = NULL;
strcpy(buf, "123456789987654");
p = &buf[0];
p = &buf[1];
p = &buf[3];
p = &buf[4];
p = &buf[5];
p = &buf[6];
for (int i = 0; i, 20; i++)
{
p = &buf[i];
}
}
7、经验:
有关字面量的理解
{
//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)); } |