C語言常見的指針筆試題解析
在我們學(xué)習(xí)指針之后,應(yīng)該在實(shí)際應(yīng)用中去理解和掌握它,畢竟實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),我們以后在找工作的過程中免不了會(huì)遇到與指針相關(guān)的試題,本篇文章可以幫助我們提前了解一些常見的指針考點(diǎn)。在學(xué)習(xí)這篇文章之前可以根據(jù)需要對(duì)指針進(jìn)行簡(jiǎn)要復(fù)習(xí)。
注:本篇文章所有代碼均在X86環(huán)境下運(yùn)行。
筆試題1
#include <stdio.h> int main() { int a[5] = { 1,2,3,4,5 }; int* pa = (int*)(&a + 1); //&a取的是整個(gè)數(shù)組的地址,&a的類型為數(shù)組指針類型int(*)[5],(int*)是將其強(qiáng)制轉(zhuǎn)換為int* 類型的指針,使其與pa的指針類型相同 printf("%d %d", *(a + 1), *(pa-1));//輸出結(jié)果為 2 5 return 0; }
解析:
int* pa = (int*)(&a+1
)中&a取的是整個(gè)數(shù)組的地址,(&a+1)
的類型為數(shù)組指針類型int(*)[5]
,指向的地址跳過一個(gè)a數(shù)組的大小,(int*)
是將其強(qiáng)制轉(zhuǎn)換為int* 類型的指針,使其與pa的指針類型相同。
由上圖可知*(a + 1)
得到的值為2,*(pa-1)
得到的值為5。
筆試題2
#include <stdio.h> struct stu { int num; char* pcname; short sdata; char ch[2]; short sarr[4]; }* ps;//p是一個(gè)結(jié)構(gòu)體指針變量 //已知,結(jié)構(gòu)體stu類型的變量大小是20個(gè)字節(jié) int main() { ps = (struct stu*)0x100000;//強(qiáng)制類型轉(zhuǎn)換為struct stu* printf("%p\n", ps + 0x1);//00100014 printf("%p\n", (unsigned long)ps + 0x1);//00100001 printf("%p\n", (unsigned long*)ps + 0x1);//00100004 return 0; }
解析:
本題考察的是指針±整數(shù)指針的變化,ps = (struct stu*)0x100000
中ps是結(jié)構(gòu)體指針變量,凡是放在指針變量中的都被當(dāng)成地址處理,0x100000被強(qiáng)制類型轉(zhuǎn)換為struct stu*
類型的指針,放在ps中,此時(shí)0x100000就被當(dāng)成地址處理。
ps + 0x1
中,0x1
是十六進(jìn)制數(shù)字1,轉(zhuǎn)換成十進(jìn)制也是1。所以ps+0x1
就是ps+1,意思是ps向后走struct stu*類型的字節(jié)大小,如 char*
類型的指針+1就是向后走1個(gè)字節(jié),因?yàn)?code>char占1個(gè)字節(jié);int*
類型的指針+1就是向后走4個(gè)字節(jié),因?yàn)?code>int類型占4個(gè)字節(jié)。而題中ps是struct stu*
類型,+1就是向后走20個(gè)字節(jié)。20轉(zhuǎn)換成十六進(jìn)制為0x14,0x100000+0x14=0x100014。
(unsigned long)ps + 0x1
是將結(jié)構(gòu)體指針變量ps強(qiáng)制類型轉(zhuǎn)換為unsigned long
類型,ps+1就是向后走1個(gè)字節(jié),unsigned long
是無符號(hào)長整型,整型+1就是+1,所以為0x100001。
(unsigned long*)ps + 0x1
是將無符號(hào)長整形ps強(qiáng)制類型轉(zhuǎn)換為unsigned long*
類型,ps+1就是向后走4個(gè)字節(jié),因?yàn)?code>unsigned long類型大小為4個(gè)字節(jié),最后為0x100004。
%p–輸出的是地址,0x表示數(shù)字是十六進(jìn)制,地址往往以十六進(jìn)制的形式輸出,在X86環(huán)境下,地址由4個(gè)字節(jié)組成,轉(zhuǎn)換為地址后,該代碼輸出結(jié)果為00100014 00100001 00100004。
筆試題3
#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);//輸出結(jié)果為 4 20000000 return 0; }
解析:
%x- - -以十六進(jìn)制輸出。
(int*)(&a + 1)
中&a
取得是整個(gè)數(shù)組的地址,它的類型為int(*)[4],&a+1
跳過一個(gè)數(shù)組的大小,即跳過16個(gè)字節(jié),再強(qiáng)制類型轉(zhuǎn)換為(int*)
。ptr[-1]
即*(ptr-1)
,所以輸出為4。
(int*)((int)a + 1)
中((int)a+1)
是將a強(qiáng)制類型轉(zhuǎn)換為int
類型后+1,a是數(shù)組名,代表著數(shù)組首元素的地址。將數(shù)組中的元素轉(zhuǎn)換成十六進(jìn)制后為a[]={0x00000001,0x00000002,0x00000003,0x00000004}
,VS中采用的是小端存儲(chǔ),在內(nèi)存中表示如下圖。
假設(shè)一個(gè)數(shù)的地址以十六進(jìn)制表示為0x00000015,強(qiáng)制類型轉(zhuǎn)換成整型之后為21,21+1=22,22轉(zhuǎn)換成十六進(jìn)制為0x00000016,所以強(qiáng)制類型轉(zhuǎn)換成整型之后再+1相當(dāng)于地址向后移動(dòng)了1個(gè)字節(jié)。因此((int)a + 1)
表示a的地址向后移動(dòng)了1個(gè)字節(jié),所以在*ptr2
中,從a的位置向后讀4個(gè)字節(jié),即00000002,因?yàn)槭切《舜鎯?chǔ),在讀取時(shí)就以小端的方式讀,所以結(jié)果為20000000。
筆試題4
#include <stdio.h> int main() { int a[3][2] = { (0,1),(2,3),(4,5) }; //逗號(hào)表達(dá)式:按照從左到右依次計(jì)算,整個(gè)表達(dá)式的結(jié)果為最后一個(gè)表達(dá)式的值。 //int a[3][2] = { 1,3,5 }; int* p; p = a[0]; //a[0],在二維數(shù)組中,把每一行都看成一維數(shù)組的時(shí)候,a[0]是第一行的數(shù)組名,數(shù)組名表示首元素的地址,即a[0]=&a[0][0] printf("%d", p[0]);//輸出結(jié)果為 1 //p等于a[0],p[0]等于*(&a[0][0]+0),即數(shù)組第一個(gè)元素的值。 return 0; }
解析:
int a[3][2] = { (0,1),(2,3),(4,5) }
中含有逗號(hào)表達(dá)式(逗號(hào)表達(dá)式:按照從左到右依次計(jì)算,整個(gè)表達(dá)式的結(jié)果為最后一個(gè)表達(dá)式的值)。計(jì)算之后得到數(shù)組為int a[3][2] = { 1,3,5 }
,p = a[0]
將a[0]賦值給整型指針變量p,在二維數(shù)組中,把每一行都看成一維數(shù)組的時(shí)候,a[0]表示第一行的數(shù)組名,數(shù)組名表示首元素的地址,即a[0]=&a[0][0]。p[0]
中p等于a[0],p[0]等于*(p+0)
等于*(&a[0][0]+0)
,即數(shù)組第一個(gè)元素的值1。
筆試題5
#include <stdio.h> int main() { int arr[5][5]; int(*p)[4]; p = arr; printf("%p %d\n", &p[4][2] - &arr[4][2], &p[4][2] - &arr[4][2]);//輸出結(jié)果為 FFFFFFFC -4 return 0; }
解析:
本題考察的是指針-指針。int(*p)[4]
是數(shù)組指針類型,p = arr
中arr
是二維數(shù)組數(shù)組名,數(shù)組名就是首元素的地址,在二維數(shù)組中首元素地址就是第一行的地址即&arr[0]
,&arr[0]
用int(*)[5]
類型數(shù)組指針接收,將int(*)[5]
類型賦給int(*)[4]
類型,可以編譯,雖然不會(huì)報(bào)錯(cuò)但是有警告,因?yàn)閜是int(*)[4]
類型,p+1
的時(shí)候向后走4個(gè)整型,具體可見下圖:
p[4][2]
可以寫成*(*(p+4)+2)
,位置如上圖所示,指針和指針相減的絕對(duì)值是元素之間的個(gè)數(shù),&p[4][2] - &arr[4][2]
是低地址-高地址,得到的是-4。
-4的原碼、反碼和補(bǔ)碼如下表所示:
原碼、反碼、補(bǔ)碼 | 值 |
---|---|
原碼 | 10000000000000000000000000000100 |
反碼 | 11111111111111111111111111111011 |
補(bǔ)碼 | 11111111111111111111111111111100 |
用%d打印的時(shí)候打印的是原碼。
%p–輸出地址,地址沒有原碼、反碼和補(bǔ)碼,在內(nèi)存中存的是補(bǔ)碼,因?yàn)榇蛴〉牡刂肥且粋€(gè)明確的數(shù)-4,打印的時(shí)候就把補(bǔ)碼當(dāng)成地址打印,補(bǔ)碼轉(zhuǎn)換成十六進(jìn)制就是FFFFFFFC。
筆試題6
#include <stdio.h> int main() { int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)(*(a + 1)); printf("%d %d", *(ptr1 - 1), *(ptr2 - 1));//輸出結(jié)果為 10 5 return 0; }
解析:
&a+1
跳過整個(gè)二維數(shù)組,然后強(qiáng)制類型轉(zhuǎn)換為(int*)類型,*((a+1))
中,a
是二維數(shù)組數(shù)組名,表示數(shù)組首元素的地址,在二維數(shù)組中,數(shù)組首元素的地址即第一行的地址,第一行的數(shù)組名可以用a[0]表示,*(a+1)
可以表示成*(&a[0]+1)
,&a[0]是int(*)[5]類型,+1之后到二維數(shù)組的第二行,所以(*(a+1))
就是a[1]
,a[1]就是第二行數(shù)組名,也表示第二行首元素的地址&a[1][0]
。
筆試題7
#include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa);//輸出結(jié)果為 at return 0; }
解析:
char* a[] = { "work","at","alibaba" }
中a[]
是字符指針數(shù)組,存的是字符串首字符的地址,char** pa
表示pa指向char*類型,char** pa = a
表示pa指向a,a是數(shù)組名,表示數(shù)組首元素的地址,此時(shí),pa指向數(shù)組中的第一個(gè)元素,pa++
表示pa跳過一個(gè)char*
類型,則pa此時(shí)指向數(shù)組中的第二個(gè)元素,*pa
取出數(shù)組中第二個(gè)元素的首地址,打印字符串。
筆試題8
#include <stdio.h> int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp);//輸出結(jié)果為POINT printf("%s\n", *-- *++cpp+3);//輸出結(jié)果為ER printf("%s\n", *cpp[-2]+3);//輸出結(jié)果為ST printf("%s\n", cpp[-1][-1]+1);//輸出結(jié)果為EW return 0; }
解析:
c
數(shù)組中存的是字符串首字符的地址,c+1
中c
是數(shù)組名,數(shù)組名表示數(shù)組首元素的地址,因?yàn)閿?shù)組c
里面的數(shù)據(jù)類型是char*
類型的,存放的是字符串首字符的地址,數(shù)組首元素就是字符的地址,地址的地址要用二級(jí)指針來接收,所以用char**
,c+1
之后指向數(shù)組c
中第二個(gè)元素的地址,c+2
指向第三個(gè)元素的地址,c+3
指向第四個(gè)元素的地址,而c
數(shù)組中的元素都是字符串首字符的地址。數(shù)組cp
中的元素類型都是char**
類型,要用char***
類型接收。
cpp
指向的類型是 char**
,char*** cpp = cp
,cp
是數(shù)組名,表示數(shù)組首元素的地址,剛開始三級(jí)指針 cpp
指向 cp
數(shù)組中的 c+3
的地址,**++cpp
中 ++cpp
之后指向c+2
的地址,第一次解引用 *++cpp
(解引用操作得到的是指針指向地址中所存放的內(nèi)容),得到的是c+2
,c+2
指向的是數(shù)組c
中第三個(gè)元素的地址,再次解引用之后得到的是c
數(shù)組中存放字符串POINT
首字符的地址,打印字符串得到POINT。
*-- *++cpp+3
中 +
的優(yōu)先級(jí)比++
、--
、*
的優(yōu)先級(jí)低,所以+3
放在最后計(jì)算, ++cpp
執(zhí)行后cpp
指向cp
數(shù)組c+1
的地址,解引用得到c+1
,--(c+1)
執(zhí)行后cp
數(shù)組中元素c+1
變?yōu)?code>c,c
指向數(shù)組c
中第一個(gè)元素的地址,解引用之后得到字符串ENTER
首字符的地址,+3向后移動(dòng)3個(gè)字節(jié),得到ER。
*cpp[-2]+3
等于*(*(cpp-2))+3
,(cpp-2)
表示指向數(shù)組cp
中c+3
的地址,但是cpp
指向的位置沒有發(fā)生改變,*(cpp-2)
表示解引用之后得到c+3
,c+3
指向數(shù)組c
中第四個(gè)元素的地址,再次解引用得到的是字符串FIRST
首字符的地址,+3向后移動(dòng)三個(gè)字節(jié),打印ST。
cpp[-1][-1]+1
等于*(*(cpp-1)-1)+1
,因?yàn)?code>cpp指向的位置沒有發(fā)生改變,所以cpp-1
指向數(shù)組cp
中c+2
的地址,*(cpp-1)
后得到c+2
,*(cpp-1)-1
等于(c+2)-1
為c+1
,c+1
指向數(shù)組c
中第二個(gè)元素的地址,*(*(cpp-1)-1)
執(zhí)行后得到字符串NEW
首字符的地址,+1向后移動(dòng)1個(gè)字節(jié)指向E,打印字符串為EW。
到此這篇關(guān)于C語言常見的指針筆試題解析的文章就介紹到這了,更多相關(guān)C語言指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言 自增自減運(yùn)算的區(qū)別詳解及實(shí)例
這篇文章主要介紹了C語言中的++a和a++的區(qū)別詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05C語言實(shí)現(xiàn)可保存的動(dòng)態(tài)通訊錄的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的可保存的動(dòng)態(tài)通訊錄,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下2022-07-07C++實(shí)現(xiàn)LeetCode(76.最小窗口子串)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(76.最小窗口子串),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07詳解state狀態(tài)模式及在C++設(shè)計(jì)模式編程中的使用實(shí)例
這篇文章主要介紹了state狀態(tài)模式及在C++設(shè)計(jì)模式編程中的使用實(shí)例,在設(shè)計(jì)模式中策略用來處理算法變化,而狀態(tài)則是透明地處理狀態(tài)變化,需要的朋友可以參考下2016-03-03C++實(shí)現(xiàn)LeetCode(96.獨(dú)一無二的二叉搜索樹)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(96.獨(dú)一無二的二叉搜索樹),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07MySQL系列教程之使用C語言來連接數(shù)據(jù)庫
c語言操作Mysql數(shù)據(jù)庫,主要就是為了實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的增、刪、改、查等操作,下面這篇文章主要給大家介紹了關(guān)于MySQL系列教程之使用C語言來連接數(shù)據(jù)庫的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09c語言詳解動(dòng)態(tài)內(nèi)存分配及常見錯(cuò)誤的解決
給數(shù)組分配多大的內(nèi)存空間?你是否和初學(xué)C時(shí)的我一樣,有過這樣的疑問。這一期就來聊一聊動(dòng)態(tài)內(nèi)存的分配,讀完這篇文章,你可能對(duì)內(nèi)存的分配有一個(gè)更好的理解2022-04-04