程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言入門知識 >> C語言07指針高級

C語言07指針高級

編輯:C語言入門知識

01內存四區

接口封裝和設計思想引導

接口封裝設計思想引導

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:指針是一種數據類型

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);

結論:指針的步長,根據所指內存空間類型來定。

 

02經驗話語

01多級指針做函數參數的理解

 

//在函數調用哪個的時候 實參的值機械的傳給形參(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");

}

 

 

 

02C和java的堆棧區別

C可以在臨時區分配內存塊。。。。。。。java不行

 

 

{

char *p1 = 0; //

strcpy(p1, "abcdefg");

strcpy(0, "abcdefg"); //拋磚:在兩個函數裡面就不一定能明白

 

}

 

03【】*的本質

//[] *的本質到底是什麼?

//*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");

}

04為什麼inta[10] a是個常量

 

{

int a[10]; //a是一個指針===》a常量指針===》為什麼c++

int *p = a;

p ++;

a ++;

}

//c++編譯器要拿著a去析構內存,為了避免你把a的指向改變。。。。。

2 *p是指針存在的最大意義

 

間接賦值成立的是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、 函數指針。。。。

 

 

 

 

03字符串

字符串操作基礎

//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) 指針做函數參數,是有輸入和輸出特性的。

*/

 

 

3 深入理解指針必須和內存四區概念相結合,注意指針的輸入輸出特性

 

//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一個指向常整形的常指針(指針和它所指向的內存空間,均不能被修改)

 

 

 

04二級指針輸入模型

01二級指針輸入模型概念

 

 

 

02多維數組名的本質

Char myArray[10][30]指針數組的一個指針.

myArray是一個指針變量 ,是一個常量。。。是一個常量指針

 

 

03多維數組做函數參數退化問題

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]

 

04第1種和第3中二級指針做函數參數退化問題

Chsr * p[3] = {“aaaa”, “bbb”,”cccc”};

Int printArray(char *p[3])==èInt printArray(char *p[])==èInt printArray(char **p)

 

 

 

05數組類型、數組指針類型、數組指針類型變量

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; //

}

 

}

 

 

06多維數組做函數參數退化原因大剖析

本質是因為 程序員眼中的二維內存,在物理內存上是線性存儲。所以說是真。。。。。

 

/證明一下多維數組的線性存儲

//線性打印

 

//多維數組做函數參數,一般情況下,只能表達到二維,

//如果是三維內存(我們程序員起的名字),已經沒有意義。

 

//一般情況: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]

 

 

07二級指針三種內存模型建立

 

 

 

 

//C:概念不清晰是產生bug的根源

//C即使概念不清晰,訓練不到位,也是產生bug的根源===》避免眼高手低、訓練到極致

//C:不能深入理解C各種語法現象,是阻礙你成為高手的主要原因。

08第三種內存模型強化

 

 

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");

 

 

}

 

09第三種內存模型結束標志

 

 

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;

}

 

10野指針產生原因及解決方案

基礎知識

//野指針產生問題分析

//指針變量和它所指內存空間變量是兩個不同的概念

 

 

//解決野指針的方案

//1定義指針時 把指針變量賦值成null

//2 釋放內存時,先判斷指針變量是否為null

//3 釋放內存完畢後,把指針變量重新賦值成null

野指針和1級指針做函數參數在一起

#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);

}

 

 

 

05結構體

01、點操作和指針操作本質研究

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; //編譯器做了什麼工作

 

 

02編譯器淺copy操作

對結構體而言,指針做函數參數和元素變量做函數不同地方

void copyStruct(Teacher *to, Teacher *from)

{

*to = *from;

}

//

int copyStruct2(Teacher to, Teacher from)

{

 

to = from;

return 10;

}

 

03結構體中套一級指針和二級指針 項目開發要點

 

 

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; //垃圾

 

}

 

04深copy和淺copy

 

//產生的原因

//編譯器給我們提供的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");

}

 

 

06文件專題講座

文件讀寫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

 

 

大數據文件加密解密設計與實現

 

 

 

指針

鐵律1:指針是一種數據類型

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);

結論:指針的步長,根據所指內存空間類型來定。

 

注意: 建立指針指向誰,就把把誰的地址賦值給指針。圖和代碼和二為一。

不斷的給指針變量賦值,就是不斷的改變指針變量(和所指向內存空間沒有任何關系)。

鐵律2:通過*p/*p++ 來改變變量的值是指針存在的最大意義

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,來改變實參,把運算結果傳出來。

//指針作為函數參數的精髓。

鐵律3:理解指針必須和內存四區概念相結合

1)主調函數 被調函數

a)主調函數可把堆區、棧區、全局數據內存地址傳給被調用函數

b)被調用函數只能返回堆區、全局數據

2)內存分配方式

a)指針做函數參數,是有輸入和輸出特性的。

鐵律4:應用指針必須和函數調用相結合(指針做函數參數)

編號

指針函數參數

內存分配方式(級別+堆棧)

主調函數

實參

被調函數

形參

備注

 

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維。

鐵律5:一級指針典型用法(指針做函數參數)

一級指針做輸入

int showbuf(char *p)

int showArray(int *array,int iNum)

一級指針做輸出

int geLen(char *pFileName,int *pfileLen);

理解

主調函數還是被調用函數分配內存

被調用函數是在heap/stack上分配內存

鐵律6:二級指針典型用法(指針做函數參數)

二級指針做輸入

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上分配內存

鐵律7: 三級指針輸出典型用法

三級指針做輸出

int getFileAllLine(char ***content, int *pLine);

int getFileAllLine_Free(char ***content, int *pLine);

理解

主調函數還是被調用函數分配內存

被調用函數是在heap/stack上分配內存

 

鐵律8:雜項,指針用法幾點擴充

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程序書寫結構

商業軟件,每一個出錯的地方都要有日志,日志級別

鐵律9:一般應用禁用malloc/new

鐵律10:C函數指針是C++至高無上的榮耀

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved