接口封裝設計思想引導
Sckclient客戶端api模型設計
第一套api函數
#ifndef _SCK_CLINT_H_
#define _SCK_CLINT_H_
//函數聲明
// 1、客戶端環境初始化
int sckClient_init(void **handle); //5 day
//
// 2、客戶端發送報文
int sckClient_send(void *handle, unsigned char *data, int datalen);
// 3、客戶端端接受報文
int sckClient_rev(void *handle, unsigned char *out, int *outlen); //1
// 4、客戶端環境釋放
int sckClient_destroy(void *handle);
#endif
//條件編譯 避免頭文件多次包含
#ifndef _SCK_CLINT02_H_
#define _SCK_CLINT02_H_
#ifdef __cplusplus
extern "C" {
#endif
//函數聲明
// 1、客戶端環境初始化
int sckClient_init2(void **handle); //5 day
//
// 2、客戶端發送報文
int sckClient_send2(void *handle, unsigned char *data, int datalen);
// 3、客戶端端接受報文
int sckClient_rev2(void *handle, unsigned char **out, int *outlen); //1
int sckClient_rev2_Free(void **p); //1
// 4、客戶端環境釋放
int sckClient_destroy2(void **handle);
#ifdef __cplusplus
}
#endif
#endif
我們找到了一套標准,我們可以高效、有目的的學習。
Socket動態庫業務模型思路分析
經驗話語
Shift+del 刪除一行 ctrl+shift+u大小 ctrl +u 小寫
Alt+F9
F5在多個斷點間切換
//當數組當做函數參數的話的時候會退化為指針
int printfArray(int a[])
{
int i = 0;
printf("排序之前\n ");
for (i=0; i<10; i++)
{
printf("%d ", a[i]);
}
return 0;
}
//int a[10] -=-->int a[] ---->int *a
//數組做函數形參的時候,如果在形參中定義int a[10]語句,
//c/c++編譯器 會做優化,技術推演如下
//int a[10] -=-->int a[] ---->int *a
//總結:函數調用的時候,把數組首地址和有效數據長度傳給被調用函數才是最正確的做法
int printfArray04(int *a, int num)
{
int i = 0;
printf("排序之前\n ");
for (i=0; i
{
printf("%d ", a[i]);
}
return 0;
}
數據類型可理解為創建變量的模具(模子);是固定大小內存的別名。
sizeof是操作符,不是函數;sizeof測量的實體大小為編譯期間就已確定
數據類型可以取別名、測量大小
數據類型的封裝
Void數據類型的封裝
數據類型的引申
C一維數組、二維數組有數據類型嗎 3
C語言中,函數是可以看做一種數據類型嗎?15
數組類型三大技術難點,壓死初學者的三座大山
變量本質:(一段連續)內存空間的別名、內存空間的標號
修改變量的3種方法
1、直接
2、間接。內存有地址編號,拿到地址編號也可以修改內存;於是。。。橫空出世了!
3、c++ 引用
總結:1對內存 可讀可寫; 2通過變量往內存讀寫數據,3不是向變量讀寫數據。4向變量代表的數據空間讀寫數據。變量跑到什麼地方去了?
1、 內存四區模型和函數調用模型
基本概念
函數1調用函數2,函數1稱為主調函數 函數2稱為被調用函數
規則1:Main(主調函數)分配的內存(在堆區,棧區、全局區)都可以在被調用函數裡使用吧。
規則2:在被調用函數裡面分配的內存
1、如果在被調用函數裡面的臨時區(棧)分配內存,主調用函數是不能使用的。
全局區://c++編譯器優化
char *getStr1()
{
char *p = "abcd1";
return p;
}
char *getStr2()
{
char *p = "abcd1";
return p;
}
//
臨時區stack
char * getStr3()
{
char buf[100];
memset(buf, 0, sizeof(buf));
strcpy(buf, "abcd1");
return buf;
}
//棧屬性
//棧向下生長的,
//棧的生長方向和內存空間buf存放方向是兩個不同的概念
//堆向上生長的,
//演示:stack生長方向
int main31()
{
float *p1 = NULL;
int *p2 = NULL;
int a = 0;
int b= 0;
char buf[16];
printf("&p1:%x, &p2:%x, &a:%x, &b:%x \n", &p1, &p2, &a, &b);
printf("&buf[0]:%x, &buf[1]:%x", &buf[0], &buf[1]);
getchar();
}
//軟件開發中 注意野指針
//仔細觀察malloc內存地址大小
//演示heap生長方向
int main32()
{
int a = 0;
int b = 0;
char *p1 = NULL;
char *p2= NULL;
p1 = (char *)malloc(16);
p2 = (char *)malloc(16);
printf("\n p1:%x, p2:%x", p1, p2);
printf("\n &p1:%x, &p2:%x", &p1, &p2);
//通過內存地址間接賦值
*((char *)0x394da0) = 'a';
*((char *)0x394da1) = 'b';
//通過內存地址間接修改內存空間的值
//通過變量名訪問內存空間
//通過內存地址間接訪問內存空間 這就是C語言的靈活性,也是c語言的精華
printf("\np2[0]:%c", p2[0]);
printf("\np2[1]:%c", p2[1]);
if (p1 != NULL)
{
free(p1);
}
if (p2 != NULL)
{
free(p2);
}
getchar();
return 0;
}
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);
結論:指針的步長,根據所指內存空間類型來定。
//在函數調用哪個的時候 實參的值機械的傳給形參(c int數組場景)
//關於形參:
寫在函數上形參變量,還是寫在函數裡面的變量,
從CC++編譯的角度來講,是沒有任何區別的(分配4字節內存);
只不過是 寫在函數上形參變量 ,具有對外的屬性而已
//數據類型分為兩種,一個是簡單的數據類型,一個是復雜的數據類型。碰見復雜的數據類型不能用簡單的數據類型的思維去思考它。拋磚
/*
int getbuf01(char *p); int getbuf01(char* p);
int getbuf02(char **p); int getbuf02(char * *p); getbuf02(char ** p);
int getbuf03(char (*p)[]); int getbuf03(char (*p) []); int getbuf03(char ( *p)[ ]);
int getbuf03(char p[10][30]);
int getbuf04(char *****p);
*/
//角度1站在c++編譯器的角度 指針就是一個變量,除此之外啥也不是!
//不管是1個* 還是8個*對c++編譯器來講,只會分配4個字節內存
//角度2:當我們程序員要使用指針所指向的內存空間的時候,我們關心,這個內存塊是一維的,還是二維的。
//一般情況:1級指針代表1維,二級指針代表二維。。。
//如果有超過char ***級及3級以上的指針,則不代表幾維的內存。。。
//多維數組做函數參數,一般情況下,只能表達到二維,
//如果是三維內存(我們程序員起的名字),已經沒有意義。
//證明一下多維數組的線性存儲
//線性打印
void printfAARRR(char ***ddd);
void printfAARRR(char *********dddd);
void printfArray411(int *array,int num)
{
int i = 0;
for (i=0; i
{
printf("%d ", array[i]);
}
}
void printfArray412(int (*array)[5],int num)
{
return ;
}
void printfArrr333(int c[3][4][5])
{
return ;
}
void main()
{
int a[3][5];
int c[3][4][5];
int i , j = 0;
int tmp = 0;
for (i=0; i<3; i++)
{
for (j=0; j<5; j++)
{
a[i][j] = tmp ++;
}
}
printfArray411((int *)a, 15);
system("pause");
}
C可以在臨時區分配內存塊。。。。。。。java不行
{
char *p1 = 0; //
strcpy(p1, "abcdefg");
strcpy(0, "abcdefg"); //拋磚:在兩個函數裡面就不一定能明白
}
//[] *的本質到底是什麼?
//*p 是我們程序員手工的(顯示)去利用間接賦值
//【】 只不過是,c/c++ 編譯器幫我們做了一個*p的操作。。。。。。
// buf4[i]======> buf4[0+i] ====> *(buf4+i)
//===*(buf4+i) --> bu4[i];
//操作數組的方法
//下標法和指針法
void main()
{
int i = 0;
char *p = NULL;
//通過字符串初始化字符數組 並且追加\0
char buf4[] = "abcd";
for (i=0; i
{
printf("%c", buf4[i]); //p[]
}
//[] *的本質到底是什麼?
//*p 是我們程序員手工的(顯示)去利用間接賦值
//【】 只不過是,c/c++ 編譯器幫我們做了一個*p的操作。。。。。。
// buf4[i]======> buf4[0+i] ====> *(buf4+i)
//===*(buf4+i) --> bu4[i];
printf("\n");
p = buf4;
for (i=0; i
{
printf("%c", *(p+i)); //*p
}
system("pause");
}
{
int a[10]; //a是一個指針===》a常量指針===》為什麼c++
int *p = a;
p ++;
a ++;
}
//c++編譯器要拿著a去析構內存,為了避免你把a的指向改變。。。。。
間接賦值成立的是3個條件
/* 間接賦值成立的三個條件
條件1 //定義1個變量(實參) //定義1個變量(形參)
條件2//建立關聯:把實參取地址傳給形參
條件3://*形參去間接地的修改了實參的值。
*/
Int iNum = 0; //實參
int *p = NULL;
p = &iNum;
iNum = 1;
*p =2 ; //通過*形參 == 間接地改變實參的值
*p成立的三個條件:
間接賦值成立三個條件的幾種組合
123在一個函數裡面
12 3 兩個函數
1 23兩個函數
//間接賦值條件應用深入分析 三個條件的組合,分別產生三種很重要的語法現象
//123都寫在一個函數裡面
//12寫在一個函數裡面 3 寫在另外一個函數裡面
//1 寫在一個函數裡面 23 寫在另外一個函數裡面 拋磚。。。到時候別不認識啊。。。。。
間接賦值應用場景12
場景1:一個函數之內 *p1++ = *p2++
場景2:int getFileLen(int *a )
間接賦值的推論
//在函數調用的時候
/*
用1級指針形參,去間接修改了0級指針(實參)的值。。
用2級指針形參,去間接修改了1級指針(實參)的值。。
用3級指針形參,去間接修改了2級指針(實參)的值。。
用n級指針形參,去間接修改了n-1級指針(實參)的值。。
*/
間接賦值的工程意義
//函數調用時,形參傳給實參,用實參取地址,傳給形參,在被調用函數裡面用*p,來改變實參,把運算結果傳出來。
//指針作為函數參數的精髓。
//C語言特有的想象,是C語言的精華。。。
尋路
指針做函數參數是我們的研究重點。。。。。
指針是子彈、函數像槍管,,子彈槍管才能發揮它的威力。。。。。。。
下一步你的方向
1、 指針學完了。。。。。你只是c語言的半壁江山。。。。。
2、 函數指針。。。。
//c語言裡面沒有字符串這種類型。。。。。
//通過字符數組來模擬字符串
//C風格字符串是以零結尾的字符串
void main11()
{
//字符數組初始化
//指定長度 如果定義的長度剩余部分補充0
char buf1[100] = {'a', 'b', 'c'};
//不指定長度
char buf2[] = {'a', 'b', 'c'};
char buf3[] = {'a', 'b', 'c','\0'};
//通過字符串初始化字符數組 並且追加\0
char buf4[] = "abcdefg";
printf("%s\n", buf4 );
system("pause");
}
printf("%s\n", buf4 );
printf("sizeof(buf4): %d\n ", sizeof(buf4)); //注意sizeof是對數組類型進行大小測量 包括了\0
printf("strlen(buf4): %d \n", strlen(buf4));//strlen是求字符串的長度不包括\0
一級指針內存模型圖
字符串做函數參數
C庫字符串API函數調用經驗談
字符串copy函數技術推演
//C字符串函數調用方法經驗談
//站在內存四區模型和函數調用模型去思考函數。。。。。api接口
/*
1) 主調函數 被調函數
a) 主調函數可把堆區、棧區、全局數據內存地址傳給被調用函數
b) 被調用函數只能返回堆區、全局數據
2) 內存分配方式
a) 指針做函數參數,是有輸入和輸出特性的。
*/
//C字符串函數調用方法經驗談
//站在內存四區模型和函數調用模型去思考函數。。。。。api接口
/*
1) 主調函數 被調函數
a) 主調函數可把堆區、棧區、全局數據內存地址傳給被調用函數
b) 被調用函數只能返回堆區、全局數據
2) 內存分配方式
a) 指針做函數參數,是有輸入和輸出特性的。
*/
字符串操作常見工程開發模型
業務模型&業務測試模型分離===》接口封裝和設計第一步
//被調用函數分配內存吧結果甩出來有兩種方法
//return
//指針做函數參數
char * getBuffer()
{
char buf[109];
char *p = (char *)malloc(199);
//char *p2= (char *)malloc(199);
return p;
}
strstr的while dowhile模型
//int cltClient_rev(void *handle, unsigned char *buf, int *buflen)
//不要相信別人給你傳送的內存地址是可用的
int getCout(char *str, char *substr, int *count)
{
int rv = 0;
char *p = str;
int ncout = 0;
if (str==NULL || substr== NULL || count==NULL)
{
rv = -1;
printf("func getCout()check (str==NULL || substr== NULL || count==NULL) err:%d \n" , rv);
return rv;
}
do
{
p = strstr(p, substr);
if (p == NULL) //沒有找到則跳出來
{
break;
}
else
{
ncout++;
p = p + strlen(substr);
}
} while (*p != '\0');
//fuzhi
*count = ncout;
printf("ncout:%d\n", ncout);
return rv;
}
void main36()
{
char *p = "abcd1111abcd222abcd3333";
int ncout = 0;
while (p = strstr(p, "abcd"))
{
p = p + strlen("abcd");
ncout ++;
if (*p == '\0')
{
break;
}
}
printf("ncout:%d\n", ncout);
system("pause");
}
兩頭堵模型(兩種寫法)
//求去掉空格
//int trimSpaceStr2(char *p, unsigned char *buf2, int *buf2len)
int trimSpaceStr2( char *p, char *buf2)
{
int ret = 0;
int ncount = 0;
int i, j;
i = 0;
j = strlen(p) -1;
while (isspace(p[i]) && p[i] != '\0')
{
i++;
}
while (isspace(p[j]) && j>0 )
{
j--;
}
ncount = j - i + 1;
//
strncpy(buf2, p+i, ncount);
buf2[ncount] = '\0';
return ret;
}
//求去掉空格
//int trimSpaceStr2(char *p, unsigned char *buf2, int *buf2len)
//不要輕易去改變指針輸入特性中in內存塊的內存。。。。
int trimSpaceStr2_notgood( char *p)
{
int ret = 0;
int ncount = 0;
int i, j;
i = 0;
j = strlen(p) -1;
while (isspace(p[i]) && p[i] != '\0')
{
i++;
}
while (isspace(p[j]) && j>0 )
{
j--;
}
ncount = j - i + 1;
//
strncpy(p, p+i, ncount);
p[ncount] = '\0';
return ret;
}
字符串反轉模型
void main51()
{
char p[] = "abcde";
char c ;
char *p1 = p;
char *p2 = p + strlen(p) -1;
while (p1 < p2)
{
c = *p1;
*p1 = *p2;
*p2 = c;
++p1;
--p2;
}
printf("p:%s \n", p);
system("pause");
}
int getKeybyValue(char *pKeyValude, char *pKey, char *pValude)
{
char rv = 0;
char *p = NULL;
if (pKeyValude==NULL )
{
rv = -1;
printf("func getKeybyValue() err:%d pKeyValude \n", rv);
return rv;
}
if ( pKey==NULL )
{
rv = -1;
printf("func getKeybyValue() err:%d pKey=NULL \n", rv);
return rv;
}
if ( pValude==NULL )
{
rv = -1;
printf("func getKeybyValue() err:%d pValude \n", rv);
return rv;
}
//1 在pKeyValude中查找是否有關鍵字pKey
p = strstr(pKeyValude, pKey);
if (p == NULL)
{
rv = -1;
printf("func getKeybyValue() err:%d 查找沒有關鍵字pKey \n", rv);
return rv;
}
p = p + strlen(pKey); //為下一次檢索做准備
//2 有沒有=
p = strstr(p, "=");
if (p == NULL)
{
rv = -2;
printf("func getKeybyValue() err:%d 查找沒有= \n", rv);
return rv;
}
p = p + 1; //為下一次提取valude做准備
//3 提取按照要求的valude
rv = trimSpaceStr03(p, pValude);
if (rv != 0)
{
printf("func trimSpaceStr03() err:%d \n", rv);
return rv;
}
return rv;
}
建立一個思想:是主調函數分配內存,還是被調用函數分配內存;
//不要相信,主調函數給你傳的內存空間,你可以寫。。。。。。一級指針你懂了。
但是二級指針,你就不一定懂。。。拋出。。。。。。。。。
越界 語法級別的越界
char buf[3] = "abc";
不斷修改指針變量的值
臨時str3內存空間
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--; /*去掉串尾結束標志*/
const專題講座
Const好處
//合理的利用const,
//1指針做函數參數,可以有效的提高代碼可讀性,減少bug;
//2清楚的分清參數的輸入和輸出特性
結論:
//指針變量和它所指向的內存空間變量,是兩個不同的概念。。。。。。
//看const 是放在*的左邊還是右邊 看const是修飾指針變量,還是修飾所指向的內存空變量
int main()
{
const int a; //
int const b;
const char *c;
char * const d;
const char * const e ;
return 0;
}
Int func1(const )
初級理解:const是定義常量==》const意味著只讀
含義:
//第一個第二個意思一樣 代表一個常整形數
//第三個 c是一個指向常整形數的指針(所指向的內存數據不能被修改,但是本身可以修改)
//第四個 d 常指針(指針變量不能被修改,但是它所指向內存空間可以被修改)
//第五個 e一個指向常整形的常指針(指針和它所指向的內存空間,均不能被修改)
Char myArray[10][30]指針數組的一個指針.
myArray是一個指針變量 ,是一個常量。。。是一個常量指針
void f(int a[5]) ====》void f(int a[]); ===》 void f(int* a);
void g(int a[3][5])====》 void g(int a[][5]); ====》 void g(int (*a)[5]);
技術推演過程 *(*(a+1) +j ) a[i][j]
Chsr * p[3] = {“aaaa”, “bbb”,”cccc”};
Int printArray(char *p[3])==èInt printArray(char *p[])==èInt printArray(char **p)
void main()
{
//03、數組類型、數組指針類型、數組指針類型變量
typedef int MyTypeArray[5];
MyTypeArray a; //int a[5];
int intArray[3][5];
{
typedef int (*MyPTypeArray)[5];
MyPTypeArray myArrayPoint ;
myArrayPoint = &a;
(*myArrayPoint)[0] = 1; //通過一個數組指針變量去操作數組內存
}
{
int (*myArrayVar)[5]; //告訴編譯給我開辟4個字節的內存‘
myArrayVar = &a;
(*myArrayVar)[1] = 2;
}
{
int (*myArrayVar2)[5]; //告訴編譯給我開辟4個字節的內存‘
myArrayVar2 = intArray; //
}
}
本質是因為 程序員眼中的二維內存,在物理內存上是線性存儲。所以說是真。。。。。
/證明一下多維數組的線性存儲
//線性打印
//多維數組做函數參數,一般情況下,只能表達到二維,
//如果是三維內存(我們程序員起的名字),已經沒有意義。
//一般情況:1級指針代表1維,二級指針代表二維。。。
//如果有超過char ***級及3級以上的指針,則不代表幾維的內存。。。
void printfAARRR(char ***ddd);
void printfAARRR(char *********dddd);
void printfArray411(int *array, int num)
{
int i = 0;
for (i=0; i
{
printf("%d ", array[i]);
}
}
void printfArray412(int (*array)[5], int num)
{
return ;
}
void printfArrr333(int c[3][4][5])
{
return ;
}
void main()
{
int a[3][5];
int c[3][4][5];
int i , j = 0;
int tmp = 0;
for (i=0; i<3; i++)
{
for (j=0; j<5; j++)
{
a[i][j] = tmp ++;
}
}
printfArray411((int *)a, 15);
system("pause");
}
1、 C語言中只會以機械式的值拷貝的方式傳遞參數(實參把值傳給形參)
int fun(char a[20], size_t b)
{
printf("%d\t%d",b,sizeof(a));
}
原因1:高效
原因2:
C語言處理a[n]的時候,它沒有辦法知道n是幾,它只知道&n[0]是多少,它的值作為參數傳遞進去了
雖然c語言可以做到直接int fun(char a[20]),然後函數能得到20這個數字,但是,C沒有這麼做。
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]
//C:概念不清晰是產生bug的根源
//C即使概念不清晰,訓練不到位,也是產生bug的根源===》避免眼高手低、訓練到極致
//C:不能深入理解C各種語法現象,是阻礙你成為高手的主要原因。
char **getMem(int count)
{
int i = 0;
char **tmp = (char **)malloc(count*sizeof(char *));
for (i=0; i
{
tmp[i] = (char *)malloc(100);
}
return tmp;
}
void sortArray(char **myArray, int count)
{
int i = 0, j = 0;
char *tmp;
for (i=0; i
{
for (j=i+1; j
{
if (strcmp(myArray[i], myArray[j]))
{
tmp = myArray[i]; //這個地方交換的是指針變量
myArray[i] = myArray[j];
myArray[j] = tmp;
}
}
}
}
void sortArray02(char **myArray, int count)
{
int i = 0, j = 0;
char tmp[200];
for (i=0; i
{
for (j=i+1; j
{
if (strcmp(myArray[i], myArray[j]) > 0)
{
strcpy(tmp, myArray[i]);
strcpy(myArray[i], myArray[j]);
strcpy(myArray[j], tmp); //交換是buf的內容
}
}
}
}
void printfArray(char **myArray, int count)
{
int i = 0, j = 0;
for (i=0; i
{
printf("%s \n", myArray[i]);
}
}
void main()
{
char **pArray = NULL;
pArray = getMem(3);
strcpy(pArray[0], "bbbbb");
strcpy(pArray[1], "aaaa");
strcpy(pArray[2], "cccc");
printf("排序之前\n");
printfArray(pArray ,3);
//sortArray(pArray, 3);
sortArray02(pArray, 3);
printf("排序之後\n");
printfArray(pArray ,3);
system("pause");
}
char **getMem(int count)
{
int i = 0;
char **tmp = (char **)malloc((count+1)*sizeof(char *) );
for (i=0; i
{
tmp[i] = (char *)malloc(100);
}
tmp[count] = '\0'; //轉義字符的0
tmp[count] = 0; //轉義字符的0
tmp[count] = NULL; //轉義字符的0
return tmp;
}
//野指針產生問題分析
//指針變量和它所指內存空間變量是兩個不同的概念
//解決野指針的方案
//1定義指針時 把指針變量賦值成null
//2 釋放內存時,先判斷指針變量是否為null
//3 釋放內存完畢後,把指針變量重新賦值成null
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//野指針產生問題分析
//指針變量和它所指內存空間變量是兩個不同的概念
//解決野指針的方案
//1定義指針時 把指針變量賦值成null
//2 釋放內存時,先判斷指針變量是否為null
//3 釋放內存完畢後,把指針變量重新賦值成null
//
void main22()
{
char *p = NULL;
p = (char *)malloc(100); //char p[100];
strcpy(p, "abcdefg");
//做業務
//此處省略5000字。。。。。
if (p != NULL)
{
free(p);
p = NULL;
}
//做業務
//此處省略5000字。。。。。
if (p != NULL)
{
free(p);
}
system("pause");
}
char *getMem2(int count)
{
char *tmp = NULL;
tmp = (char *)malloc(100*sizeof(char)); //char tmp[100];
return tmp;
}
//實參和形參是兩個不同的概念
void getMem3(int count, char *p)
{
char *tmp = NULL;
tmp = (char *)malloc(100*sizeof(char)); //char tmp[100];
p = tmp; //在這個場景下,你給形參賦值了,沒有給實參賦值
//直接修改實參沒戲。。。。。。。 實參和形參是兩個不同的概念
//return tmp;
}
void getMem4(int count, char **p /*out*/)
{
char *tmp = NULL;
tmp = (char *)malloc(100*sizeof(char)); //char tmp[100];
//p = tmp; //在這個場景下,你給形參賦值了,沒有給實參賦值
//直接修改實參沒戲。。。。。。。 實參和形參是兩個不同的概念
//間接的修改實參
//*(實參的地址) =
*p = tmp;
//return tmp;
}
//函數調用的時候,這個場景修改不實參
int FreeMem2(char *p)
{
if (p ==NULL)
{
return -1;
}
if (p != NULL)
{
free(p);
p = NULL; //想把實參給改掉,你能修改嗎? 修改不了實參。。。。。
}
return 0;
}
void main51()
{
char *myp = NULL;
myp = getMem2(100);
//getMem3(100, myp);
//getMem4(100, &myp);
//做業務操作
//此 50000
FreeMem2(myp);
FreeMem2(myp);
}
void main()
{
Teacher t1;
Teacher t2;
Teacher *p = NULL;
printf(" %d \n", sizeof( Teacher));
p = &t1;
strcpy(t1.name, "name");
t1.age = 10; //通過.的方法來操作結構體的成員域
p->age = 12;
p->age; // . ->的本質是尋址。。。。。尋每一個成員相對於大變量t1的內存偏移。。。。。。沒有操作內存
//所以這樣寫是沒有問題的。
t2 = t1; //編譯器做了什麼工作
對結構體而言,指針做函數參數和元素變量做函數不同地方
void copyStruct(Teacher *to, Teacher *from)
{
*to = *from;
}
//
int copyStruct2(Teacher to, Teacher from)
{
to = from;
return 10;
}
Teacher *creatTArray2(int num)
{
int i = 0, j = 0;
Teacher *tArray = NULL;
tArray = (Teacher *)malloc(num * sizeof(Teacher));
if (tArray == NULL)
{
return NULL;
}
for (i=0; i
{
tArray[i].tile = (char *)malloc(100);
}
//創建老師帶的學生
for (i=0; i
{
char **ptmp = (char **)malloc((3+1)*sizeof(char *));
for (j=0; j<3; j++)
{
ptmp[j] = (char *)malloc(120);
}
//ptmp[3] = NULL;
tArray[i].pStuArray = ptmp;
}
return tArray;
}
釋放函數
int FreeTArray(Teacher *tArray, int num)
{
int i =0, j = 0;
if (tArray == NULL)
{
return -1;
}
for (i=0; i
{
char **tmp = tArray[i].pStuArray;
if (tmp ==NULL)
{
continue;;
}
for (j=0; j<3; j++)
{
if (tmp[j] != NULL)
{
free(tmp[j]);
}
}
free(tmp);
}
for (i=0; i<3; i++)
{
if (tArray[i].tile != NULL)
{
free(tArray[i].tile);
tArray[i].tile = NULL; //laji
}
}
free(tArray);
tArray = NULL; //垃圾
}
//產生的原因
//編譯器給我們提供的copy行為是一個淺copy
//當結構體成員域中含有buf的時候,沒有問題
//當結構體成員域中還有指針的時候,編譯器只會進行指針變量的copy。指針變量所指的內存空間,編譯器不會在多分分配內存
//這就是編譯器的淺copy,我們要屬順從。。。。
//
/結構體的定義
typedef struct _AdvTeacher
{
char *name;
char buf[100];
int age;
}Teacher ;
Teacher * creatT()
{
Teacher *tmp = NULL;
tmp = (Teacher *)malloc(sizeof(Teacher));
tmp->name = (char *)malloc(100);
return tmp;
}
void FreeT(Teacher *t)
{
if (t == NULL)
{
return ;
}
if (t->name != NULL)
{
free(t->name);
}
}
//解決方案
int copyObj(Teacher *to, Teacher *from)
{
//*to = *from;//copy;
memcpy(to, from, sizeof(Teacher));
to->name = (char *)malloc(100);
strcpy(to->name, from->name);
}
深刻理解-》 。操作符的本質
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
typedef struct _A
{
int a ;
};
//結構體的定義
typedef struct _AdvTeacher
{
char *name; //4
int age2 ;
char buf[32]; //32
int age; //4
struct _A
}Teacher ;
void main2()
{
int i = 0;
Teacher * p = NULL;
p = p - 1;
p = p - 2;
p = p +2;
p = p -p;
i = (int) (&(p->age)); //1邏輯計算在cpu中,運算
printf("i:%d \n", i);
//&屬於cpu的計算,沒有讀寫內存,所以說沒有coredown
system("pause");
}
//-> .
void main()
{
int i = 0;
i = (int )&(((Teacher *)0)->age );
printf("i:%d \n", i);
//&屬於cpu的計算,沒有讀寫內存,所以說沒有coredown -->
system("pause");
}
文件讀寫api的熟悉
char *fname = "c:\\1.txt";
char *fname2 = "c:/a1.txt"; //統一的用45度斜槓
fgetc fputc 按照字符讀寫文件
fputs fgets 按照行讀寫文件 (讀寫配置文件)
fread fwirte 按照塊讀寫文件 (大數據塊遷移)
void main04()
{
int i = 0;
FILE *fp = NULL;
char buf[100];
char *p = NULL;
char *fname = "c:\\1.txt";
char *fname2 = "c:/a1.txt"; //統一的用45度斜槓
fp = fopen(fname2, "r"); //不管文件是否存在,新建文件
if (NULL == fp)
{
printf("func fopen() err: \n");
}
while (!feof(fp))
{
//_cdecl fgets(_Out_z_cap_(_MaxCount) char * _Buf, _In_ int _MaxCount, _Inout_ FILE * _File);
p = fgets(buf, 100, fp);
if (p == NULL)
{
printf("func fgets() .....\n");
return ;
}
printf("%s \n", buf);
printf("%s \n", p);
}
if (fp != NULL)
{
fclose(fp);
}
}
項目開發中參考fgets函數的實現方法
fgets(buf, bufMaxLen, fp);
對fgets函數來說,n必須是個正整數,表示從文件按中讀出的字符數不超過n-1,存儲到字符數組str中,並在末尾加上結束標志’\0’,換言之,n代表了字符數組的長度,即sizeof(str)。如果讀取過程中遇到換行符或文件結束標志,讀取操作結束。若正常讀取,返回指向str代表字符串的指針,否則,返回NULL(空指針)。
文件控制
fp = fopen(pFileName, "r+");
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
//goto End;
}
if (fp == NULL)
{
fp = fopen(pFileName, "w+t");
if (fp == NULL)
{
rv = -3;
printf("fopen() err. \n");
goto End;
}
}
fseek(fp, 0L, SEEK_END); //把文件指針從0位置開始,移動到文件末尾
//獲取文件長度;
length = ftell(fp);
fseek(fp, 0L, SEEK_SET);
配置文件讀寫庫的設計與實現
// cfg_op.h
#ifndef _INC_CFG_OP_H
#define _INC_CFG_OP_H
#ifdef __cplusplus
extern "C" {
#endif
int GetCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/);
int WriteCfgItem(char *pFileName /*in*/, char *pItemName /*in*/, char *pItemValue/*in*/, int itemValueLen /*in*/);
//int CfgItem_Init(void *pHandle, int iType);
//int GetCfgItem(void *pHandle /*in*/, char *pKey /*in*/, char * pValue/*in out*/, int * pValueLen /*out*/);
//int WriteCfgItem(void *pFileName /*in*/, char *pItemName /*in*/, char *pItemValue/*in*/, int itemValueLen /*in*/);
//int CfgItem_Destory(void *pHandle);
#ifdef __cplusplus
}
#endif
#endif
大數據文件加密解密設計與實現
2)指針也是一種變量,占有內存空間,用來保存內存地址
測試指針變量占有內存空間大小
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);
結論:指針的步長,根據所指內存空間類型來定。
注意: 建立指針指向誰,就把把誰的地址賦值給指針。圖和代碼和二為一。
不斷的給指針變量賦值,就是不斷的改變指針變量(和所指向內存空間沒有任何關系)。
1)兩碼事:指針變量和它指向的內存塊變量
2)條件反射:指針指向某個變量,就是把某個變量地址否給指針
3)*p間接賦值成立條件:3個條件
a)2個變量(通常一個實參,一個形參)
b) 建立關系,實參取地址賦給形參指針
c)*p形參去間接修改實參的值
Int iNum = 0; //實參
int *p = NULL;
p = &iNum;
iNum = 1;
*p =2 ; //通過*形參 == 間接地改變實參的值
*p成立的三個條件:
4)引申: 函數調用時,用n指針(形參)改變n-1指針(實參)的值。
//改變0級指針(int iNum = 1)的值有2種方式
//改變1級指針(eg char *p = 0x1111 )的值,有2種方式
//改變2級指針的(eg char **pp1 = 0x1111 )的值,有2種方式
//函數調用時,形參傳給實參,用實參取地址,傳給形參,在被調用函數裡面用*p,來改變實參,把運算結果傳出來。
//指針作為函數參數的精髓。
1)主調函數 被調函數
a)主調函數可把堆區、棧區、全局數據內存地址傳給被調用函數
b)被調用函數只能返回堆區、全局數據
2)內存分配方式
a)指針做函數參數,是有輸入和輸出特性的。
編號
指針函數參數
內存分配方式(級別+堆棧)
主調函數
實參
被調函數
形參
備注
01
1級指針
(做輸入)
堆
分配
使用
一般應用禁用
棧
分配
使用
常用
Int showbuf(char *p);
int showArray(int *array, int iNum)
02
1級指針
(做輸出)
棧
使用
結果傳出
常用
int geLen(char *pFileName, int *pfileLen);
03
2級指針
(做輸入)
堆
分配
使用
一般應用禁用
棧
分配
使用
常用
int main(int arc ,char *arg[]); 指針數組
int shouMatrix(int [3][4], int iLine);二維字符串數組
04
2級指針
(做輸出)
堆
使用
分配
常用,但不建議用,轉化成02
int getData(char **data, int *dataLen);
Int getData_Free(void *data);
Int getData_Free(void **data); //避免野指針
05
3級指針
(做輸出)
堆
使用
分配
不常用
int getFileAllLine(char ***content, int *pLine);
int getFileAllLine_Free(char ***content, int *pLine);
指針做函數參數,問題的實質不是指針,而是看內存塊,內存塊是1維、2維。
1)如果基礎類int變量,不需要用指針;
2)若內存塊是1維、2維。
一級指針做輸入
int showbuf(char *p)
int showArray(int *array,int iNum)
一級指針做輸出
int geLen(char *pFileName,int *pfileLen);
理解
主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存
二級指針做輸入
int main(int arc ,char *arg[]); 字符串數組
int shouMatrix(int [3][4], int iLine);
二級指針做輸出
int Demo64_GetTeacher(Teacher **ppTeacher);
int Demo65_GetTeacher_Free(Teacher **ppTeacher);
int getData(char **data, int *dataLen);
Int getData_Free(void *data);
Int getData_Free2(void **data); //避免野指針
理解
主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存
三級指針做輸出
int getFileAllLine(char ***content, int *pLine);
int getFileAllLine_Free(char ***content, int *pLine);
理解
主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存
1)野指針 2種free形式
int getData(char **data, int *dataLen);
int getData_Free(void *data);
int getData_Free2(void **data);
2)2次調用
主調函數第一次調用被調用函數求長度;根據長度,分配內存,調用被調用函數。
3)返回值char */int/char **
4)C程序書寫結構
商業軟件,每一個出錯的地方都要有日志,日志級別