コンテンツ
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; } //程序的结果是什么?
- 解析:
- *(ptr-1)
&aは配列全体のアドレスを取り出すことを意味し、&a + 1は配列全体をスキップします。タイプは&a:int(*)[5]、タイプはまだint(*)[5]、キャスト&a + 1 int *の場合、それをptrに割り当て、ptrは&a + 1のアドレスを指し、ptr-1は前の要素のアドレスであり、*(ptr-1)を逆参照して5を取得します。
- *(a + 1):
aは配列名の最初の要素1のアドレスを表し、+ 1は要素2のアドレスを表し、逆参照*(a + 1)は2です。
2.筆記試験の質問2:
#include<stdio.h> 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); printf("%x\n", p + 0x1); printf("%x\n", (unsigned long)p + 0x1); printf("%x\n", (unsigned int*)p + 0x1); //%p以地址的形式打印,不省略0 //%x就是打印16进制,省略0 return 0; }
- 解析:
- p + 0x1:
整数ポインター(int *)+ 1は4バイトをスキップし、文字ポインター(char *)+ 1は1バイトをスキップし、構造ポインターp + 1は構造サイズ(20バイト)をスキップする必要があります。20の16進数は0x000014です。 、および追加後は0x100014です。
- (unsigned long)p + 0x1:
pは元々構造体へのポインタでしたが、強制的にunsigned long型に変換すると、unsigned integer型になり、ポインタではなくなります。unsignedinteger型なので、次のように扱われます。数値であり、1を追加した後、直接追加するだけです。つまり、0x100001
- (unsigned int *)p + 0x1:
pは整数ポインターに強制変換され、整数ポインター+1は4バイトをスキップし、4を加算すると0x100004になります。
3.筆記試験の質問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); return 0; }
- 解析:
- ptr1 [-1]:
&aは配列のアドレスを取り出し、&a + 1は配列全体をスキップし、それをint *にキャストして、ptr1に割り当てます。このとき、ptr1のアドレスは&a + 1であり、ptr[-1]は次のようになります。 *(ptr1-1)、ptr1-1のアドレスは図のようになり、逆参照された結果は4になります。
- * ptr2:
配列名aは最初の要素のアドレスを表し、強制型は値である整数に変換されます。1を追加すると、1バイトが追加されます。メモリはバイト単位のデータに変換される必要があり、要素1は4です。セクション、それがリトルエンディアンの場合、aのアドレスが0x10で、10進数に変換されて16、プラス1が17、16進数が0x11で、ptr2のアドレスを見つけて、参照解除するだけでさらに4バイトにアクセスできます。 2000000として印刷できます。プロセスを図に示します
4.筆記試験の質問4:
#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] = {(0、1)、(2、3)、(4、5)};このコードは次のように変換できます。 int a [3] [2] = {1、2、5};この配列は3行2列で、a [0]は最初の行の配列名、配列名は最初の要素のアドレスを表します。 a [0]は、最初の行の最初の行を表します。要素のアドレス、つまり、pが指す位置、p [0] = *(p + 0)= * p 、およびpは1を取得するために参照を解除します。 。
5.筆記試験の質問5:
#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; }
- 解析:
- &a [4] [2]:
&a [4] [2]は、図に示すように、行5と列3のアドレスを表します。
- &p [4] [2]:
pは配列ポインタであり、pが指すことができる配列は4要素であり、p + 1は16バイトである4つの整数をスキップします、p [4] [2] ---> *(*(p + 4)+2 )、aは配列の配列名、つまり最初の要素のアドレスであり、aをpに割り当てます。また、図に示すように、pはアドレスを指します。p + 1は4つの要素の配列をスキップし、+ 4の位置は図に示すようになります。*(p + 4)は、黄色のボックス*(p + 4)、*(p + 4)+2は、図のように2つの要素が指す位置をスキップし、それを逆参照して、要素を取得します。
- &p [4] [2]-&a [4] [2] 2つの整数型のアドレスを引くと、中央の要素数は4になります。これは、小さいアドレスが大きいアドレスを減らすため、次の形式で印刷するためです。 %dは-4
- そして、%pの形式で印刷することは、アドレスを印刷することであり、補数を印刷するだけです。
-4の補数:1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100
16進印刷に変換されるのはFFFFFFFFFFFF FFFC
6.筆記試験の質問6:
#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; }
- 解析:
- *(ptr1-1):
&aaは2次元配列のアドレスを意味し、&aa + 1は、2次元配列全体を図に示す位置にスキップし、整数ポインターに強制してptr1に割り当てることを意味します。ptr1-1のアドレス図に示すように、それを逆参照します。これは10番です。
- *(ptr2-1):
aaは、2次元配列の最初の要素の最初の行のアドレスを示す配列名です。aa+ 1は、図に示すように最初の行を2番目の行にスキップしてから、*(aa + 1)を逆参照します。配列名の2行目を取得します。最初の要素のアドレス、つまり要素6のアドレスがptr2に強制的に割り当てられ、ptr2-1が前方に移動し、それを逆参照して5を取得します。
7.筆記試験の質問7:
#include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa); return 0; }
- 解析:
paのタイプはchar*です。1を追加すると、char *の要素をスキップし、図に示す位置をポイントし、* paを逆参照してaのアドレスを取得し、%sが\0に逆方向にアクセスして停止します。 。それはです。
8.筆記試験の質問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); printf("%s\n", *-- * ++cpp + 3); printf("%s\n", *cpp[-2] + 3); printf("%s\n", cpp[-1][-1] + 1); return 0; }
- 解析:
- ** ++ cpp:
++cppはchar**要素のアドレスを図に示されている位置にスキップし、*++cppを逆参照して要素c+2を取得し、c+2はcの3番目の要素のアドレスです。 referenceはpのアドレスを取得し、\ 0、つまりPOINTまで%sの形式で出力します。
- *-* ++ cpp + 3:
++cppは別のchar*要素をスキップしてアイコンの位置に移動し、*++cppを逆参照してc+1を取得します。-*++cppはc+1-1 = cになります。このとき、cはc配列名です。最初の要素のアドレス、次に間接参照*-* ++ cppを使用してEのアドレスを取得し、次に+3を使用して2番目のEをポイントし、ERを%sで出力します。
- * cpp [-2] + 3:
* cpp [-2]は**(cpp-2)+3です。cpp-2が指す位置を図に示します。*(cpp-2)を逆参照してc + 3を取得し、次に**(cpp-2)を逆参照してFのアドレスを取得し、+3がSを指します。次に、%sSTを出力します。
- cpp [-1] [-1] + 1:
cpp [-1] [-1] + 1は*(*(cpp-1)-1)+1であり、cpp-1の位置は図に示すとおりであり、間接参照はc + 2になり、マイナス1はc+になります。 1、次に間接参照*(*(cpp-1)-1)を使用してNのアドレスを取得し、さらに1を使用してEを指すと、%sはEWを出力します。