C語言指針筆試題這麼變態?我可能白學C語言了!帶詳解!

碼字不易,對你有幫助 點贊/轉發/關注 支持一下作者

整理,做圖,排版不易,看完如果你有收穫不要忘記點個贊!
如果你有問題,歡迎留言區評論或者私信我。
我們廢話不多說,直接開莽!

6.指針和數組筆試題

環境:32 位機器

第一組

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

答案:

printf("%d\n",sizeof(a));// 16
printf("%d\n",sizeof(a+0));// 4 (a + 0 這個操作使得編譯器將 a 看爲指針)
printf("%d\n",sizeof(*a));// 4
printf("%d\n",sizeof(a+1));// 4
printf("%d\n",sizeof(a[1]));// 4
printf("%d\n",sizeof(&a));// 4
printf("%d\n",sizeof(*&a));// 16 (&a 是數組指針。再次用 * 解引用,是從這個地址開始取 int(*)[4] 類型對應的字節數)
printf("%d\n",sizeof(&a+1));// 4 (&a 得到的是 int(*)[4] 類型的指針,只要是指針大小就是 4)
printf("%d\n",sizeof(&a[0]));// 4
printf("%d\n",sizeof(&a[0]+1));// 4

第二組

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

答案:

printf("%d\n", sizeof(arr));// 6
printf("%d\n", sizeof(arr+0));// 4
printf("%d\n", sizeof(*arr));// 1
printf("%d\n", sizeof(arr[1]));// 1
printf("%d\n", sizeof(&arr));// 4 (char (*)[6] 類型的指針)
printf("%d\n", sizeof(&arr+1));// 4
printf("%d\n", sizeof(&arr[0]+1));//4

printf("%d\n", strlen(arr));// 未定義 (arr 字符數組沒有 '\0',有可能會出現一個隨機值,程序也有可能會崩潰。)
printf("%d\n", strlen(arr+0));// 未定義
printf("%d\n", strlen(*arr)); // 錯誤的參數類型 (strlen 要的是 char* 類型,但是 *arr 是 char類型。*arr 是字符 a,也就是 97,編譯器有可能將 97 當成一個 16 進制的地址。所以,這樣的代碼一定是不對的)
printf("%d\n", strlen(arr[1]));//同上
printf("%d\n", strlen(&arr));// 未定義
printf("%d\n", strlen(&arr+1)); // 未定義
printf("%d\n", strlen(&arr[0]+1));// 未定義

第三組

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

答案:

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr+0));//7
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4 (char (*)[7])
printf("%d\n", sizeof(&arr+1));//4 (char (*)[7])
printf("%d\n", sizeof(&arr[0]+1));//4 (char*)

printf("%d\n", strlen(arr));// 6
printf("%d\n", strlen(arr+0));// 6
printf("%d\n", strlen(*arr));// 錯誤的參數類型
printf("%d\n", strlen(arr[1]));// 同上
printf("%d\n", strlen(&arr));// 6 (&arr 的類型是 char (*)[7] 與 char* 類型不一致,但是 &arr 與 arr 是相同的,所以恰巧能得出正確結果,但是這是錯誤的寫法。)
printf("%d\n", strlen(&arr+1)); // 未定義 (&arr + 1,跳過了整個數組,訪問數組後面的空間,非法內存訪問)
printf("%d\n", strlen(&arr[0]+1)); // 5 (&arr[0] -> char* ,加以跳過一個數組元素)

第四組

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));

printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

答案:

char *p = "abcdef";
printf("%d\n", sizeof(p));// 4
printf("%d\n", sizeof(p+1));// 4
printf("%d\n", sizeof(*p));// 1
printf("%d\n", sizeof(p[0]));// 1
printf("%d\n", sizeof(&p));// 4 (char**)
printf("%d\n", sizeof(&p+1));// 4 (char**)
printf("%d\n", sizeof(&p[0]+1));// 4 

printf("%d\n", strlen(p));// 6
printf("%d\n", strlen(p+1));// 5
printf("%d\n", strlen(*p));// 錯誤的參數類型 
printf("%d\n", strlen(p[0]));// 錯誤的參數類型
printf("%d\n", strlen(&p));// 同上 (&p 的類型是 char**,將char** 強轉成的 char* 並不是一個字符串) 
printf("%d\n", strlen(&p+1));// 未定義
printf("%d\n", strlen(&p[0]+1));// 5 (對於 &p[0] 來說,p 先與 [] 結合)

指針爲什麼也可以用 []運算符?

對於指針 int* p = “abc”;

p[1] 等價於 *(p + 1)

這是因爲數組很多時候可以隱式轉換成指針。

重點注意:printf("%d\n", strlen(&p));

&p的類型是 char**,但是C語言會將其隱式類型轉換成 char*,但是 strlen 訪問的是地址p的內存空間,那這其實是未定義行爲。

第五組

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

答案:

int a[3][4] = {0};
//所謂二維數組本質是一維數組。裏面的每個元素又是一個一維數組。
//本例是一個長度爲 3 的一維數組,每個元素又是長度爲 4 的一維數組。(VS 中可以用調試來測試)
printf("%d\n",sizeof(a));// 48
printf("%d\n",sizeof(a[0][0]));// 4
printf("%d\n",sizeof(a[0]));// 16 (a[0] 的類型是 int[4])
printf("%d\n",sizeof(a[0]+1));// 4 (a[0]->int[4]相當於一個一維數組,a[0] + 1 隱式轉換爲指針 int*)
printf("%d\n",sizeof(*(a[0]+1)));// 4 (a[0] + 1 -> a[0][1])
printf("%d\n",sizeof(a+1));// 4
printf("%d\n",sizeof(*(a+1)));// 4
printf("%d\n",sizeof(&a[0]+1));// 4 (a[0] -> int[4],&a[0] -> int (*)[4],再加1還是數組指針)
printf("%d\n",sizeof(*(&a[0]+1)));// 16 (int (*)[4] 解引用變爲 int[4])
printf("%d\n",sizeof(*a));// 16 (*a -> *(a + 0) -> a[0])
printf("%d\n",sizeof(a[3]));// 16

重點注意:

printf("%d\n",sizeof(a[0]+1))

printf("%d\n",sizeof(&a[0]+1))

a[0] 與 &a[0] 的差異比較:

	int a[3][4] = {
		{1, 2, 3, 4},
		{5, 6, 7, 8},
		{5, 10, 11, 12},
	};

	printf("%d\n", *(a[0] + 1));// 2
	printf("%d\n", **(&a[0] + 1));//5

printf("%d\n",sizeof(*(&a[0]+1)));

我們來一步一步分析:

a[0] -> int[4] ; &a[0] -> int (\*)[4] ; &a[0] + 1 -> int (\*)[4] ; *(&a[0] + 1) -> int[4]

printf("%d\n",sizeof(a[3]))

sizeof是一個運算符,並不是函數。它在預編譯時期替換。而我們說的“數組下標訪問越界”前提條件是 內存訪問越界,這個時期是程序運行時。a[3] 就是 int[4] 類型,所以就是 16。哪怕你寫 a[100]都可以。

printf("%d\n", 16)是程序運行時執行的語句。

關於 const

int num;
const int* p = #
int const* p = #// 這樣的寫法不科學,int* 應該當成一個整體,不過它的含義與上面的相同。
int* const p = #

對於第一種寫法,*p 是不能改變的;對於第三種寫法,地址 p 是不能被改變的。

7. 指針筆試題

int main(void)
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}

a + 1:a 隱式轉換成 指針,指向 首地址後移 4 個字節。(a 隱式轉換後是 int* 類型,它指向的 int 大小是 4 個字節,所以後移 4 個字節)

&a 的類型是 int(*)[5] ,所以 &a + 1 後移 int[5] 的長度

所以最後輸出的是:2,5

//由於還沒學習結構體,這裏告知結構體的大小是20個字節
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假設p 的值爲0x100000。 如下表表達式的值分別爲多少?
int main(void)
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	
    return 0;
}

p + 0x1 p 加十六進制的 1,p 所指向的結構體大小是 20,所以 p 會增加 20 。但是注意 %p 輸出的是 16 進制的地址,所以輸出的是 0x100014

(unsigned long)p + 0x1 p 被強轉成了一個數,所以輸出的就是 0x100001

(unsigned int*)p + 0x1 p 被強轉成了一個 int* 類型的指針,所以輸出的是 0x100004

int main(void)
{
	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;
}

ptr1[-1]: 前面我們說過,這個操作相當於 *(ptr1 - 1)

(int)a + 1 是將 a 先強轉爲 int 然後再加 1,所以 a 僅僅增加了 1 個字節

在這裏插入圖片描述

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

p[0] -> a[0] [0] ,所以輸出的是 0 嗎?

並不是,注意看 a[3] [2]大括號內的內容,裏面是圓括號而不是大括號,這是逗號表達式

所以,a[0] [0] == 1

int main(void){
	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][2] -> 數組中的第 19 個元素(4 * 4 + 3)

&a[4][2] -> 數組中的第 23 個元素 (4 * 5 + 3)

答案:FFFFFFFC,-4

int main(void)
{
	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;
}

&aa 的類型是 int(*)[2][5],所以 &aa + 1 指向的是整個數組後面的內存 。所以 *(ptr1 - 1) 的值是 10

aa aa + 1 讓 aa 隱式轉換爲 int(*)[5] ,所以 aa + 1 指向的是元素 6 所在的地址。所以 *(ptr2 - 1) 的值是 5

#include <stdio.h>
int main(void)
{
	char *a[] = {"work","at","alibaba"};
	char**pa = a;
	pa++;
	printf("%s\n", *pa);
	
    return 0;
} 

在這裏插入圖片描述

int main(void)
{
	char *c[] = {"ENTER","NEW","POINT","FIRST"};
	char** cp[] = {c+3,c+2,c+1,c};
	char***cpp = cp;
	printf("%s\n", **++cpp);// ++cpp 會改變 cpp 的值
	printf("%s\n", *--*++cpp+3);//
	printf("%s\n", *cpp[-2]+3);//-2 並沒有改變 cpp
	printf("%s\n", cpp[-1][-1]+1);
	
    return 0;
}

在這裏插入圖片描述

單目運算符從右向左依次運算。

char* p = "ENTER";
printf("%s", p + 3);// 輸出 ER,p + 3 增加 3 個字節,因爲 p 指向的類型是 char 大小是 1 個字節。

以上就是本次的內容。

如果文章有錯誤歡迎指正和補充,感謝!

最後,如果你還有什麼問題或者想知道到的,可以在評論區告訴我呦,我可以在後面的文章加上你們的真知灼見​​。

關注我,看更多幹貨!

我是程序圓,我們下次再見。

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