目录
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;
}
//程序的结果是什么?
正确答案:2,5。
解题:
&a是取出整个数组的地址,&a指向a,&a+1就指向了5的后面。
&a是指向数组的,是数组指针,其类型是:int (*)[5],&a+1之后还是数组指针,还是这种类型;
但是(int*)(&a+1)强制类型转换为整型指针:(int *)。
现在吧整形指针(&a+1)赋给ptr,则ptr就是整形指针,指向5的后面,-1移动一个整形指向5,解引用就是5。
a是数组名,是1的地址,2的地址解引用就是2。
2、笔试题2
#include <stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
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);
return 0;
}
正确答案:
00000014
00000001
00000004
解题:
p是结构体指针,用结构体类型创建了指针变量p,假设p的值为0x100000,p =0x100000,0x100000是十六进制数字而且是整型类型,p是整形指针,若想0x100000是一种地址或指针类型则可以对它强制类型转换。
p =(sttuct Test*)0x100000,这里p的类型和数字0x100000的类型是一样的,赋过去。
问:
p是结构体指针,0x1(写成了十六进制)就是1,指针+1到底+几?
指针+1是+几取决于类型。(int*)整型指针+1就是+4个字节。因为整形指针是指向整形的,+1就会跳过一个整形就是4个字节;(char*)字符指针+1就是+1个字节。因为字符指针是指向字符的,+1就会跳过一个字符就是1个字节。
注意:一个结构体指针+1就是跳过一个结构体大小。
①p+0x1是多少?
这里已知结构体是20个字节,所以p+0x1就是跳过20个字节,p变成了0x100014(十六进制的14就是十进制的20),p+0x1就是0x100014。
②p强制类型转换为unsigned long后+0x1是多少?
p本来是结构体指针,此时强制类型转换为unsigned long,则此时p就是无符号整形,所以会把p里面的值当成是一个数字0x100000,这个十六进制0x100000对应的十进制是1048576,这个整形数字+1就仅仅是+1(不是指针+1,指针+1时才会考虑跳的字节数),所以数字0x100000+1就是0x100001。
③p强制类型转换为unsigned int*后+0x1是多少?
p此时是整形指针,整型指针+1就是+4个字节,就是0x100004。
总结知识:
十六进制数字是以0x开头的。0开头的是八进制数字。
%p是以地址的形式打印,把数字以十六进制形式打印,不省略高位的0。(如打印出的00100014、00100001、00100004)。
%x是打印十六进制的,会省略高位的0。(如打印出的100014、100001、100004)。
注意:地址本来是二进制的;二进制,八进制、十进制、十六进制只是数值的表现形式,地址是值,可以用十进制、十六进制等多种形式表示。
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;
}
错误答案:十六进制的4和2。
正确答案:4,2000000。
解题:
&a取出的是数组的地址,&a+1就会跳过整个数组指向4的后面,然后把这个指针强制类型转换为整形指针赋给ptr1,即ptr1指向4的后面。
ptr1(-1)就是*(ptr1 + (-1))就等价于*(ptr1-1),是4,用十六进制打印也是4。
a是数组名,是首元素地址,即是1的地址,(int)a,是把地址强制类型转换为整形,是值,所以仅仅只是数字+1,(数字+1)作为地址只是向后跳了一个字节,(例如500作为地址和501作为地址这两者之间只是相差了一个字节。) 而整形是4个字节,把a数组的1,2、3、4这四个整形以字节为单位写出来:
小端排序:
01 00 00 00
02 00 00 00
03 00 00 00
04 00 00 00
(复习:低位放在低地址是小端排序,1本来是0x 00 00 00 01,01是低位)
如a的起始地址就是0x10,即1的01处的地址是0x10,地址0x10转换为值(整形值)后+1就是0x11(十六进制的0x10转换为十进制是16,+1是17,17作为十六进制就是0x11)加完之后(int*)0x11是强制类型转换为整形指针赋给ptr2,所以是地址0x11,这个0x11地址相较于起始地址0x10是向后偏移了一个字节,ptr2作为整形指针指向了1的第一个00处,则*ptr2是:以ptr作为起始地址向后访问一个整形,即访问4个字节,一个整形的内容,在内存中以小端形式存储访问的是:00 00 00 02,以小端形式拿出来还原出来的数字就是0x 02 00 00 00,以%x形式打印高位的0会省略,所以打印的是2 00 00 00。
对于指针强制类型转换为整形(值)+1就仅仅只是+1。整形+1就是值+1。
只有是指针+1的时候才会考虑到底+几。
a作为数组名,它的类型是:int*
a+1对整形指针+1就是+4;
把a强制类型转换为整形,则a就是整形数字,整形数字+1就是+1(5+1=6),两个地址差1就是差1个字节(在内存块中,每个内存单元给一个编号,一个编号就是一个地址,大小就是一个字节)。
若转换为char,(char)a+1,是以ASCII码值解读的,+1还是+1。
温故而知新:
数据在内存中存储是有顺序的。
例如数据:0x11223344
这里44是低位字节,11是高位字节。从11到44从左到右依次是逐次增高的字节位。
数据在内存中存储:
预先规定:若内存中从左到右是从低地址到高地址。
若是按大端排序:
11223344
把一个数字的低位字节的内容放到高地址处,高位字节的内容放到低地址处,是大端存储。
若是按小端排序:
44332211
把一个数字的低位字节的内容放到低地址处,高位字节的内容放到高地址处,是小端存储。
以小端形式存储则以小端形式取出。数据还是原来的数据。
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]);//*(p+0)
return 0;
}
错误答案:0。
正确答案:1。
解题:
注意数组中放的不是0,1、2、3、4、5;
不是 { {0,1},{2,3},{4,5} };而是“(0,1),(2,3),(4,5)”,是逗号表达式。
若是 { {0,1},{2,3},{4,5} },意思是0、1放在第一行;2、3放在第二行;4、5放在第三行;
是“(0,1),(2,3),(4,5)”,则意思是:(0,1)这个逗号表达式的结果是1,(2,3)这个逗号表达式的结果是3,(4,5)这个逗号表达式的结果是5,所以这里面没有0、2、4的数字。
int a[3][2] = { (0,1),(2,3),(4,5) }; 就等同于:int a[3][2] = { 1,3,5 };
int a[3][2] = { 1,3,5 };三行两列表示的就是:
第一行:1 3
第二行:3 5
第三行:0 0
a[0]是第一行的数组名,数组名没有&,没有单独放在sizeof内部,所以数组名就表示数组首元素地址,a[0]就是第一行第一个元素的地址,即p指向1。因为p[0] = *(p+0) = *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;
}
错误答案:随机值,0
正确答案:FFFFFFFC,-4
解题:
&a[4][2]是一个整型指针;p是一个数组指针,p能够指向的是4个元素的数组,每个元素是整型,
p+1会跳4个整型,跳的是16个字节。p它自己认为它指向的是一个数组,+1就会跳过这个数组。
p[4][2] = *(*(p+4)+2)
a作为二维数组的数组名表示的是第一行的地址,但指向的也是数组的起始位置。p = a,则p指向的也是数组的起始位置,p认为它指向的数组是4个元素,所以p+1会跳过4个整型(跳过4个元素的数组),找到p+4的位置后,指向p+4,因为p认为它指向是4个元素的数组,所以*(p+4)就是向后访问4整型,就是4个元素的数组,此时*(p+4)相当于4个整型的数组名,数组名又表示数组首元素地址,所以*(p+4)就是指向的起始位置的地址,所以*(p+4)+2就是该4个整型的数组中的下标为2的地址;即找到了元素:*(*(p+4)+2),就是p[4][2]
&p[4][2] - &a[4][2] = ?
两个整型地址相减,小地址 - 大地址,减的是中间元素的个数,这两个指针都是int*的指针,所以这2个指针相减计算的是二者之间int的个数,是 -4。
-4:
以%d形式打印,打印的就是-4。
以%p形式打印,就认为-4是地址,直接打印-4在内存中的补码:
-4的原码:10000000000000000000000000000100
-4的反码:11111111111111111111111111111011
-4的补码:11111111111111111111111111111100
11111111111111111111111111111100这个值以16进制写是:fffffffc(4个1就是1个f)
a数组中有25个int,一个元素是一个int
总结知识:
数组指针+1——>会跳过一个数组
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;
}
错误答案:0,0
正确答案:10,5
解题:
二维数组的数组名+1就跳过了整个二维数组,强制类型转换为整形指针赋给ptr1,ptr1为整形指针,ptr1-1就是向前移动一个字节,解引用就是10。
aa是二维数组的数组名就是第一行的地址,aa+1就跳了一行指向第二行,*(aa+1)相当于第二行的数组名,第二行的数组名又相当于首元素的地址,即拿到的是6的地址,6的地址强制类型转换为整形指针赋给ptr2,ptr2是整形指针,指向6的地址。
第二行的地址解引用就相当于拿到第二行的数组名:aa[1],第二行的数组名又相当于首元素地址,就是6的地址,6的地址强制类型转换为整形指针(int*)(这里其实不用强制类型转换为int*,因为6的地址本来就是int*)
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;
}
正确答案:at
解题:
a首先与[结合说明a是数组,数组的每个元素是char*类型,所以a是指针数组。
后面的{}是a的初始化,拿字符串首字符的地址即w的地址初始化了数组a的第一个元素;at中a的地址初始化了数组第二个元素,alibaba中a的地址初始化了数组第三个元素。
a是数组名,是首元素地址,就是a数组中第一个元素的地址,该元素是char*类型,它的地址是char**,(一级指针变量的地址就是char**)
pa = a;a是数组名,是数组首元素的地址;则pa中存的是a数组中第一个元素的地址,即pa指向了a数组中第一个元素;pa是char**类型,pa指向的元素是char*类型的:char**pa——>char* *pa;第二个*说明pa是指针,第一个*和char合起来说明pa指向的元素对象是char*类型的;pa+1就相当于跳过一个char*类型的元素,此时pa指向了第二个元素,相当于指向了at中的a,*pa,对pa解引用,就是向后访问了一个元素,这个元素里放的是a的地址,当把at的地址放到printf()函数中打印时就是打印字符串at。
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;
}
正确答案:
POINT
ER
ST
EW
解题:
注意:
%s就是打印字符串的,要打印字符串,那就需要把字符串的首字所在空间地址传进去,才可以从起始位置开始一个一个往后打印 ,直到遇到\0位置。
所以:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = { 'a','b','c','d','e','f','\0'};
char arr2[20] = "xxxxxxxx";
printf("%s\n", strcpy(arr2, arr1));
//注意这里strcpy()函数返回的是arr2这块空间的起始地址,以%s形式打印
//实际输出的是字符串abcdef
return 0;
}
#include <stdio.h>
int main()
{
char* p = "ENTER";
p++;
printf("%s\n", p);
return 0;
}//NTER