目錄
- 一、指針運算筆試題解析
- 3.1 題目1:
- 3.2 題目2:
- 3.3 指針3:
- 3.4 題目4:
- 3.5 題目5:
- 3.6 題目6:
- 3.7 題目7:
- 總結
一、指針運算筆試題解析
3.1 題目1:
#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;
}
程序的運行結果是什么?
結合這個程序的內存布局來講
這里把int(*)[5]類型的數組指針強制類型轉換為int *類型賦值給ptr,因為類型一樣才可以賦值,ptr是int *型指針,int *指針減1,向前挪動一個整型,所以 *(ptr - 1)打印結果是5。另一個打印原理很簡單,看了我指針6前面的原理講解就懂了。
3.2 題目2:
該結構體在x86環境下,大小確實為20個字節,在x64下就不是了。
這里結構體類型加了一個*,這是結構體指針類型,結構體指針創建了個變量叫p,p里有地址,0x100000是個整數,把整數賦給結構體指針,要強制類型轉換。
第一個printf:指針加減整數與指針類型有關,結構體指針加1跳過一個結構體,一個結構體20個字節,這是16進制,也就是0x100014。只有指針變量+1,才可以說加幾個字節。
第二個printf:這里p本是指針變量,p被強制類型轉換為unsigned long,無符號長整型,整數類型,整數加1就是加1,也就是0x100001.
第三個printf:p被強制類型轉換為unsigned int*,這時加1,跳過一個無符號整型,4個字節。這時地址是0x100004,這時用%p打印在x86環境下要打印夠8個16進制位,32個比特位,前面補兩個0,%p不打印0x。
3.3 指針3:
#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;
}
依據這串代碼你所想的結果是什么呢?是不是0呢?而且所設想的初始化情況是不是這樣
特別要注意:(0, 1)這是一個逗號表達式。在C語言中,逗號運算符會先對左邊的操作數求值,然后直接丟棄這個值,最終返回右邊操作數的值。
進監視看初始化情況也確實如此:
所以該數組的初始化結果如圖:
一般情況的初始化要使用大括號的
int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };
所以改代碼的輸出結果是1
3.4 題目4:
*(ptr1 - 1)
這里第一個打印沒啥好說的,直接說第二個* (ptr2 - 1),aa數組名表示首元素地址,因為是二維數組,所以aa是第一行的地址,(aa + 1)是第二行一維數組的地址,* (aa + 1)拿到第二行整個數組,相當于數組名,aa[1]數組名又相當于首元素地址。
補充一下:這里(*(aa+1))指向6,6的地址已經是整型地址,這里也不需要強制類型轉換了。
打印結果:
3.5 題目5:
這里把a的地址賦給p,兩者類型不同,但p其實還是可以接收的,只不過會報警告。類似于下面的代碼
int num = 10;
int* p1 = #
char* p2 = #//類型差異
由于p指向的數組是4個元素,所以p接收的是第一個黑框,這里p也訪問的是二維數組。
注意:這里要區分p和a的角度,這些黑框都是以p為單位+1,+2指向的地址。地址減地址,得到的是兩個地址之間的元素個數。且隨著數組下標的增長,地址是由低到高變化的。
紅塊是&p[4][2],綠塊是&a[4][2],打印的兩個值都是小地址減大地址,得到的是一個負數(-4)。
打印的時候
%d - 按照10進制的形式,打印有符號的整數。
%d認為內存中存放的是有符號整數的補碼。
%p - 打印地址的
%p認為內存中存放的補碼就是地址
%p打印地址是以16進制的形式打印的
//假設環境是x86環境,程序輸出的結果是啥?
#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]);//-4//10000000 00000000 00000000 00000100 - 原碼//11111111 11111111 11111111 11111011 - 反碼//11111111 11111111 11111111 11111100 - 補碼//FF FF FF FCreturn 0;
}
3.6 題目6:
#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}
這里創建了一個char* 的指針數組,數組的每個元素為char* ,是否每個元素對應一個字符串呢?
實則不然
char* p={“abcdef"};
這個代碼p里存放的是字符串的首字符地址,對應過來,這里char* 的指針數組,每個元素存放一個字符串首字符的地址,a數組的每個元素都為char* 類型。
結合圖來看:
pa是一個二級指針,char** pa = a相當于把一個一級指針的地址放到二級指針變量里去了,pa++跳過一個char* 的元素,指向第二個元素。* pa拿到at里a的地址,a的地址交給%s打印,有了起始地址向后打印,遇到\0停下來。
3.7 題目7:
接下來的題目是這一章里最難的一題了
#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;
}
先看第一個printf內存布局圖:
printf("%s\n", **++cpp);
++cpp指向c+2的地址,* ++cpp拿出c+2的地址,c+2的地址又指向c
數組里P的地址,再解引用拿到P的地址,所以第一個printf打印出POINT。
第二個printf內存布局圖:
printf("%s\n", *-- * ++cpp + 3);
前面++cpp已經把cpp的指向改了,cp改為cp+1。這里++的優先級高于加法運算符,這里++cpp指向c+1的地址,* ++cpp拿到c+1的地址,也可以理解為拿到c+1這個元素再- -,c+1變c,這時就只想了c數組里E的地址,再解引用拿到E的地址,+3拿到ENTER\0里第二個E的地址,此時打印ER。
第三個printf內存布局圖:
printf("%s\n", *cpp[-2] + 3);
*cpp[-2]不好理解拆解換算一下,cpp[-2]就是cpp+(-2)再解引用,再把原來的 * 加上就是 * * (cpp-2),再+3。
cpp-2就是cp+1+1-2,即cp。意思為cpp里的變量值-2,cp為c+3的地址,解引用拿到c+3的地址,c+3指向指向c數組里的F,再解引用拿到F的地址,+3指向S,打印結果為ST。
第四個printf內存布局圖:
printf("%s\n", cpp[-1][-1] + 1);
拆解換算之后如圖:
這里cpp里的值沒變,只有加加,減減才會改變里面的值,依舊是cp+2,(cpp-1)之后變為cp+1,相當于指向c+2處的地址,第一次解引用拿到c
+2的地址,c+2指向P處位置的地址,再-1,指向N處,解引用拿到N的地址,+1指向N后E的地址,打印結果為EW。
總結
以上就是筆試題的全部內容了,不得不說有些筆試題確實出的非常刁鉆,但總的來說還是考察指針地址的理解,還是挺有意思的。喜歡的靚仔靚女們不要忘記一鍵三連支持一下~