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