c6011取消对null指针的引用_C/C++学习笔记——C提高:指针强化
指針是一種數(shù)據(jù)類型
指針變量
指針是一種數(shù)據(jù)類型,占用內(nèi)存空間,用來保存內(nèi)存地址。
void test01(){ int* p1 = 0x1234; int*** p2 = 0x1111; printf("p1 size:%d\n",sizeof(p1)); printf("p2 size:%d\n",sizeof(p2)); //指針是變量,指針本身也占內(nèi)存空間,指針也可以被賦值 int a = 10; p1 = &a; printf("p1 address:%p\n", &p1); printf("p1 address:%p\n", p1); printf("a address:%p\n", &a);}野指針和空指針
空指針
標(biāo)準(zhǔn)定義了NULL指針,它作為一個(gè)特殊的指針變量,表示不指向任何東西。要使一個(gè)指針為NULL,可以給它賦值一個(gè)零值。為了測(cè)試一個(gè)指針百年來那個(gè)是否為NULL,你可以將它與零值進(jìn)行比較。
對(duì)指針解引用操作可以獲得它所指向的值。但從定義上看,NULL指針并未執(zhí)行任何東西,因?yàn)閷?duì)一個(gè)NULL指針因引用是一個(gè)非法的操作,在解引用之前,必須確保它不是一個(gè)NULL指針。
如果對(duì)一個(gè)NULL指針間接訪問會(huì)發(fā)生什么呢?結(jié)果因編譯器而異。
不允許向NULL和非法地址拷貝內(nèi)存:
void test(){ char *p = NULL; //給p指向的內(nèi)存區(qū)域拷貝內(nèi)容 strcpy(p, "1111"); //err char *q = 0x1122; //給q指向的內(nèi)存區(qū)域拷貝內(nèi)容 strcpy(q, "2222"); //err }野指針
在使用指針時(shí),要避免野指針的出現(xiàn):
野指針指向一個(gè)已刪除的對(duì)象或未申請(qǐng)?jiān)L問受限內(nèi)存區(qū)域的指針。與空指針不同,野指針無法通過簡單地判斷是否為 NULL避免,而只能通過養(yǎng)成良好的編程習(xí)慣來盡力減少。對(duì)野指針進(jìn)行操作很容易造成程序錯(cuò)誤。
什么情況下會(huì)導(dǎo)致野指針?
指針變量未初始化
任何指針變量剛被創(chuàng)建時(shí)不會(huì)自動(dòng)成為NULL指針,它的缺省值是隨機(jī)的,它會(huì)亂指一氣。所以,指針變量在創(chuàng)建的同時(shí)應(yīng)當(dāng)被初始化,要么將指針設(shè)置為NULL,要么讓它指向合法的內(nèi)存。
指針釋放后未置空
有時(shí)指針在free或delete后未賦值 NULL,便會(huì)使人以為是合法的。別看free和delete的名字(尤其是delete),它們只是把指針?biāo)傅膬?nèi)存給釋放掉,但并沒有把指針本身干掉。此時(shí)指針指向的就是“垃圾”內(nèi)存。釋放后的指針應(yīng)立即將指針置為NULL,防止產(chǎn)生“野指針”。
指針操作超越變量作用域
不要返回指向棧內(nèi)存的指針或引用,因?yàn)闂?nèi)存在函數(shù)結(jié)束時(shí)會(huì)被釋放。
操作野指針是非常危險(xiǎn)的操作,應(yīng)該規(guī)避野指針的出現(xiàn):
初始化時(shí)置 NULL
指針變量一定要初始化為NULL,因?yàn)槿魏沃羔樧兞縿偙粍?chuàng)建時(shí)不會(huì)自動(dòng)成為NULL指針,它的缺省值是隨機(jī)的。
釋放時(shí)置 NULL
當(dāng)指針p指向的內(nèi)存空間釋放時(shí),沒有設(shè)置指針p的值為NULL。delete和free只是把內(nèi)存空間釋放了,但是并沒有將指針p的值賦為NULL。通常判斷一個(gè)指針是否合法,都是使用if語句測(cè)試該指針是否為NULL。
間接訪問操作符
通過一個(gè)指針訪問它所指向的地址的過程叫做間接訪問,或者叫解引用指針,這個(gè)用于執(zhí)行間接訪問的操作符是*。
注意:對(duì)一個(gè)int類型指針解引用會(huì)產(chǎn)生一個(gè)整型值,類似地,對(duì)一個(gè)float指針解引用會(huì)產(chǎn)生了一個(gè)float類型的值。
int arr[5]; int *p = * (&arr);int arr1[5][3] arr1 = int(*)[3]&arr1在指針聲明時(shí),* 號(hào)表示所聲明的變量為指針
在指針使用時(shí),* 號(hào)表示操作指針?biāo)赶虻膬?nèi)存空間
1)* 相當(dāng)通過地址(指針變量的值)找到指針指向的內(nèi)存,再操作內(nèi)存
2)* 放在等號(hào)的左邊賦值(給內(nèi)存賦值,寫內(nèi)存)
3)* 放在等號(hào)的右邊取值(從內(nèi)存中取值,讀內(nèi)存)
指針的步長
指針是一種數(shù)據(jù)類型,是指它指向的內(nèi)存空間的數(shù)據(jù)類型。指針?biāo)赶虻膬?nèi)存空間決定了指針的步長。指針的步長指的是,當(dāng)指針+1時(shí)候,移動(dòng)多少字節(jié)單位。
思考如下問題:
int a = 0xaabbccdd;unsigned int *p1 = &a;unsigned char *p2 = &a;//為什么*p1打印出來正確結(jié)果?printf("%x\n", *p1);//為什么*p2沒有打印出來正確結(jié)果?printf("%x\n", *p2);//為什么p1指針+1加了4字節(jié)?printf("p1 =%d\n", p1);printf("p1+1=%d\n", p1 + 1);//為什么p2指針+1加了1字節(jié)?printf("p2 =%d\n", p2);printf("p2+1=%d\n",?p2?+?1);指針的意義_間接賦值
間接賦值的三大條件
通過指針間接賦值成立的三大條件:
1)2個(gè)變量(一個(gè)普通變量一個(gè)指針變量、或者一個(gè)實(shí)參一個(gè)形參)
2)建立關(guān)系
3)通過 * 操作指針指向的內(nèi)存
如何定義合適的指針變量
void test(){ int b; int *q = &b; //0級(jí)指針 int **t = &q; int ***m = &t;}間接賦值:從0級(jí)指針到1級(jí)指針
int func1(){ return 10; }void func2(int a){ a = 100;}//指針的意義_間接賦值void test02(){ int a = 0; a = func1(); printf("a = %d\n", a); //為什么沒有修改? func2(a); printf("a = %d\n", a);}//指針的間接賦值void func3(int* a){ *a = 100;}void test03(){ int a = 0; a = func1(); printf("a = %d\n", a); //修改 func3(&a); printf("a = %d\n", a);}間接賦值:從1級(jí)指針到2級(jí)指針
void AllocateSpace(char** p){ *p = (char*)malloc(100); strcpy(*p, "hello world!");}void FreeSpace(char** p){ if (p == NULL){ return; } if (*p != NULL){ free(*p); *p = NULL; }}void test(){ char* p = NULL; AllocateSpace(&p); printf("%s\n",p); FreeSpace(&p); if (p == NULL){ printf("p內(nèi)存釋放!\n"); }}間接賦值的推論
用1級(jí)指針形參,去間接修改了0級(jí)指針(實(shí)參)的值。
用2級(jí)指針形參,去間接修改了1級(jí)指針(實(shí)參)的值。
用3級(jí)指針形參,去間接修改了2級(jí)指針(實(shí)參)的值。
用n級(jí)指針形參,去間接修改了n-1級(jí)指針(實(shí)參)的值。
指針做函數(shù)參數(shù)
指針做函數(shù)參數(shù),具備輸入和輸出特性:
輸入:主調(diào)函數(shù)分配內(nèi)存
輸出:被調(diào)用函數(shù)分配內(nèi)存
輸入特性
void fun(char *p /* in */){ //給p指向的內(nèi)存區(qū)域拷貝內(nèi)容 strcpy(p, "abcddsgsd");}void test(void){ //輸入,主調(diào)函數(shù)分配內(nèi)存 char buf[100] = { 0 }; fun(buf); printf("buf = %s\n", buf);}輸出特性
void fun(char **p /* out */, int *len){ char *tmp = (char *)malloc(100); if (tmp == NULL) { return; } strcpy(tmp, "adlsgjldsk"); //間接賦值 *p = tmp; *len = strlen(tmp);}void test(void){ //輸出,被調(diào)用函數(shù)分配內(nèi)存,地址傳遞 char *p = NULL; int len = 0; fun(&p, &len); if (p != NULL) { printf("p = %s, len = %d\n", p, len);??}字符串指針強(qiáng)化
字符串指針做函數(shù)參數(shù)
字符串基本操作
//字符串基本操作//字符串是以0或者'\0'結(jié)尾的字符數(shù)組,(數(shù)字0和字符'\0'等價(jià))void test01(){ //字符數(shù)組只能初始化5個(gè)字符,當(dāng)輸出的時(shí)候,從開始位置直到找到0結(jié)束 char str1[] = { 'h', 'e', 'l', 'l', 'o' }; printf("%s\n",str1); //字符數(shù)組部分初始化,剩余填0 char str2[100] = { 'h', 'e', 'l', 'l', 'o' }; printf("%s\n", str2); //如果以字符串初始化,那么編譯器默認(rèn)會(huì)在字符串尾部添加'\0' char str3[] = "hello"; printf("%s\n",str3); printf("sizeof str:%d\n",sizeof(str3)); printf("strlen str:%d\n",strlen(str3)); //sizeof計(jì)算數(shù)組大小,數(shù)組包含'\0'字符 //strlen計(jì)算字符串的長度,到'\0'結(jié)束 //那么如果我這么寫,結(jié)果是多少呢? char str4[100] = "hello"; printf("sizeof str:%d\n", sizeof(str4)); printf("strlen str:%d\n", strlen(str4)); //請(qǐng)問下面輸入結(jié)果是多少?sizeof結(jié)果是多少?strlen結(jié)果是多少? char str5[] = "hello\0world"; printf("%s\n",str5); printf("sizeof str5:%d\n",sizeof(str5)); printf("strlen str5:%d\n",strlen(str5)); //再請(qǐng)問下面輸入結(jié)果是多少?sizeof結(jié)果是多少?strlen結(jié)果是多少? char str6[] = "hello\012world"; printf("%s\n", str6); printf("sizeof str6:%d\n", sizeof(str6)); printf("strlen str6:%d\n", strlen(str6));}八進(jìn)制和十六進(jìn)制轉(zhuǎn)義字符:
在C中有兩種特殊的字符,八進(jìn)制轉(zhuǎn)義字符和十六進(jìn)制轉(zhuǎn)義字符,八進(jìn)制字符的一般形式是’\ddd’,d是0-7的數(shù)字。十六進(jìn)制字符的一般形式是’\xhh’,h是0-9或A-F內(nèi)的一個(gè)。八進(jìn)制字符和十六進(jìn)制字符表示的是字符的ASCII碼對(duì)應(yīng)的數(shù)值。
比如 :
‘\063’表示的是字符’3’,因?yàn)椤?’的ASCII碼是30(十六進(jìn)制),48(十進(jìn)制),63(八進(jìn)制)。
‘\x41’表示的是字符’A’,因?yàn)椤疉’的ASCII碼是41(十六進(jìn)制),65(十進(jìn)制),101(八進(jìn)制)。
字符串拷貝功能實(shí)現(xiàn)
//拷貝方法1void copy_string01(char* dest, char* source ){ for (int i = 0; source[i] != '\0';i++){ dest[i] = source[i]; }}//拷貝方法2void copy_string02(char* dest, char* source){ while (*source != '\0' /* *source != 0 */){ *dest = *source; source++; dest++; }}//拷貝方法3void copy_string03(char* dest, char* source){ //判斷*dest是否為0,0則退出循環(huán) while (*dest++ = *source++){}}//拷貝方法4//1)應(yīng)該判斷下傳入的參數(shù)是否為NULL//2)最好不要直接使用形參int copy_string04(char* dest, char* source){ if (dest == NULL){ return -1; } if (source == NULL){ return -2; } char* src = source; char* tar = dest; while (*tar++ = *src++){} return 0;}字符串反轉(zhuǎn)模型
void reverse_string(char* str){ if (str == NULL){ return; } int begin = 0; int end = strlen(str) - 1; while (begin < end){ //交換兩個(gè)字符元素 char temp = str[begin]; str[begin] = str[end]; str[end] = temp; begin++; end--; }}void test(){ char str[] = "abcdefghijklmn"; printf("str:%s\n", str); reverse_string(str); printf("str:%s\n", str);}字符串的格式化
sprintf
#include int sprintf(char *str, const char *format, ...);功能: 根據(jù)參數(shù)format字符串來轉(zhuǎn)換并格式化數(shù)據(jù),然后將結(jié)果輸出到str指定的空間中,直到 出現(xiàn)字符串結(jié)束符 '\0' 為止。參數(shù): str:字符串首地址 format:字符串格式,用法和printf()一樣返回值: 成功:實(shí)際格式化的字符個(gè)數(shù)??失敗:?-?1void test(){ //1. 格式化字符串 char buf[1024] = { 0 }; sprintf(buf, "你好,%s,歡迎加入我們!", "John"); printf("buf:%s\n",buf); memset(buf, 0, 1024); sprintf(buf, "我今年%d歲了!", 20); printf("buf:%s\n", buf); //2. 拼接字符串 memset(buf, 0, 1024); char str1[] = "hello"; char str2[] = "world"; int len = sprintf(buf,"%s %s",str1,str2); printf("buf:%s len:%d\n", buf,len); //3. 數(shù)字轉(zhuǎn)字符串 memset(buf, 0, 1024); int num = 100; sprintf(buf, "%d", num); printf("buf:%s\n", buf); //設(shè)置寬度 右對(duì)齊 memset(buf, 0, 1024); sprintf(buf, "%8d", num); printf("buf:%s\n", buf); //設(shè)置寬度 左對(duì)齊 memset(buf, 0, 1024); sprintf(buf, "%-8d", num); printf("buf:%s\n", buf); //轉(zhuǎn)成16進(jìn)制字符串 小寫 memset(buf, 0, 1024); sprintf(buf, "0x%x", num); printf("buf:%s\n", buf); //轉(zhuǎn)成8進(jìn)制字符串 memset(buf, 0, 1024); sprintf(buf, "0%o", num); printf("buf:%s\n", buf);}sscanf
#include int sscanf(const char *str, const char *format, ...);功能: 從str指定的字符串讀取數(shù)據(jù),并根據(jù)參數(shù)format字符串來轉(zhuǎn)換并格式化數(shù)據(jù)。參數(shù): str:指定的字符串首地址 format:字符串格式,用法和scanf()一樣返回值: 成功:實(shí)際讀取的字符個(gè)數(shù)??失敗:?-?1//1. 跳過數(shù)據(jù)void test01(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 //匹配第一個(gè)字符是否是數(shù)字,如果是,則跳過 //如果不是則停止匹配 sscanf("123456aaaa", "%*d%s", buf); printf("buf:%s\n",buf);}//2. 讀取指定寬度數(shù)據(jù)void test02(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 sscanf("123456aaaa", "%7s", buf); printf("buf:%s\n", buf);}//3. 匹配a-z中任意字符void test03(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 //先匹配第一個(gè)字符,判斷字符是否是a-z中的字符,如果是匹配 //如果不是停止匹配 sscanf("abcdefg123456", "%[a-z]", buf); printf("buf:%s\n", buf);}//4. 匹配aBc中的任何一個(gè)void test04(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 //先匹配第一個(gè)字符是否是aBc中的一個(gè),如果是,則匹配,如果不是則停止匹配 sscanf("abcdefg123456", "%[aBc]", buf); printf("buf:%s\n", buf);}//5. 匹配非a的任意字符void test05(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 //先匹配第一個(gè)字符是否是aBc中的一個(gè),如果是,則匹配,如果不是則停止匹配 sscanf("bcdefag123456", "%[^a]", buf); printf("buf:%s\n", buf);}//6. 匹配非a-z中的任意字符void test06(){ char buf[1024] = { 0 }; //跳過前面的數(shù)字 //先匹配第一個(gè)字符是否是aBc中的一個(gè),如果是,則匹配,如果不是則停止匹配 sscanf("123456ABCDbcdefag", "%[^a-z]", buf); printf("buf:%s\n", buf);}一級(jí)指針易錯(cuò)點(diǎn)
越界
void test(){ char buf[3] = "abc"; printf("buf:%s\n",buf);}指針疊加會(huì)不斷改變指針指向
void test(){ char *p = (char *)malloc(50); char buf[] = "abcdef"; int n = strlen(buf); int i = 0; for (i = 0; i < n; i++) { *p = buf[i]; p++; //修改原指針指向 } free(p);}返回局部變量地址
char *get_str(){ char str[] = "abcdedsgads"; //棧區(qū), printf("[get_str]str = %s\n", str); return str;}同一塊內(nèi)存釋放多次
void test(){ char *p = NULL; p = (char *)malloc(50); strcpy(p, "abcdef"); if (p != NULL) { //free()函數(shù)的功能只是告訴系統(tǒng) p 指向的內(nèi)存可以回收了 // 就是說,p 指向的內(nèi)存使用權(quán)交還給系統(tǒng) //但是,p的值還是原來的值(野指針),p還是指向原來的內(nèi)存 free(p); } if (p != NULL) { free(p); }}const使用
//const修飾變量void test01(){ //1. const基本概念 const int i = 0; //i = 100; //錯(cuò)誤,只讀變量初始化之后不能修改 //2. 定義const變量最好初始化 const int j; //j = 100; //錯(cuò)誤,不能再次賦值 //3. c語言的const是一個(gè)只讀變量,并不是一個(gè)常量,可通過指針間接修改 const int k = 10; //k = 100; //錯(cuò)誤,不可直接修改,我們可通過指針間接修改 printf("k:%d\n", k); int* p = &k; *p = 100; printf("k:%d\n", k);}//const 修飾指針void test02(){ int a = 10; int b = 20; //const放在*號(hào)左側(cè) 修飾p_a指針指向的內(nèi)存空間不能修改,但可修改指針的指向 const int* p_a = &a; //*p_a = 100; //不可修改指針指向的內(nèi)存空間 p_a = &b; //可修改指針的指向 //const放在*號(hào)的右側(cè), 修飾指針的指向不能修改,但是可修改指針指向的內(nèi)存空間 int* const p_b = &a; //p_b = &b; //不可修改指針的指向 *p_b = 100; //可修改指針指向的內(nèi)存空間 //指針的指向和指針指向的內(nèi)存空間都不能修改 const int* const p_c = &a;}//const指針用法struct Person{ char name[64]; int id; int age; int score;};//每次都對(duì)對(duì)象進(jìn)行拷貝,效率低,應(yīng)該用指針void printPersonByValue(struct Person person){ printf("Name:%s\n", person.name); printf("Name:%d\n", person.id); printf("Name:%d\n", person.age); printf("Name:%d\n", person.score);}//但是用指針會(huì)有副作用,可能會(huì)不小心修改原數(shù)據(jù)void printPersonByPointer(const struct Person *person){ printf("Name:%s\n", person->name); printf("Name:%d\n", person->id); printf("Name:%d\n", person->age); printf("Name:%d\n", person->score);}void test03(){ struct Person p = { "Obama", 1101, 23, 87 }; //printPersonByValue(p); printPersonByPointer(&p);}原文:https://blog.csdn.net/tichimi3375/article/details/104722295
【END】
如果您覺得本篇文章對(duì)您有幫助請(qǐng)轉(zhuǎn)發(fā)給更多的人
順手點(diǎn)一下“在看”也是對(duì)小編最大的支持
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的c6011取消对null指针的引用_C/C++学习笔记——C提高:指针强化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux跟踪内存块,在Linux程序中
- 下一篇: asp:dropdownlist如何去掉