指標進階(四)(跑路人筆記)

2022-01-09 13:00:22

前言

本次對為面試題題量不大有一些難大家可以試試.
廢話: 寫的有點累就不寫廢話了.

筆試題

答案我都放在了程式碼塊裡在最底部.

第一題

int main() 
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//程式的結果是什麼?
//答 2 5

第一個答案a為首元素地址+1後解除參照是第二個元素的地址
第二個答案中&a+1是跳過了整個陣列所以ptr指向的是5後的地址又因為ptr是int*型別-1回跳了4個位元組就指向了5

第二題

//由於還沒學習結構體,這裡告知結構體的大小是20個位元組 
struct Test
{
	int Num; 
	char *pcName;
	short sDate; 
	char cha[2]; short sBa[4]; 
}*p; 
//假設p 的值為0x100000。 如下表表示式的值分別為多少? 
//已知,結構體Test型別的變數大小是20個位元組
int main()
{
	printf("%p\n", p + 0x1); 
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0; 
}
//答案: 0x100014 0x100001 0x100004

第一個答案: 結構題的大小為20個位元組我們建立的結構題可以類似於關鍵字在指標是也奏效我們的結構體指標可以直接跳過20個位元組通過16進位制列印就是答案
第二個答案: 因為對p強制型別轉換成了整數(泛意)所以加上16進位制的1就和正常整數加1一樣通過地址的形式列印出來就是答案
第三個答案: 與第二個類似我們的指標是根據型別進行跳躍的我們unsigned int*型別的指標一次跳動四個位元組如答案所示.
小結: 本次題目主要考察陌生型別的程式碼我們的處理能力.

第三題

//%x是對十六進位制的列印   %#x可以在列印時增加字首0x
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); 
	return 0; 
}
//答案: 4 2000000

第一個答案: 與第一題的相似我們只需知道解除參照操作符[X]可以理解為*(ptr+X)所以ptr[-1]相當於*(ptr1-1) 所以為4
第二個答案: 比較複雜涉及到我們使用編譯器的大小端儲存問題俺使用的是VS2019為小端儲存我們以小端儲存(不知道大小端的下面有連線)為例:
https://blog.csdn.net/qq_61434711/article/details/121798086?spm=1001.2014.3001.5501

  1. 我們將a強制型別轉換成int那麼我們的(int)a+1也就是以我們a的儲存地址加上了一個位元組後賦給了ptr2
  2. 這裡我們用圖片講解:
    ![[Pasted image 20220108165432.png]]

小端儲存將1按照16進位制儲存成01000000 當我們讀取的時候就反過來00000001來使用而ptr2這個指標因為只跳動了一個位元組所以他指向的內容變成了00000002當我們讀取的時候翻過來就出現了答案20000000.

第四題

#include <stdio.h>
int main()
{ 
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int *p;
	p = a[0];
	printf( "%d", p[0]); return 0;

}
//答案: 1

解析: 考驗逗號表示式小括號內的為逗號表示式(0,1) 的結果就是1 所以相當於
int a[3][2] = {1,3,5};因為a是二維陣列a[0]表示a第一行的元素首元素的地址所以p[0]其實就是a陣列首行首列的元素1.

第五題

int main() 
{ 
	int a[5][5];  
	int(*p)[4];   
	p = a; 
	printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); 
	return 0; 
}
//FFFFFFFC   -4

倆地址相減的值為他倆之間的元素個數(可為負)
第一問: p為陣列指標它指向4個元素的陣列可作為二維陣列使用相當於一行有四列的二維陣列我們為了描述清晰我還是畫圖吧:
![[Pasted image 20220108184556.png]]

我把這兩個元素對應的位置都畫在了圖上這兩個記憶體指向頭部(如下圖)
![[Pasted image 20220108184959.png]]

兩者之間的元素又因為&p[4][2]為小地址小地址減去大地址為負數及-4
答案一 : 是以地址的形式列印-4 我們儲存-4以二補數儲存: 11111111111111111111111111111100
再以地址形勢列印: FFFFFFFC
正常整形形勢 : -4

第六題

int main()
{ 
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
	int *ptr1 = (int *)(&aa + 1);   
	int *ptr2 = (int *)(*(aa + 1));   
	printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));  
	return 0; 
}
//答案: 10 5

第一個答案: &aa得到的是整個陣列的地址+1就是跳過了整個陣列所以ptr1指向陣列後第一個元素-1後就指向10.
第二個答案: 我們對二維陣列解除參照時為了精準到元素是要兩次解除參照的, 如果只一次解除參照就會得到行陣列的首元素地址+1就跳過一行所以第二個答案是5

對第二個答案的詳解: 對於二維陣列來說哪aa舉例我們要使用它時格式為aa[1][2] = 8其實aa[1][2]*(*(aa+1)+2) 而我們ptr2中*(aa+1)就是跳過了一行所以我們的ptr2就指向了第二行的首元素及指向6所以第二個答案為5.

第七題

#include <stdio.h>
int main() 
{
	char *a[] = {"work","at","alibaba"};//指標陣列
	char**pa = a;
	pa++;
	printf("%s\n", *pa); 
	return 0;
}
//答案: at

解析: a作為指標陣列每個元素都指向一個字串二級指標pa指向a的首元素pa++後指向第二個元素及at.

第八題

int main()
{
	char *c[] = {"ENTER","NEW","POINT","FIRST"};
	char**cp[] = {c+3,c+2,c+1,c};
	char***cpp = cp;
	printf("%s\n", **++cpp); 
	printf("%s\n", *--*++cpp+3); 
	printf("%s\n", *cpp[-2]+3); 
	printf("%s\n", cpp[-1][-1]+1); return 0;
}
	//答案: POINT ER ST EW

這道題比較難大家要耐心看解析
解析
第一個答案: ++cpp指向c+2 為"POINT"(cpp值已經發生了改變)
第二個答案: 優先順序和指標的重複考察首先優先順序 : ++ * 和 – 為一個等級 +3是等級最低我們結合性是從左到右首先++ 我們的cpp就指向了cp陣列中的c+1然後解除參照就為c+1後–;c+1就成了c(注cp陣列裡的c+1就變成了c了)就指向首元素地址瞭然後再解除參照為c首元素地址ENTER再+3 就指向ENTER中的E用字串形勢列印就成了ER.
第三個答案: 中cpp已經指向了cp中的第三個元素(現在是c而不是c+1),所以cpp[-2]就是cp中的首元素c+3 再次解除參照後就是FIRST 再+3後指向了S列印後就是ST
(cpp[-2]等價與*(cpp-2)現在的cpp沒有改變).
第四個答案: cpp[-1][-1]相當於*(*(cpp-1)-1) 原本cpp指向cp中的第三個元素-1後解除參照就是第二個c+2再-1 後解除參照就是c+1的元素指向NEW的指標最後再+1就是指向E用字串列印後就是EW.