这里收录的是相关指针的练习,主要针对的是指针与sizeof之间的练习,练完你对指针的理解将更进一层喔
- 一维数组指针练习
- 字符数组指针练习
- 二维数组指针练习
- 练习总结:
- 指针笔试真题
一维数组指针练习
一维数组相关练习,下面答案是多少呢,为什么呢?
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
解析:
int main()
{
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
//16
//a为数组名,但在sizeof(数组名)表示的计算整个数组的大小
printf("%d\n", sizeof(a + 0));
//4或8
//首元素地址
printf("%d\n", sizeof(*a));
//4
//首元素解引用--第一个元素1的大小为4字节
printf("%d\n", sizeof(a + 1));
//4或8
//第二个元素的地址
printf("%d\n", sizeof(a[1]));
//4
//第二个元素2,4字节
printf("%d\n", sizeof(&a));
//4或8
//&数组名表示取出的是整个数组的地址,地址的大小就是4或8
printf("%d\n", sizeof(*&a));
//16
//*和&抵消还是a,sizeof(数组名)表示计算整个数组大小
printf("%d\n", sizeof(&a + 1));
//4或8
//&a取出的是整个地址大小,+1也是加上整个数组地址大小,但地址大小就是4或8
printf("%d\n", sizeof(&a[0]));
//4或8
//a[0]就是 *(a+0)就是第一个元素,然后再取地址,就是首元素地址了
printf("%d\n", sizeof(&a[0] + 1));
//4或8
//第二个地址
return 0;
}
答案:
字符数组指针练习
字符数组相关练习,下面答案是多少呢,为什么呢?
1.
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr + 0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr + 1));
printf("%d\n", sizeof(&arr[0] + 1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr + 0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr + 1));
printf("%d\n", strlen(&arr[0] + 1));
解析:
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
//6
//sizeof(数组名)表示计算的是整个数组大小为6字节
printf("%d\n", sizeof(arr + 0));
//4或8
//这里arr+0表示首元素地址
printf("%d\n", sizeof(*arr));
//1
//这里的arr表示首元素地址,*arr表示第一个元素'a'
printf("%d\n", sizeof(arr[1]));
//1
//表示第二个元素'b'
printf("%d\n", sizeof(&arr));
//4或8
//&arr表示取出的是整个数组地址,但还是地址
printf("%d\n", sizeof(&arr + 1));
//4或8
//&arr表示取出的整个数组大小,+1表示加上整个数组大小地址
printf("%d\n", sizeof(&arr[0] + 1));
//4或8
//第二个元素地址
printf("%d\n", strlen(arr));
//这里要注意strlen是专门用来计算字符大小的函数,遇到'\0'停止,而arr这个数组没有\0
//所以这个为随机值
printf("%d\n", strlen(arr + 0));
//无\0,所以为随机值
printf("%d\n", strlen(*arr));
//对首元素解引用,访问的是第一个元素,strlen('a')->strlen('97')---非法访问
//strlen()里面放的应该是一个地址,然后strlen跟着这个地址往后找,找到\0就停止了
//但这里strlen('a')里放的不是地址,而是97这个数,所以造成非法访问。
printf("%d\n", strlen(arr[1]));
//第二个元素strlen('b')->strlen('97')---也是非法访问。
printf("%d\n", strlen(&arr));
//&arr表示取出的是整个数组地址,但不知道后面是否有\0,所以还是随机值
printf("%d\n", strlen(&arr + 1));
//这里的arr表示首元素地址,&首元素地址,取出一个地址,这个地址指向的是arr首元素地址,再+1呢,也不知道这个地址是啥了
//还是随机值
printf("%d\n", strlen(&arr[0] + 1));
//&arr[0]+1,第二个元素地址,后面无\0,为随机值。
return 0;
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
解析:
int main()
{
char arr[] = "abcdef";
//这里是有\0的
printf("%d\n", sizeof(arr));
//7
//sizeof()计算整个数组大小,\0也算大小而strlen不算\0
printf("%d\n", sizeof(arr + 0));
//4或8
//首元素地址
printf("%d\n", sizeof(*arr));
//第一个元素大小 1
printf("%d\n", sizeof(arr[1]));
//第二个元素大小 1
printf("%d\n", sizeof(&arr));
//&arr取出的是整个数组的地址,还是地址
printf("%d\n", sizeof(&arr + 1));
//4或8
//地址
printf("%d\n", sizeof(&arr[0] + 1));
//4或8
//第二个元素地址
printf("%d\n", strlen(arr));
//首元素地址,后面有\0可以停止
//6
printf("%d\n", strlen(arr + 0));
//6
//首元素地址
printf("%d\n", strlen(*arr));
//第一个元素strlen(a)-->strlen(97)
//非法访问了
printf("%d\n", strlen(arr[1]));
//第二个元素
//非法访问
printf("%d\n", strlen(&arr));
//6
//&arr取出的是整个数组地址,但&arr地址和首元素地址是一样的然后可以找到\0
printf("%d\n", strlen(&arr + 1));
//随机值
//&arr取出的是整个地址,+1,跳过整个数组地址,不知道后面是否有\0
printf("%d\n", strlen(&arr[0] + 1));
//第二个元素地址
//5
return 0;
}
3.`
char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
解析:
int main()
{
char* p = "abcdef";
// p表示字符指针,指向的是首元素的地址
printf("%d\n", sizeof(p));
//4或8
//计算首元素地址大小
printf("%d\n", sizeof(p + 1));
//4或8
//第二个元素地址
printf("%d\n", sizeof(*p));
//1
//第一个元素a
printf("%d\n", sizeof(p[0]));
//1
//第一个元素a
printf("%d\n", sizeof(&p));
//4或8
//&p是二级指针,指向的是a的地址的地址
printf("%d\n", sizeof(&p + 1));
//4或8
//地址
printf("%d\n", sizeof(&p[0] + 1));
//4或8
//第二个元素的地址
printf("%d\n", strlen(p));
//6
//p表示首元素地址,strlen直达\0停止
printf("%d\n", strlen(p + 1));
//5
//从第二个元素地址开始
//printf("%d\n", strlen(*p));
*p表示a
非法访问了
//printf("%d\n", strlen(p[0]));
非法访问
strlen(a)
printf("%d\n", strlen(&p));
//二级指针,指向首元素的地址的地址
//所以为随机值
printf("%d\n", strlen(&p + 1));
//随机值
printf("%d\n", strlen(&p[0] + 1));
//第二个元素地址
//5
return 0;
}
二维数组指针练习
二维数组相关练习,下面答案是多少呢,为什么呢?
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[0][0]));
printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a[0] + 1));
printf("%d\n", sizeof(*(a[0] + 1)));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(*(a + 1)));
printf("%d\n", sizeof(&a[0] + 1));
printf("%d\n", sizeof(*(&a[0] + 1)));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a[3]));
解析:
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
//48
//sizeof(数组名)表示计算的整个数组大小
printf("%d\n", sizeof(a[0][0]));
//4
//第一行第一个元素大小
printf("%d\n", sizeof(a[0]));
//a[0]表示第一行数组名,sizeof(数组名)表示计算的是第一行数组大小
//16
printf("%d\n", sizeof(a[0] + 1));
//a[0]表示第一行数组名,但没有单独放进sizeof()中也没有&,所以表示首元素地址,首元素地址+1表示第二个元素地址
//4或8
printf("%d\n", sizeof(*(a[0] + 1)));
//4
//(a[0]+1)表示第一行第二个元素地址,解引用访问第二个元素
printf("%d\n", sizeof(a + 1));
//a是数组名,表示首元素地址,表示第一行的数组地址,+1表示第二行地址
//4或8
printf("%d\n", sizeof(*(a + 1)));
//16
//(a+1)表示第二行地址,解引用表示整个第二行元素
printf("%d\n", sizeof(&a[0] + 1));
//a[0]表示第一行数组名,&数组名,表示取出的是第一行整个地址,+1跳过整个第一行数组地址大小所以表示第二行地址
//4或8
printf("%d\n", sizeof(*(&a[0] + 1)));
//16
//第二行数组地址解引用
printf("%d\n", sizeof(*a));
//a数组名,没有单独放进sizeof中也没有&,所以表示的是首元素地址,表示第一行地址
//4或8
printf("%d\n", sizeof(a[3]));
//arr[3]表示第四行数组名,单独放进sizeof()内部表示计算整个数组大小
// a[3]类型是int[4]----sizeof(int[4])
// 答案是16,而不是越界访问
//根据类型来计算大小,不会真实访问内部空间
//表达式不参与运算,不进行计算
return 0;
}
练习总结:
- sizeof(数组名)表示计算的整个数组大小,数组名单独放进sizeof()才可以
- &数组名表示取出的整个数组的地址
- 处以上两个情况,其他数组名都表示首元素地址。
- sizeof()内部不参与运算,不进行真实的空间访问
- sizeof() 只关注占用内存空间的大小,单位是字节,不关心内存中放的是什么
- strlen是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到\0才能结束,所以可能存在越界访问的可能
- sizeof 是操作符 strlen是库函数
指针笔试真题
第一题:考察&数组名表示什么意思
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 main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
//ptr是个int*类型的指针,这里的&a表示取出的是整个数组的地址再+1的话,表示跳过整个数组大小的地址
//(&a+1)的类型是数组指针,然后这里将它强制类型转化成int *类型
printf("%d,%d", *(a + 1), *(ptr - 1));
//*(a+1),这里的a表示首元素地址,+1表示第二个元素地址,解引用访问第二个元素,2
//*(ptr-1),ptr被强制类型转换成int*类型,减1后就访问到最后一个元素的地址了
return 0;
}
总结:
&数组名表示取出的是整个数组的地址,+1表示跳过整个数组大小。
第二题:考察指针加1到底加多少?
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);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
结果:
解析:
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
//p+1取决于指针类型,这个指针是结构体类型大小为20,所以加1就跳过一个结构体大小20
//16进制的20为14
//0x100014
printf("%p\n", (unsigned long)p + 0x1);
//这里将p强制类型转换成无符号长整形,也就变成了整数了,整数加1就是简单的加减运算
//0x100001
printf("%p\n", (unsigned int*)p + 0x1);
//这里将p强制类型转换成int*类型,所以p+1跳过一个整形大小,也就是4个字节
//0x100004
return 0;
}
总结:
指针加+1到底加几呢?
指针加1跳过多少取决于指针类型
整数加一就是加1
第三题:考察指针解引用访问多大空间?
//环境是小段字节序存储,X86
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;
}
结果:
解析:
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
//&a取出的是整个数组地址,+1跳过整个数组大小,然后又被强制转换成int*类型
int* ptr2 = (int*)((int)a + 1);
//a表示首元素地址,强制类型转换成整形,这个地址+1,就是整数+1,然后又被强制转换成int*类型
printf("%x,%x", ptr1[-1], *ptr2);
//ptr1[-1]可以这样表示--> *(ptr-1)
//对ptr2这个指针解引用,能访问多大空间呢?
return 0;
}
总结:
指针解引用访问多少空间也取决于指针类型,要记住指针解引用是要看访问多少空间的喔,不要忘记啦。
第四题:考察观察,逗号表达式
#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 main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//注意这个不是将(0,1),(2,3),(4,5)放进数组,括号里是个逗号表达式,最终结果应该是{1,3,5};
//所以这个二维数组放的是1,3,5,0,0,0
int* p;
p = a[0];
//a[0]表示数组第一行的数组名,没有单独放进sizeof中,也没有&,所以表示首元素地址,放进p里
printf("%d", p[0]);
//p[0]表示*(p+0),就是第一个元素 1
return 0;
}
第五题:考察指针-指针表示什么意思
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;
}
结果:
解析:
指针-指针表示指针之间的元素个数,由于低地址减去高地址,所以为都为-4,但注意到,第一个是以%p的形式进行打印,是打印地址的,-4在内存中是以补码的形式进行保存,-4的原码反码补码分别是:
//100000000000000000000000000000100原码
//111111111111111111111111111111011反码
//111111111111111111111111111111100补码
补码直接转换成16进制形式就是
总结:指针-指针表示指针之间的元素个数
第6题:不同类型的指针+1表示跳过多少?
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;
}
结果:
解析:
上面有题目和这个类似
第7题:考察pa++,到底指什么意思呢?
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
结果:
解析:
总结:
pa++,其实是pa指向的元素进行++