1. 指针笔试题
指针笔试题
- 题1
int main(int argc, const char * argv[])
{
int a[5] = {1,2,3,4,5};
int *p = (int *)(&a+1); // p指向5后面的那个地址
printf("%d,%d\n", *(a+1), *(p-1)); //2,5
return 0;
}
- 题2
struct test
{
int Num;
char *pcName;
shortsDate;
char cha[2];
shortsBa[4];
}*p;
假设结构体test的大小为20个字节,p的地址为0x100000。
p + 0x1 = ? // p+1 => +20
(unsigned long)p + 0x1 = ? // 0x100001 (eg: int a = 0, a + 1 = 1)
(unsigned int *)p + 0x1 = ? // 0x100004 (加一个指针的大小,32位平台下4)
- 题3
int main()
{
int a[4] = {1,2,3,4};
int *p1 = (int *)(&a+1);
int *p2 = (int *)((int)a+1);
printf("%x,%x\n", p1[-1], *p2);
// p1[-1] 输出 4
// *p2 这个编译可以通过,但是运行错误
return 0;
}
在内存中:
- 题4
int main()
{
int a[3][2] = { (0,1), (2,3), (4,5) };
int *p = a[0];
printf("%d\n", p[0]); // 输出1
return 0;
}
注意逗号表达式:运算结果为后面的值
所以:
- 题5
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]); //输出: -4的补码,-4
return 0;
}
输出他们之间元素的个数。
因为p[4][2]
的地址小于a[4][2]
的地址,所以为-4
,但是由于输出的时候,%p
输出的是地址,也就是一个无符号的数,所以将-4
的补码输出,%d
正常输出。
- 题6
int main()
{
int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
int *p1 = (int *)(&aa + 1);
int *p2 = (int *)(*(aa + 1));
printf("%d,%d\n", *(p1-1), *(p2-1)); // 输出10,5
return 0;
}
&aa+1
跨过了整个数组aa
的长度,指向元素10后的地址。
aa+1
代表的跨过了一个aa
的元素,而aa
是一个二维数组,它的元素是一个一维数组。
如图:
- 题7
int main()
{
char *a[] = {"work", "at", "360"};
char **pa = a;
pa++;
printf("%s\n", *pa); // 输出at
return 0;
}
- 题8
int main()
{
char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };
int **cp[] = { c+3, c+2, c+1, c };
char ***cpp = cp;
printf("%s\n", **++cpp); // POINT
printf("%s\n", *--*++cpp+3); // ER
printf("%s\n", *cpp[-2]+3); // ST
printf("%s\n", cpp[-1][-1]+1); // EW
return 0;
}
++、—
的优先级高于*
,*
的优先级高于+
。
++cpp
会影响cpp
的值,但cpp+1
不会影响。
[]
的优先级大于*
。
**++cpp
:先++
,此时的cpp
指向cp[1]
,解引用为c[2]
,再解引用即为POINT
。
*--*++cpp+3
:经过上一步,cpp
现在的指向如上图。
先++
,此时cpp
指向cp[2]
,解引用即为cp[2]
,再--
,此时改变了cp[2]
的指向,他指向c[0]
,再解引用即为c[0]
,给c[0]+3
,输出的结果为ER
。
*cpp[-2]+3
:经过上一步,现在的指向如上图所示。
cpp[-2]
指向了cp[0]
,解引用指向c[3]
,c[3]+3
输出ST
。
cpp[-1][-1]+1
:经过上一步并没有改变指针的指向。
cpp[-1][-1]
代表c[1]
,再+1
输出EW
。
2. 大量的sizeof()和strlen()练习,加深理解数组和指针
2.1)一维数组
int main(int argc, const char * argv[])
{
// 一维数组
int a[] = {1,2,3,4};
printf("----------\n");
printf("%p\t->\ta[0]\t->\t%d\n", &a[0], a[0]);
printf("%p\t->\ta[1]\t->\t%d\n", &a[1], a[1]);
printf("%p\t->\ta[2]\t->\t%d\n", &a[2], a[2]);
printf("%p\t->\ta[3]\t->\t%d\n", &a[3], a[3]);
printf("----------\n");
printf("sizeof(a)\t->\t%lu\n", sizeof(a));//a代表整个数组,16
printf("sizeof(a+0)\t->\t%lu\n", sizeof(a+0));//a+0代表的是数组首元素的地址,&a[0],这里32位输出4,64位输出8。
// printf("a+0\t->\t%p\n", a + 0);
// printf("a\t->\t%p\n", a);
printf("sizeof(*a)\t->\t%lu\n", sizeof(*a));//*a是数组第一个元素,4
// printf("*a\t->\t%d\n", *a);
printf("sizeof(a+1)\t->\t%lu\n", sizeof(a+1));//a+1代表数组第二个元素的地址,&a[1],8
// printf("%p\n", a+1);
printf("sizeof(a[1])\t->\t%lu\n", sizeof(a[1]));//a[1]的大小,4
printf("sizeof(&a)\t->\t%lu\n", sizeof(&a));//数组的地址,8
// printf("%p\n", &a);
printf("sizeof(*&a)\t->\t%lu\n", sizeof(*&a));//*和&抵消,相当于a代表整个数组,16
printf("sizeof(&a+1)\t->\t%lu\n", sizeof(&a+1));//这个是个地址,跨过了16个字节,8
// printf("%p\n", &a+1);
printf("sizeof(&a[0])\t->\t%lu\n", sizeof(&a[0]));//8
printf("sizeof(&a[0]+1\t->\t%lu\n", sizeof(&a[0]+1));//&a[1]
// printf("%p\n", &a[0]+1);
return 0;
}
2.2)字符数组
int main()
{
char a[] = {'a', 'b', 'c', 'd', 'e', 'f'};
printf("----------\n");
printf("%p\t->\ta[0]\t->%c\n", &a[0], a[0]);
printf("%p\t->\ta[1]\t->%c\n", &a[1], a[1]);
printf("%p\t->\ta[2]\t->%c\n", &a[2], a[2]);
printf("%p\t->\ta[3]\t->%c\n", &a[3], a[3]);
printf("%p\t->\ta[4]\t->%c\n", &a[4], a[4]);
printf("%p\t->\ta[5]\t->%c\n", &a[5], a[5]);
printf("----------\n");
printf("sizeof(a)\t->\t%d\n", sizeof(a));//a表示整个数组,6
printf("sizeof(a+0)\t->\t%d\n", sizeof(a+0));//表示&a[0],8
// printf("%p\n", a+0);
printf("sizeof(*a)\t->\t%d\n", sizeof(*a));//*a表示a[0],1
// printf("%c\n", *a);
printf("sizeof(a[1])\t->\t%d\n", sizeof(a[1]));//a[1],1
printf("sizeof(&a)\t->\t%d\n", sizeof(&a));//数组的地址,8
// printf("%p\n", &a);
printf("sizeof(&a+1)\t->\t%d\n", sizeof(&a+1));//字符f(a[5])后面的那个元素的地址,跨越了一个a的长度,8
// printf("%p\n", &a+1);
printf("sizeof(&a[0]+1)\t->\t%d\n", sizeof(&a[0]+1));//字符b的地址,8
// printf("%c\n", *(&a[0]+1));
printf("----------------\n");
printf("strlen(a)\t->\t%d\n", strlen(a));//这个不确定是多少,strlen是以\0为标志计算的,不知道a数组完后是否为'\0'
printf("strlen(a+0)\t->\t%d\n", strlen(a+0));
// printf("strlen(*a)\t->\t%d\n", strlen(*a));//ERROR!strlen()的参数类型为char*的
// printf("strlen(a[1])\t->\t%d\n", strlen(a[1]));//ERROR!strlen()的参数类型为char*的,做如下行的修改
printf("strlen(a[1])\t->\t%d\n", strlen(&a[1]));//OK
printf("strlen(&a)\t->\t%d\n", strlen(&a));//6
printf("strlen(&a+1)\t->\t%d\n", strlen(&a+1));//这个是不确定的
printf("strlen(&a[0]+1)\t->\t%d\n", strlen(&a[0]+1));//5
return 0;
}
2.3)字符串数组
int main()
{
char a[] = "abcdef";
printf("----------\n");
printf("%p\t->\ta[0]\t->%c\n", &a[0], a[0]);
printf("%p\t->\ta[1]\t->%c\n", &a[1], a[1]);
printf("%p\t->\ta[2]\t->%c\n", &a[2], a[2]);
printf("%p\t->\ta[3]\t->%c\n", &a[3], a[3]);
printf("%p\t->\ta[4]\t->%c\n", &a[4], a[4]);
printf("%p\t->\ta[5]\t->%c\n", &a[5], a[5]);
printf("----------\n");
printf("sizeof(a)\t->\t%d\n", sizeof(a));//7,字符数组长度,算\0
printf("sizeof(a+0)\t->\t%d\n", sizeof(a+0));//8,第一个元素的地址
printf("sizeof(*a)\t->\t%d\n", sizeof(*a));//1,第一个元素的大小
printf("sizeof(a[1])\t->\t%d\n", sizeof(a[1]));//1
printf("sizeof(&a)\t->\t%d\n", sizeof(&a));//8,地址
printf("sizeof(&a+1)\t->\t%d\n", sizeof(&a+1));//8,地址(是'\0'后面的那个字符的地址)
printf("sizeof(&a[0]+1)\t->\t%d\n", sizeof(&a[0]+1));//8,是a[1]的地址
printf("----------------\n");
printf("strlen(a)\t->\t%d\n", strlen(a));//6,字符串长度,不算\0
printf("strlen(a+0)\t->\t%d\n", strlen(a+0));//6
// printf("strlen(*a)\t->\t%d\n", strlen(*a));//ERROR!
// printf("strlen(a[1])\t->\t%d\n", strlen(a[1]));//ERROR!
printf("strlen(&a[1])\t->\t%d\n", strlen(&a[1]));//5
printf("strlen(&a)\t->\t%d\n", strlen(&a));//6
printf("strlen(&a+1)\t->\t%d\n", strlen(&a+1));//0
printf("strlen(&a[0]+1)\t->\t%d\n", strlen(&a[0]+1));//5
return 0;
}
2.4)字符串常量
int main()
{
char *p = "abcdef";
printf("--------\n");
printf("%p -> p[0] -> %c\n", &p[0], p[0]);
printf("%p -> p[1] -> %c\n", &p[1], p[1]);
printf("%p -> p[2] -> %c\n", &p[2], p[2]);
printf("%p -> p[3] -> %c\n", &p[3], p[3]);
printf("%p -> p[4] -> %c\n", &p[4], p[4]);
printf("%p -> p[5] -> %c\n", &p[5], p[5]);
printf("--------\n");
printf("%d\n", sizeof(p));//8,字符常量的地址
printf("%d\n", sizeof(p+1));//8,b字符的地址地址
// printf("%c\n", *(p+1));
printf("%d\n", sizeof(*p));//1,首元素的大小
printf("%d\n", sizeof(p[0]));//1,首元素的大小
printf("%d\n", sizeof(&p));//8,字符常量的地址
printf("%d\n", sizeof(&p+1));//8,这个并不是字符b的地址,是\0后面元素的地址
printf("%d\n", sizeof(&p[0]+1));//8,字符b的地址大小
printf("---------\n");
printf("%d\n", strlen(p));//6,字符串长度
printf("%d\n", strlen(p+1));//5,从b开始往后数字符串的长度
// printf("%d\n", strlen(*p));//ERROR!需要传入一个char *
// printf("%d\n", strlen(p[0]));//ERROR!和上面一样
printf("%d\n", strlen(&p));//这个值是不确定的,p不是数组,传入后会从p的地址往后一个字节一个字节的找,遇到\0停下
printf("%d\n", strlen(&p+1));//不确定
printf("%d\n", strlen(&p[0]+1));//5,从字符b开始往后数
return 0;
}
2.5)二维数组
int main()
{
int a[3][4] = {0};
printf("-------\n");
printf("%p -> a[0][0] -> %d\n", &a[0][0], a[0][0]);
printf("%p -> a[3][4] -> %d\n", &a[3][4], a[3][4]);
printf("&a[3][4] - &a[0][0] = %d\n", &a[3][4]-&a[0][0]);//一共有多少个元素
printf("3 行 4 列\n");
printf("-------\n");
printf("%d\n", sizeof(a));//48,16个元素,每个元素占4个字节
printf("%d\n", sizeof(a[0][0]));//4,第一个元素的大小
printf("%d\n", sizeof(a[0]));//16,第一行元素的大小
printf("%d\n", sizeof(a[0]+1));//8,a[0]是数组名,+1即为下一个元素的地址,a[0]+1 <=> a[0][1]
// printf("%p\n", a[0]+1);
printf("%d\n", sizeof(*(a[0]+1)));//4,a[0][1]的大小(int)
printf("%d\n", sizeof(a+1));//8,第二个元素的地址,即&a[1]
printf("%d\n", sizeof(*(a+1)));//16,第二行元素,相当于sizeof(a[1])
printf("%d\n", sizeof(&a[0]+1));//8,第二行数组的地址,相当于a+1
printf("%d\n", sizeof(*(&a[0]+1)));//16,相当于a[1]
printf("%d\n", sizeof(*a));//16,相当于a[0]
printf("%d\n", sizeof(a[3]));//16,虽然越界了,但是还是当前数组的类型
return 0;
}