=========================================================================
相關(guān)代碼gitee自取:C語言學(xué)習(xí)日記: 加油努力 (gitee.com)
?=========================================================================
接上期:
學(xué)C的第二十六天【指針的進(jìn)階(二)】_高高的胖子的博客-CSDN博客
?=========================================================================
? ? ? ? ? ? ? ? ? ? ?
復(fù)習(xí)鞏固:
數(shù)組名:
數(shù)組名是數(shù)組首元素的地址,
但是有兩個例外:
? ? ? ? ? ? ? ??文章來源地址http://www.zghlxwxcb.cn/news/detail-578541.html
1 . sizeof(數(shù)組名) :這里的數(shù)組名表示整個數(shù)組,計算的是整個數(shù)組的大小,單位是字節(jié)
? ? ? ? ? ? ? ??
2 . &數(shù)組名 :這里的數(shù)組名表示整個數(shù)組,取出的是整個數(shù)組的地址
? ? ? ? ? ? ? ? ??
9. 指針和數(shù)組筆試題解析
補(bǔ)充(回顧):
sizeof()
?是一個運(yùn)算符,而?strlen()
?是一個函數(shù)。sizeof()
?計算的是變量或類型所占用的內(nèi)存字節(jié)數(shù),而?strlen()
?計算的是字符串中字符的個數(shù)。sizeof()
?可以用于任何類型的數(shù)據(jù),而?strlen()
?只能用于以空字符 '\0' 結(jié)尾的字符串。sizeof()
計算字符串的長度,包含末尾的 '\0',strlen()
計算字符串的長度,不包含字符串末尾的 '\0'。? ? ? ? ? ? ? ? ??
(1). 一維數(shù)組相關(guān)題:
? ? ? ? ? ?
對應(yīng)代碼:
#include <stdio.h> int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); //特殊情況,這里的a為整個數(shù)組,為4*4=16字節(jié) printf("%d\n", sizeof(a + 0)); //數(shù)組名a是首元素地址,a+0還是首元素地址,地址的大小為4/8字節(jié) printf("%d\n", sizeof(*a)); //數(shù)組名a為首元素地址,*a就是首元素,大小是4個字節(jié) printf("%d\n", sizeof(a + 1)); //數(shù)組名是首元素地址。a+1是第二個元素地址,為4/8字節(jié) printf("%d\n", sizeof(a[1])); //a[1]是數(shù)組的第二個元素,為4個字節(jié) printf("%d\n", sizeof(&a)); //&a是整個數(shù)組的地址,數(shù)組地址也只是個地址,為4/8字節(jié) printf("%d\n", sizeof(*&a)); //解引用整個數(shù)組的地址,相當(dāng)于sizeof(a),為16個字節(jié) printf("%d\n", sizeof(&a + 1)); //相當(dāng)于跳過了整個數(shù)組,雖然跳過了整個數(shù)組,&a+1依然是個地址,為4/8個字節(jié) printf("%d\n", sizeof(&a[0])); //取數(shù)組首元素的地址,為4/8個字節(jié) printf("%d\n", sizeof(&a[0] + 1)); //&a[0]是首元素地址,&a[0]+1就是第二個元素的地址,是地址就為4/8個字節(jié) return 0; }
? ? ? ? ? ? ? ??
(2). 字符數(shù)組和指針相關(guān)題:
(2.1). 明確賦值的字符數(shù)組(無結(jié)束符\0):
? ? ? ? ? ? ? ??
對應(yīng)代碼:
char arr[] = { 'a','b','c','d','e','f' }; //六個字符的字符數(shù)組,沒有結(jié)束符\0 printf("%d\n", sizeof(arr)); //數(shù)組名單獨(dú)放在sizeof中,這里的arr為整個數(shù)組,計算的是整個數(shù)組大小,為6個字節(jié) -- char[6] printf("%d\n", sizeof(arr + 0)); //arr表示數(shù)組首元素地址,arr+0還是數(shù)組首元素地址,是地址就為4/8個字節(jié) -- char* printf("%d\n", sizeof(*arr)); //arr表示數(shù)組首元素地址,*arr就是首元素,為1個字節(jié) -- char printf("%d\n", sizeof(arr[1])); //arr[1]就是第二個元素,為1個字節(jié) -- char printf("%d\n", sizeof(&arr)); //&arr是整個數(shù)組的地址,數(shù)組地址也是地址,是地址就為4/8個字節(jié) printf("%d\n", sizeof(&arr + 1)); //&arr+1是跳過整個數(shù)組后的地址,是地址就為4/8個字節(jié) printf("%d\n", sizeof(&arr[0] + 1)); //為第二個元素的地址,為4/8個字節(jié) printf("%d\n", strlen(arr)); //因?yàn)樽址麛?shù)組arr中沒有\(zhòng)0,所以在求字符串長度時, //會一直往后數(shù)到未知的\0為止,是不確定的,所以會產(chǎn)生隨機(jī)值 printf("%d\n", strlen(arr + 0)); //arr+0是首元素地址,所以和上面的一樣會產(chǎn)生隨機(jī)值 printf("%d\n", strlen(*arr)); //arr是數(shù)組首元素地址,*arr是數(shù)組的元素,是 'a' ,ACSII碼為97, //所以strlen會從 '97' 的地址開始統(tǒng)計字符串長度,為非法訪問內(nèi)存 printf("%d\n", strlen(arr[1])); //arr[1]為數(shù)組首元素,即 'a' ,所以和上面一樣為非法訪問內(nèi)存 printf("%d\n", strlen(&arr)); //&arr是數(shù)組地址,數(shù)組地址和數(shù)組首元素的地址,兩者的值是一樣的, //數(shù)組地址類型char*[6],會強(qiáng)制轉(zhuǎn)化為char*類型,所以依然從數(shù)組的首元素開始向后統(tǒng)計,結(jié)果為隨機(jī)值 printf("%d\n", strlen(&arr + 1)); //&arr+1為跳過這個數(shù)組,所以從這個數(shù)組后的地址開始統(tǒng)計,結(jié)果為隨機(jī)值 printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]+1是第二個元素的地址,往后統(tǒng)計直到遇到\0,結(jié)果為隨機(jī)值
? ? ? ? ? ? ? ? ?
(2.2). 未明確賦值的字符數(shù)組(有結(jié)束符\0):
? ? ? ? ? ? ??
對應(yīng)代碼:
char arr[] = "abcdef"; //未明確定義的字符數(shù)組,有結(jié)束符\0 // {a b c d e f \0} printf("%d\n", sizeof(arr)); //sizeof(數(shù)組名):計算整個數(shù)組的大小,為7個字節(jié),包含\0 -- char [7] printf("%d\n", sizeof(arr + 0)); //arr+0是首元素的地址,是地址就是4/8個字節(jié) -- char* printf("%d\n", sizeof(*arr)); //*arr為數(shù)組首元素,為1個字節(jié) -- char printf("%d\n", sizeof(arr[1])); //arr[1]為第二個元素,為1個字節(jié) -- char printf("%d\n", sizeof(&arr)); //&arr是數(shù)組的地址,是地址就是4/8個字節(jié) -- char* [7] printf("%d\n", sizeof(&arr + 1)); //&arr為跳過arr這個數(shù)組后的地址,是地址就是4/8個字節(jié) -- 隨機(jī)地址 printf("%d\n", sizeof(&arr[0] + 1)); //&arr[0]+1是第二個元素的地址,是地址就是4/8個字節(jié) -- char* printf("%d\n", strlen(arr)); //統(tǒng)計到\0為止,即 a b c d e f ,所以結(jié)果為6 printf("%d\n", strlen(arr + 0)); //和上題一樣,都是從數(shù)組首元素開始,\0結(jié)束,所以結(jié)果為6 printf("%d\n", strlen(*arr)); //strlen應(yīng)該接收一個地址,從這個地址向后計算長度, //這里接收的是數(shù)組首元素字符'a'(char),不是地址,所以會報錯 printf("%d\n", strlen(arr[1])); //這里和上面一樣接收的也是數(shù)組首元素字符'a',不是地址,會報錯 printf("%d\n", strlen(&arr)); //接收數(shù)組指針char*[7],但是會被強(qiáng)制轉(zhuǎn)化為const char*類型, //值還是首元素的地址,所以統(tǒng)計的長度還是6 printf("%d\n", strlen(&arr + 1)); //&arr+1是跳過這個數(shù)組后的一個地址,從這個地址往后不知道什么時候后有\(zhòng)0,所以結(jié)果為隨機(jī)值 printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]+1為數(shù)組的第二個元素地址,從后計算長度直到\0,所以結(jié)果為5
? ? ? ? ? ? ??
(2.3).?未明確賦值的字符指針(有結(jié)束符\0):
? ? ? ? ? ? ?
對應(yīng)代碼:
char* p = "abcdef"; //這樣寫是把字符串的首字符地址放入指針中,即‘a(chǎn)’的地址 printf("%d\n", sizeof(p)); //p為一級指針,存放的是地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(p + 1)); //p+1是字符'b'的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(*p)); //相當(dāng)于‘b’的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(p[0])); //p[0]相當(dāng)于*(p+0),即*p,為字符‘a(chǎn)’,為1個字節(jié) printf("%d\n", sizeof(&p)); //取出一級指針p的地址,相當(dāng)于p的二級指針,存放的是指針p的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(&p + 1)); //相當(dāng)于指針p地址的后面一個地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(&p[0] + 1)); //相當(dāng)于a的地址的后一個地址,即b的地址,是地址就是4/8個字節(jié) printf("%d\n", strlen(p)); //指針p存放的是‘a(chǎn)’的地址,所以從a的地址往后數(shù),所以長度為6 printf("%d\n", strlen(p + 1)); //p+1是‘b’的地址,所以長度為5 printf("%d\n", strlen(*p)); //*p是字符‘a(chǎn)’,strlen的參數(shù)應(yīng)該是const char*指針,所以會報錯 printf("%d\n", strlen(p[0])); //字符串可以理解為一個字符數(shù)組,所以p[0]相當(dāng)于字符‘a(chǎn)’,會報錯 printf("%d\n", strlen(&p)); //&是一級指針p的地址,從該地址往后數(shù)直到\0,所以結(jié)果為隨機(jī)值 printf("%d\n", strlen(&p + 1)); //從一級指針的地址的后一個地址開始數(shù)直到\0,結(jié)果為隨機(jī)值 printf("%d\n", strlen(&p[0] + 1)); //相當(dāng)于a的地址的后一個地址,即b的地址,直到\0,所以結(jié)果為5
? ? ? ? ? ? ? ??
(3). 二維數(shù)組相關(guān)題:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
//二維數(shù)組 int a[3][4] = { 0 }; //創(chuàng)建三行四列的二維數(shù)組 printf("%d\n", sizeof(a)); //三行四列每個4個字節(jié),所以一個是3*4*4=48字節(jié) printf("%d\n", sizeof(a[0][0])); //求該二維數(shù)組0行0列的元素大小,為4個字節(jié) printf("%d\n", sizeof(a[0])); //a[0]是二維數(shù)組的第一個元素地址,即第一行地址(第一行數(shù)組的數(shù)組名) //相當(dāng)于把數(shù)組名放入sizeof中,所以計算的是整個數(shù)組的大小,為16個字節(jié) printf("%d\n", sizeof(a[0] + 1)); //這里a[0]不是單獨(dú)放在sizeof中的,所以是第一行數(shù)組的首元素地址,即a[0][0]地址 //所以a[0]+1是第一行第二個元素的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(*(a[0] + 1))); //相當(dāng)于解引用第一行數(shù)組的第二個元素地址,元素是int類型,為4個字節(jié) printf("%d\n", sizeof(a + 1)); //a是二維數(shù)組首元素地址,即第一行的地址 -- int(*)[4] //a+1就是第二行的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(*(a + 1))); //a+1是第二行地址--int(*)[4],*(a+1)解引用后是4個int類型的元素 //為16個字節(jié) printf("%d\n", sizeof(&a[0] + 1)); //&a[0]是二維數(shù)組第一行數(shù)組地址--int(*)[4] //&a[0] + 1是第二行的地址,是地址就是4/8個字節(jié) printf("%d\n", sizeof(*(&a[0] + 1))); //&a[0] + 1是第二行的地址,解引用后是4*4=16個字節(jié) printf("%d\n", sizeof(*a)); //*a是解引用二維數(shù)組首元素地址,即第一行地址,為4*4=16個字節(jié) printf("%d\n", sizeof(a[3])); //a[3]看起來是越界了,但sizeof()運(yùn)行時是不會訪問內(nèi)存的(類型屬性),只看類型 //這里a[3]的類型是int[4],為4*4=16個字節(jié) //表達(dá)式都有兩個屬性:1.值屬性 2.類型屬性 //代碼運(yùn)行時:編譯+鏈接 --> 可執(zhí)行程序 --> 運(yùn)行 --> 結(jié)果 //sizeof是在 編譯 時就已經(jīng)有結(jié)果了(類型屬性),根據(jù)類型為16個字節(jié) //a[3]是在 運(yùn)行 是才有結(jié)果(值屬性),根據(jù)運(yùn)行結(jié)果應(yīng)該為數(shù)組越界報錯
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
10. 指針筆試題
(1). 筆試題一:
? ? ? ? ? ? ?
對應(yīng)代碼:
#include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); //&a+1: 跳過該數(shù)組后的地址,再強(qiáng)制轉(zhuǎn)化成int*類型,方便后面移動時是4個字節(jié) printf("%d,%d", *(a + 1), *(ptr - 1)); //*(a+1): 是數(shù)組的第二個元素 -- 2 //*(ptr-1): ptr剛好在整個數(shù)組后,ptr-1后指針在 5 的地址位置,所以解引用后是5 return 0; }
? ? ? ? ? ? ? ??
(2). 筆試題二:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> //結(jié)構(gòu)體的大小是20個字節(jié)(x86) struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }* p = (struct Test*)0x100000;//結(jié)構(gòu)體指針變量 //假設(shè)p 的值為0x100000。 如下表表達(dá)式的值分別為多少? //已知,結(jié)構(gòu)體Test類型的變量大小是20個字節(jié) int main() { printf("%p\n", p + 0x1); //結(jié)構(gòu)體指針 +1 跳過一個結(jié)構(gòu)體(20個字節(jié)) //所以 結(jié)果 == 0x100000 + 0x14(20的十六進(jìn)制數(shù)) // == 0x100014 printf("%p\n", (unsigned long)p + 0x1); //把原來的結(jié)構(gòu)體指針的類型強(qiáng)制轉(zhuǎn)化為(unsigned long)整數(shù), //相當(dāng)于普通的數(shù)值計算,+1 就是 +1 // 結(jié)果 == 0x100000 + 0x1 == 0x100001 printf("%p\n", (unsigned int*)p + 0x1); //轉(zhuǎn)化為無符號整型指針,所以每次跳過4個字節(jié)(x86) // 結(jié)果 == 0x100000 + 0x4 == 0x100004 return 0; }
? ? ? ? ? ? ? ??
(3). 筆試題三:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> int main() { int a[4] = { 1, 2, 3, 4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2); //ptr1[-1] 等價于 *(ptr1-1) //%x : 以十六進(jìn)制打印 return 0; }
? ? ? ? ? ? ? ??
(4). 筆試題四:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> #include <stdio.h> int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; //注:這里用的是(),而不是{},()表達(dá)式的結(jié)果是最后一個 //所以a的實(shí)際情況是:{1,3,5,0,0,0} int* p; p = a[0]; //a[0]是二維數(shù)組的第一行第一列的地址,相當(dāng)于a[0][0] printf("%d", p[0]); //p[0] 相當(dāng)于 *(p+0),即*p -- *a[0][0],解引用后為 1 return 0; }
? ? ? ? ? ? ? ??
(5). 筆試題五:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> int main() { int a[5][5]; int(*p)[4]; p = a; //地址-地址 得到的是兩地址間的元素個數(shù) printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); //%d: //這里是小地址-大地址,兩個相差4,又因?yàn)橐?d打印 //所以是 -4 (把補(bǔ)碼轉(zhuǎn)換為原碼后打?。? //%p: //以 %p 打印的話,就不考慮無符號和有符號了,直接以補(bǔ)碼打?。? //-4是負(fù)數(shù),內(nèi)存中存的是補(bǔ)碼: //原碼:10000000000000000000000000000100 //反碼:11111111111111111111111111111011 //補(bǔ)碼:1111 1111 1111 1111 1111 1111 1111 1100 -- 內(nèi)存中存儲的-4 // F F F F F F F C return 0; }
? ? ? ? ? ? ? ??
(6). 筆試題六:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> int main() { //二維數(shù)組: int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //第一行: 1 2 3 4 5 第二行: 6 7 8 9 10 int* ptr1 = (int*)(&aa + 1); //&aa+1:跳過整個二維數(shù)組后的當(dāng)前位置的地址(10后面的位置) int* ptr2 = (int*)(*(aa + 1)); //aa+1:跳過二維數(shù)組的首元素(第一行),指向第二行首元素地址 //*(aa+1) 相當(dāng)于 *aa[1][0],解引用后拿到第二行的首元素地址(6的位置) printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //ptr1-1:“10”后面的地址-1,指向了10的地址 //ptr2-1:“6”的前一個地址,指向5的地址 return 0; }
? ? ? ? ? ? ? ??
(7). 筆試題七:
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> #include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; //相當(dāng)于用三個字符指針分別存放三個字符串: //char* p1 = "work" -- 存放w的地址 //char* p2 = "at" -- 存放a的地址 //char* p3 = "alibaba" -- 存放a的地址 char** pa = a; //存放a的首元素"p1"的地址 pa++;//指向"p2"的地址 printf("%s\n", *pa); //*pa:解引用指針p2的地址,p2指向"a"的地址,以%s打印為 at return 0; }
? ? ? ? ? ? ? ??
(8). 筆試題八:
文章來源:http://www.zghlxwxcb.cn/news/detail-578541.html
? ? ? ? ? ? ? ??
對應(yīng)代碼:
#include <stdio.h> int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; //相當(dāng)于用四個字符指針分別存放四個字符串: //(下標(biāo)為0)char* p1 = "ENTER" -- 存放“E”的地址 //(下標(biāo)為1)char* p2 = "NEW" -- 存放“N”的地址 //(下標(biāo)為2)char* p3 = "POINT" -- 存放“P”的地址 //(下標(biāo)為3)char* p4 = "FIRST" -- 存放“F”的地址 char** cp[] = { c + 3,c + 2,c + 1,c }; //相當(dāng)于用四個二級字符指針分別存放四個一級指針: //(下標(biāo)為0)char** pp1 = c+3 = &p4 -- 存放一級指針p4的地址 //(下標(biāo)為1)char** pp2 = c+2 = &p3 -- 存放一級指針p3的地址 //(下標(biāo)為2)char** pp3 = c+1 = &p2 -- 存放一級指針p2的地址 //(下標(biāo)為3)char** pp4 = c = &p1 -- 存放一級指針p1的地址 char*** cpp = cp; //三級指針存放二級指針cp首元素的地址,即pp1地址 printf("%s\n", **++cpp); //cpp指向pp1地址,++后指向pp2地址, //第一次*得到pp2內(nèi)容:p3的指針(地址) //第二次*得到p3內(nèi)容:“P”的地址 //所以打印 "POINT" //因?yàn)?+,此時cpp已經(jīng)指向了pp2,不是pp1 printf("%s\n", *-- * ++cpp + 3); //先進(jìn)行++,cpp指向pp3 //再進(jìn)行第一次*:得到pp3內(nèi)容 -- p2的指針(地址) //再--:p2-1 ,指向p1 //再進(jìn)行第二次*:得到p1內(nèi)容 -- “E”的地址("ENTER") //最后進(jìn)行+3:“E”的地址+3,指向“ENTER”第二個“E” //所以打印"ER" //因?yàn)?+,此時cpp已經(jīng)指向了pp3,不是pp2 printf("%s\n", *cpp[-2] + 3); //cpp[-2] 相當(dāng)于 *(cpp-2) //所以題目可以寫成:**(cpp-2)+3 //先進(jìn)行 cpp-2 : 從指向pp3轉(zhuǎn)為指向pp1 //再進(jìn)行第一次*:得到pp1內(nèi)容 -- p4的指針(地址) //再進(jìn)行第二次*:得到p4的內(nèi)容 -- “F”的地址(“FIRST”) //最后+3:指向“FIRST”的“S”的地址 //所以打印“ST” //此時cpp還是指向pp3 printf("%s\n", cpp[-1][-1] + 1); //cpp[-1] 相當(dāng)于 *(cpp-1) //所以題目可以轉(zhuǎn)化為:*(cpp-1)[-1]+1 //再轉(zhuǎn)化:*(*(cpp-1)-1)+1 //先進(jìn)行cpp-1:cpp從pp3轉(zhuǎn)為指向pp2 //再進(jìn)行*:得到pp2內(nèi)容 -- p3的指針(地址) //再進(jìn)行-1:p3-1 -- 得到p2的地址 //再進(jìn)行*:得到p2的內(nèi)容 -- “N”的地址(“NEW”) //最后+1:指向“NEW”的"E" //所以打印“EW” return 0; }
到了這里,關(guān)于學(xué)C的第二十七天【指針的進(jìn)階(三)】的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!