文章目录
- 笔试题一
- 笔试题二
- 笔试题三
- 笔试题四
- 笔试题五
- 笔试题六
- 笔试题七
- 笔试题八
笔试题一
#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