指針筆試題講解

筆試題一

#include<stdio.h>
int main()
{
   
   
    int a[5] = {
   
    1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0; 
}
// 程序的結果是什麼?

要想答對這道題,首先要知道 &數組名數組名 的區別.

&數組名 : 這裏的數組名錶示整個數組,取出的是整個數組的地址,&數組名 + 1 跳過整個數組,&數組名爲數組指針類型,在該題中即爲 int (*)[5] 類型

數組名 : 數組名爲首元素的地址,+ 1 跳過一個元素類型大小的字節,爲 int *類型

雖然 &數組名 和 數組名 以 %p 的形式打印出來結果是一樣的,但一定要注意它們之間的區別。


因此結果爲 2,5

筆試題二

#include<stdio.h>
struct Test
{
   
   
	int Num;
	char *pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p;
//假設p 的值爲0x100000。 如下表表達式的值分別爲多少?
//已知,結構體Test類型的變量大小是20個字節
int main()
{
   
   
	p = (struct Test*)0x100000;

	printf("%p\n", p + 0x1); // 0x100014
	printf("%p\n", (unsigned long)p + 0x1); // 0x100001
	printf("%p\n", (unsigned int*)p + 0x1); // 0x100004

	return 0;
}

這題考察的是 強制類型轉換不同類型指針的區別.

不同類型指針的區別 :

1.加減整數所走的步長不一樣.
2.解引用後訪問的權限不一樣.

第一個p是指向結構體的指針,加1跳過一個結構體,0x100000 + 20 = 0x100014
第二個p爲無符號長整形,加1直接加1 0x100000 + 1 = 0x100001
第三個p是指向無符號整形的指針,加1跳過一個無符號整形,0x100000 + 4 = 0x100004

筆試題三

#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);
	return 0;
}
// 輸出結果是什麼?

如果有讀者對大小端的存儲模式不清楚的話,可以去看一下這篇博客

數據在內存中的存儲(整數)

筆試題四

#include <stdio.h>
int main()
{
   
   
	int a[3][2] = {
   
    1, 3, 5 };
	int *p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}
// 輸出結果是什麼?

解決這道題,我們首先要理解二維數組.

對於 int[3][2],我們可以把它看作一個一維數組,一維數組裏有三個元素,每個元素爲一個數組(存放兩個整形)

a[0],a[1],a[2] 分別是每一行的數組名,即每一行首元素的地址.

二維數組的數組名同樣表示首元素的地址,首元素爲a[0],即 &a[0],爲數組指針類型.


結果爲 1

筆試題五

#include<stdio.h>
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;
}

p[4] 等價於 *(p + 4) , 因爲p爲數組指針類型,+ 1 跳過 一個數組,解引用後得到首元素的地址,這裏要理解 *(&數組名)數組名 是等價的

筆試題六

#include<stdio.h>
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;
}
// 輸出結果爲多少?

該題和 第一題,第三題很相似,讀者們可以放在一起加深理解
因此結果爲 5,10

筆試題七

#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;
}
// 輸出結果爲多少?

這裏要注意的點是 括號表達式 ,括號表達式的計算結果只取決於最後一個表達式的計算結果 , 原代碼相當於:

int a[3][2] = {
   
    1,3,5 };

因此該題結果爲 1

筆試題八

#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);
	 printf("%s\n", *--*++cpp+3);
	 printf("%s\n", *cpp[-2]+3);
 	 printf("%s\n", cpp[-1][-1]+1);
	 return 0;
}
// 輸出結果是什麼?

**++cpp : 等價於 **(++cpp) 

1.++cpp後,cpp指向了cp[1] , 因爲是前綴++ , 先++後使用 , 第一次解引用後得到cp[1]的內容,即c[2]的地址 .

2 第二次解引用後,拿到c[2]的內容 , 即字符串"POINT"的首地址,即 ‘p’ 的地址, 因此打印結果爲 POINT

第一步進行完後,指向如下:
*-- *++cpp + 3 : 等價於 ( *( -- ( * (++cpp) ) ) ) + 3

1.++cpp後,cpp指向了cp[2] , 解引用後得到了cp[2]的內容,即c[1]的地址

2.前綴 - -後,cp[2]的內容被改成c[0]的地址

3.解引用後拿到c[0]的內容,即字符串"ENITR"的首元素地址,即字符 'E’的地址

4 . +3 後,得到第二個字符 'E’的地址,因此打印結果爲 ER

第二步進行完後,指向如下:

 * cpp[-2]+3 : 等價於 *( *(cpp - 2) ) + 3

1 . cpp - 2指向了cp[0] , 解引用後得到cp[0]的內容,即c[3]的地址

2 .第二次解引用後得到c[3]的內容,即字符串" FIRST"首字符的地址,即字符 ’ F’的地址

3 .+3後,得到字符 'S '的地址,打印結果爲 ST

第三步進行完後,指向如下:

注意喔 : cpp的指向可沒有改變,一定要小心喔!!!

cpp[-1][-1]+1 : 等價於 *( *(cpp - 1) - 1) + 1

1 . cpp - 1指向cp[1] , 解引用得到cp[1]的內容,即c[2]的地址

2 .- 1 後得到c[1]的地址 ,解引用得到c[1]的內容,即字符 'N’的地址

3 . +1後得到字符 'E’的地址,打印結果爲 EW

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章