前言
⚠️当前演示环境均为 x86环境(32位平台)
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
//&a 对数组进行取地址,&a就是int(*)[5] 数组指针类型;int (*p) = &a;-->int(*)[5]
//&a+1 是数组+1,跳过一个数组指向数组的后面
//&a+1也是int(*)[5]数组指针类型,将&a+1强制转换为int*类型,再赋给ptr
printf("%d,%d", *(a + 1), *(ptr - 1));
//a 是数组名,数组名就是首元素的地址,数组名+1,就是首元素+1,
// 跳过一个元素指向第二个元素,对a+1解引用就是2
// a 是int*类型 a+1也是int*类型 a+1跳过一个整型,对a+1解引用就是2
//ptr是int*类型,ptr-1向前挪一个整型,再解引用就是5
return 0;
}
笔试题2🌖
考查指针加减整数的问题
假设p 的值为0x100000。 如下表表达式的值分别为多少?(X86环境下)
已知,结构体Test类型的变量大小是20个字节
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
解析:
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;//p是结构体指针变量
int main()
{
p = (struct Test*)0x100000;//将0x100000赋给p需要强制类型转换
printf("%p\n", p + 0x1);//0x100014
//0x1 就是1,p+0x1=>p+1
//p+1就是p要跳过一个结构体,一个结构体的大小是20,0x100000 + 20 = 0x100014;
//16进制14转换为10进制就是20;1x16^1+4x16^0 = 20
printf("%p\n", (unsigned long)p + 0x1);//0x100001
//这里将p强制类型转换为unsigned long 整型,整型+1就是+1,所以是0x100001
printf("%p\n", (unsigned int*)p + 0x1);//0x100004
//这里将p强制类型转换为unsigned int*指针类型,p指向的是无符号整型 4字节,
//整型指针+1是跳过一个整型4字节,所以是0x100004
return 0;
}
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
//&a a是数组名,+1跳过一个数组,
int* ptr2 = (int*)((int)a + 1);
//a本来是int*类型,强制类型转换成int类型,整型+1就是+1,所以ptr2指向第一个元素的第二个字节
printf("%x,%x", ptr1[-1], *ptr2);
//ptr1[-1]=>*(ptr1-1);就是ptr1往前挪一个整型,再解引用就是4
//ptr2是一个整型指针,是2个字节,所以要从第一个元素的第二个字节往后数四个字节 就是 00 00 00 02,
//我们这里是小端存储要转换成16进制就是 0x 2 00 00 00 打印出来就是2000000
return 0;
}
%p —— 打印地址
%x —— 是16进制的格式打印
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//数组初始化内容有逗号表达式,逗号表达式是从左向右依次计算,逗号表达式的结果是最后一个表达式的结果
//数组实际上初始化的是int a[3][2] = {1,3,5};
int* p;
p = a[0];//a[0] => a[0][0]
printf("%d", p[0]); //p[0] => *(p+0) => *p =1
return 0;
}
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4];
//p是一个指针,指向一个数组有四个元素,每个元素都是整型类型
p = a;
//a是数组名,数组名是首元素的地址,二维数组就是第一行的地址
//a是int(*)[5]数组指针类型;p的类型是int(*)[4];将a的地址强制赋值给p,运行起来不会报错,但是编译器会报警告
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//p是int(*)[4],所以让p+1只会跳过4个字节
//p[4][2] => *(*(p+4)+2); p+4是一个整型指针,解引用访问4个字节;
//p+4=>p[4]; 想象成一个二维数组,p[4]就是第5行的数组名;数组名表示首元素地址
//指针和指针相减得到的是指针之间的元素个数,小地址减大地址是负数
//&p[4][2] - &a[4][2] = -4
//10000000000000000000000000000100 —— -4的原码
//11111111111111111111111111111011 —— -4的反码
//11111111111111111111111111111100 —— -4的补码
//%d打印的是原码,就是-4;
//%p打印的是地址,整型在内存中存的是补码,所以%p打印出来就是FFFFFFFC
return 0;
}
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1);
//aa是数组名,&aa+1就是跳过整个数组指向数组的后面
//将&aa+1 数组指针,强制类型转换为整型指针,赋给ptr1
int* ptr2 = (int*)(*(aa + 1));
//aa是数组名,数组名就是首元素的地址,二维数组就是第一行的地址,aa+1指向第二行
//*(aa + 1)=>aa[1],aa[1]就是第二行的数组名,没有单独放在sizeof内部,也没有&,就是首元素的地址
//*(aa + 1)=>aa[1]=>&aa[1][0],拿到的就是就是一个int*类型的整型指针
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//ptr1是int*类型,ptr1-1就是向前挪一个整型,解引用就是10
//ptr2是int*类型,ptr2-1就是向前挪一个整型,解引用就是5
return 0;
}
笔试题7🌓
下面代码的运行结果是什么:
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解析:
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
//数组a的每个元素都是char*类型每个元素都是char*指针
//字符串作为表达式存放的是首字符的地址
char** pa = a;
//a是数组名,数组名表示首元素地址,a的每个元素都是char*类型,
//a的地址就是char**二级指针,pa指向a的第一个元素是合理的
pa++;
//pa++要跳过一个char*的元素指向下一个元素
printf("%s\n", *pa);
//pa指向第二个元素,解引用就是at
return 0;
}
笔试题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;
}
解析:
#include <stdio.h>
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
//数组C的每个元素都是char*类型每个元素都是char*指针
//字符串作为表达式存放的是首字符的地址
char** cp[] = { c + 3,c + 2,c + 1,c };
//c是数组名,数组名表示首元素地址,所以c指向数组c的第一个元素,c+3是首元素+3
//c的每个元素都是char*类型,数组名表示首元素地址,所以c的地址就是char**二级指针
//cp是一个存放二级指针的数组,元素类型为char**
char*** cpp = cp;
//cpp里面存放cp,cp是数组名,数组名表示首元素地址,所以cpp指向cp的第一个元素
//cp的每个元素都是char**类型,数组名表示首元素地址,所以cp的地址就是char***三级指针
//cpp里面存放cp,所以cpp的类型是char***
printf("%s\n", **++cpp);
//++cpp是前置++,cpp自增然后指向cp的第二个元素,这一次cpp的指自增会影响下一次的计算
//**++cpp第一颗*解引用的结果是c+2,第二颗*解引用的结果是POINT
printf("%s\n", *-- * ++cpp + 3);
//++cpp是前置++,上一次计算中cpp自增指向cp的第二个元素,这一次再自增就指向cp的第三个元素
//*++cpp得到的结果是c+1;--c+1 =>c,c指向数组c的第一个元素,*--*++cpp = *c[0]
//解引用得到c[0]的地址是E,E的地址+3,得到的是后面的E,打印的是R往后的字符就是ER
printf("%s\n", *cpp[-2] + 3);
//cpp[-2] = *(cpp-2); *cpp[-2] + 3 =**(cpp-2)+3
//cpp现在指向数组cp的第三个元素,cpp-2就指向数组cp的第一个元素,
// *(cpp-2)=c+3,**(cpp-2) = *c[3],解引用得到c[3]的地址是F,再+3就是S,打印的结果就是ST
printf("%s\n", cpp[-1][-1] + 1);
//cpp[-1][-1] = *(*(cpp-1)-1); cpp[-1][-1] + 1 = *(*(cpp-1)-1) +1
//此时的cpp还是指向数组cp的第三个元素,cpp-1就指向数组cp的第二个元素
//*(cpp-1) = c+2, ==> *(cpp-1)-1 = c+1,c+1指向数组c的第二个元素
//*(*(cpp-1)) = *c[1],解引用得到c[1]的地址是N,c[1]的地址+1就是E,所以打印的结果是EW
return 0;
}
//注:cpp只有在++或者--的时候才会影响下一次cpp的计算,非++或者--不影响下一次运算
本章到这里就结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话请给个三连支持一下吧!
Fighting!!!✊