目录
一、数组sizeof/strlen的练习
(1)整形数组-sizeof函数
(2)字符数组-sizeof函数
(3)字符数组-strlen函数
(4)字符串数组-strlen函数
(5)字符串数组-strlen函数
(6)指针变量-sizeof函数
(7)指针变量-strlen函数
(8) 二维数组-sizeof函数
二、指针笔试题
(1)笔试题1
(2)笔试题2
(3)笔试题3
(4)笔试题4
(5)笔试题5
(6)笔试题6
(7)笔试题7
(8)笔试题8
首先我们再来重新认识一下数组名的概念
数组名是首元素的地址
1.sizeof(数组名) - 数组名表示整个数组
2.&数组名 - 数组名表示整个数组
注意:
上面这两种情况都需要 sizeof() / &后面直接+数组名,如果不是直接 + 数组名,则不是表示整数数组。除了上述两种情况外,其余所有情况数组名均表示的是首元素地址。
其次地址的大小根据操作平台32位/64位的不同会开辟不同大小的空间。
32位开辟的是4个字节的空间大小,而64位开辟的是8个字节的空间大小。
那么接下去我们就来看题目叭!
写出下面程序执行的结果:
//一维数组
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));
//字符数组
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));
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));
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 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]));
看到这么多的代码是不是一下子有点头疼?没事,我们一点一点来进行分析:
#include
int main()
{
//1.数组名是首元素的地址
//2.sizeof(数组名) - 数组名表示整个数组
//3.&数组名 - 数组名表示整个数组
//一维数组
int a[] = {1,2,3,4};// 4*4 = 16字节
printf("%d\n",sizeof(a));
// 16 sizeof(数组名) 计算的是数组总大小单位是字节 4*4=16字节
printf("%d\n",sizeof(a+0));
// 4/8 数组名这里表示首元素的值,a+0还是首元素地址,地址的大小就是4/8个字节
printf("%d\n",sizeof(*a));
// 4 数组名表示首元素地址,*a就是首元素,是int型,sizeof(*a)为4个字节
printf("%d\n",sizeof(a+1));
// 4/8 - 数组名这里表示首元素地址,a+1为第二个元素的地址,地址的大小就是4/8个字节
printf("%d\n",sizeof(a[1]));
// 4 - 下标为1,即第2个元素的大小。为4个字节
printf("%d\n",sizeof(&a));
// 8 &a取出的是数组的地址,但是数组的地址也是地址,地址大小为4/8个字节
printf("%d\n",sizeof(*&a));
// 16 &a数组的地址,数组的地址解引用访问的数组,sizeof计算的就是数组的大小,单位是字节
printf("%d\n",sizeof(&a+1));
// 4/8 &a是数组的地址,&a+1,跳过整个数组,仍为一个地址,大小为4/8个字节
printf("%d\n",sizeof(&a[0]));
// 4/8 &a[0]是第一个元素的地址,地址大小为4/8个字节
printf("%d\n",sizeof(&a[0]+1));
// 4/8 &a[0]+1是第二个元素的地址,地址大小为4/8个字节
return 0;
}
结果:
#include
int main()
{
//字符数组
char arr1[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr1));
// 6 sizeof计算的是数组大小,由于arr1是一个指针,他里面有6个char类型的元素,6*1=6字节
printf("%d\n", sizeof(arr1+0));
// 4/8 数组名arr1不是单独放在数组内部,并且它没有取地址,+0后依然是首元素地址,地址为4/8个字节
printf("%d\n", sizeof(*arr1));
// 1 arr1是首元素地址,*arr1是首元素,首元素是字符大小为1个字节
printf("%d\n", sizeof(arr1[1]));
// 1 arr1[1]代表下标为1,即第2个元素的大小,为1个字节
printf("%d\n", sizeof(&arr1));
// 4/8 &arr1虽然是数组的地址,但他仍然是一个地址,所以地址大小是4/8个字节
printf("%d\n", sizeof(&arr1+1));
// 4/8 &arr1是数组的地址,&arr1+1跳过了整个数组,仍为一个地址,地址大小为4/8个字节
printf("%d\n", sizeof(&arr1[0]+1));
// 4/8 &arr1[0]是第一个元素的的地址,即首元素地址,而&arr1[0]+1是第二个元素的地址,地址大小就是4/8个字节
return 0;
}
结果:
#include
int main()
{
char arr[] = { 'a ', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", strlen(arr1));
// 随机值 arr表示首元素地址,数组中没有\0,所以用strlen计算长度的时候不知道会在哪里停止,所以是随机值
printf("%d\n", strlen(arr1+0));
// 随机值 随机值,arr+0表示首元素地址,数组中没有\0,所以用strlen计算长度的时候不知道会在哪里停止,所以是随机值
//printf("%d\n", strlen(*arr1));
// error strlen后面需要传递一个地址,*arr表示首元素,首元素是字符'a',其ASCII码是97,strlen会将97当作地址来处理,此时会造成越界访问,运行时会报错
//printf("%d\n", strlen(arr1[1]));
// error strlen后面需要传递一个地址,arr[1]表示第二个元素,首元素是字符'b',其ASCII码是98,strlen会将98当作地址来处理此时会造成越界访问,运行时会报错
printf("%d\n", strlen(&arr1));
// 随机值 &arr表示数组的地址,数组中没有\0,所以用strlen计算长度的时候不知道会在哪里停止,所以是随机值
printf("%d\n", strlen(&arr1+1));
// 随机值-6 &arr+1表示跳过数组后的地址,后面不知道什么时候遇到'\0',所以是随机值,但是要减去这个数组的大小,即随机值-6
printf("%d\n", strlen(&arr1[0]+1));
// 随机值-1 &arr[0] + 1表示第二个元素的地址,后面不知道什么时候遇到'\0',所以是随机值,但是这里由于少了一个a,所以随机值要减去1,即随机值-1
return 0;
}
结果:
#include
int main()
{
char arr2[] = "abcdef";
printf("%d\n", sizeof(arr2));
// 7 由于这是一个字符串,字符串后面有'\0',sizeof(arr2)计算的整个数组的大小,单位是字节,所以7*1=7
printf("%d\n", sizeof(arr2+0));
// 4/8 数组名arr2不是单独放在数组内部,并且没有取地址,+0后依然是首元素地址,地址为4/8个字节
printf("%d\n", sizeof(*arr2));
// 1 arr2是首元素地址,*arr2是首元素,首元素是a,字符大小为1个字节
printf("%d\n", sizeof(arr2[1]));
// 1 arr2[1]计算下标为1,即第二个元素,sizeof(arr2[1])计算的是第二个元素b的大小,因为类型是char,所以为1个字节
printf("%d\n", sizeof(&arr2));
// 4/8 &arr2是数组的地址,但也是地址,地址大小是4/8个字节
printf("%d\n", sizeof(&arr2+1));
// 4/8 &arr2是数组的地址,&arr2+1跳过了整个数组,但仍是一个地址,大小是4/8个字节
printf("%d\n", sizeof(&arr2[0]+1));
// 4/8 &arr2[0]是第一个元素的地址,即首元素地址,而&arr[0]+1是第二个元素的地址,大小为4/8个字节
return 0;
}
结果:
#include
int main()
{
char arr2[] = "abcdef";
printf("%d\n", strlen(arr2));
// 6 strlen是求'\0'前的元素个数,abcdef是6个元素,所以是6个字节
printf("%d\n", strlen(arr2+0));
// 6 arr2是首元素地址,数组名,+0仍一样,所以是6个字节
//printf("%d\n", strlen(*arr2));
// error arr2是首元素地址,*arr2传过去的值是a,a的ASCII码值是97,此时的空间不属于你所要访问的空间,属于非法访问,所以这里产生报错
//printf("%d\n", strlen(arr2[1]));
// error 同上,传的是b的值,b的ASCII码值是98,开辟空间仍然不属于应访问的空间,所以产生报错
printf("%d\n", strlen(&arr2));
// 6 &arr2是数组的地址,数组指针,而他仍是一个地址,strlen是求'\0'前的所有元素,所以是6个字节
printf("%d\n", strlen(&arr2+1));
// 随机值 &arr2跳过了整个数组,此时'\0'也被移动过去了,所以找不到'\0'的位置,产生的是随机值
printf("%d\n", strlen(&arr2[0]+1));
// 5 &arr2[0]是取首元素地址,+1看出向右移,取第二个元素b的地址,strlen求'\0'前的所有元素,所以此时是5个字节
return 0;
}
结果:
#include
int main()
{
char *p = "abcdef";
printf("%d\n", sizeof(p));
// 4/8 此时p是char *的指针变量,它里面存放的是首元素a的地址,指针变量p的大小为4/8个字节
printf("%d\n", sizeof(p+1));
// 4/8 此时p仍然是char *的指针变量,+1的得到的是第二个元素字符b的地址,地址的大小为4/8个字节
printf("%d\n", sizeof(*p));
// 1 *p就是字符串的首元素a,字符a的大小为1个字节
printf("%d\n", sizeof(p[0]));
// 1 p[0] == *p(p+0),就是字符串的首元素a,字符a的大小为1个字节
printf("%d\n", sizeof(&p));
// 4/8 &p就是变量p的地址,那么地址的大小为4/8个字节
printf("%d\n", sizeof(&p+1));
// 4/8 &p+1是跳过了一个p的地址,此时仍为地址,所以大小是4/8个字节
printf("%d\n", sizeof(&p[0]+1));
// 4/8 &p[0]是首元素的地址,+1后是元素b的地址,地址的大小是4/8个字节
return 0;
}
结果:
#include
int main()
{
char *p = "abcdef";
printf("\n%d\n", strlen(p));
// 6 strlen是求'\0'前的所有元素大小,这里p是数组名,首元素地址,所以从a开始,一共是6个元素,大小是6个字节
printf("%d\n", strlen(p+1));
// 5 p是首元素,即a的地址,+1后是b的地址,从b一直到'\0'前一共是5个元素,所以大小是5个字节
//printf("%d\n", strlen(*p));
// error *p为字符‘a’,ASCII码是97,strlen会将97当作地址来处理,此时会造成越界访问,运行时会报错
//printf("%d\n", strlen(p[0]));
// error p[0]为字符‘a’,ASCII码是97,strlen会将97当作地址来处理,此时会造成越界访问,运行时会报错
printf("%d\n", strlen(&p));
// 随机值 取出p的地址,传给strlen,strlen在往后寻找'\0',不知道什么时候遇到'\0'
printf("%d\n", strlen(&p+1));
// 随机值 取出p后面的地址,传给strlen,strlen在往后寻找'\0',不知道什么时候遇到'\0'
printf("%d\n", strlen(&p[0]+1));
// 5 p[0]表示首元素,&p[0]表示首元素地址,&p[0] + 1表示第二个元素'b'的地址,而strlen从'b'开始计算字符串长度
return 0;
}
结果:
在进行这段代码的剖析前,我们先来了解一些知识:
二维数组的数组名表示首元素地址时,首元素指的是第一行数组,也就是说首元素是数组,数组名表示的是第一行数组的地址。
代码:
#include
int main()
{
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//48 3*4*4=48
printf("%d\n",sizeof(a[0][0]));
//4 指向的是下标0,第一行的第一个元素,因为是int型,大小为4字节
printf("%d\n",sizeof(a[0]));
//16 a[0]相当于第一行作为一维数组的数组名,第一行一共是4个元素,大小为4*4=16字节
printf("%d\n",sizeof(a[0]+1));
//4/8 此时a[0]没有单独放到sizeof()表达式中,表示的是首元素地址,此时a[0]+1是第一行第二个元素的地址,所以是4/8个字节
printf("%d\n",sizeof(*(a[0]+1)));
//4 a[0]+1指向的是第一行第二个元素的地址,解引用后得到的是他的值,由于是int型,所以大小是4个字节。
printf("%d\n",sizeof(a+1));
//4/8 a是二维数组的数组名,没有sizeof(数组名),也没有&数组名,所以a是首元素地址,二维数组的首元素是第一行,此时a+1就是第二行地址,大小是4/8字节
printf("%d\n",sizeof(*(a+1)));
//16 由于a+1是第二行的地址,而数组的地址解引用就是找到这个数组,大小是4*4=16字节
printf("%d\n",sizeof(&a[0]+1));
//4/8 &a[0]是第一行的地址,+1后代表第二行的地址,地址大小是4/8字节
printf("%d\n",sizeof(*(&a[0]+1)));
//16 因为a[0]+1是第二行的地址,*解引用后就是第二行的值,大小是4*4=16字节
printf("%d\n",sizeof(*a));
//16 因为此时a不是单独放在sizeof()表达式中,所以此时数组名a是首元素的地址即第一行的地址,*解引用后就是第一行的值,所以是4*4=16字节
printf("%d\n",sizeof(a[3]));
//16 a[3]是第四行,虽然它不存在,但是a[3]其实等于a[0],即第一行的值,所以是16字节
return 0;
}
结果:
写代码的三种境界
1.看代码是代码
2.看代码是内存
3.看代码还是代码
程序的结果是什么?
#include
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
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
//&a表示取出数组a的地址,&a+1表示跳过该数组,指向数组后面的地址
printf("%d,%d", *(a + 1), *(ptr - 1));
//此时a表示首元素地址,a+1表示第二个元素地址,(a + 1)表示第二个元素---2
//ptr表示数组后面的地址,ptr - 1表示数组末尾的地址,(ptr - 1)表示数组最后一个元素---5
return 0;
}
结果:
2 , 5
#include
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
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;
}
解题思路:
这个题目考察的就是指针类型的意义,指针 + -的步长,指针 + 1到底是加几个字节。
#include
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
//0x1 指的是十六进制的1,转换成十进制也是1
//0x100014 此时p是结构体指针,结构体大小是20个字节,p+1其地址+20,而20转换成地址是0x00100014(这里后面的1为16,4就是4)
printf("%p\n", (unsigned long)p + 0x1);
//0x100001 此时p被强制类型转换成 unsigned long 类型,也就是整型,整型+1就是实实在在的+1,所以p+1就是地址+1(即整数+1)
printf("%p\n", (unsigned int*)p + 0x1);
//0x100004 p被强制类型转换成unsigned int*类型,p+1就是跳过一个int类型,地址+4
return 0;
}
结果:
00100014
00100001
00100004
程序的结果是什么?
#include
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表示取出数组的地址,&a+1表示跳过数组,到数组后面的地址
//(int*)(&a+1)将这个地址强制类型转换成int*,放到ptr1里面
int* ptr2 = (int*)((int)a + 1);
//a表示数组首元素地址,(int)a将这个地址强制类型转换成int类型
//(int)a + 1,就是地址+1,(int*)((int)a + 1)将这个地址强制类型转换成int*,放到ptr2里面
printf("%x,%x", ptr1[-1], *ptr2);
//4 ptr[-1]==>*(ptr-1)得到数组最后一个元素,即4,%x十六进制打印结果也是4
//02 00 00 00 *ptr2得到的就是被强制类型转换为int类型的地址的值+1,地址的单位是字节
//由于ptr2解引用是通过小端(字节序存储)访问 00 00 00 02 ,打印的时候就是 02 00 00 00
return 0;
}
结果:
4,2000000
程序的结果是什么?
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
解题思路:
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//初始化{}内部的( )是逗号表示式,结果是最后一个表达式的结果,即{1,3,5}
int* p;
p = a[0];
//将二维数组第一行第一个元素的地址赋值给p
printf("%d", p[0]);
//0 p[0]指向的是首元素地址,%d打印就是1
return 0;
}
答案:
1
程序的结果是什么?
#include
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
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]);
//指针:指针得到两者之间元素的个数
//&p[4][2] - &a[4][2]之间相差4个元素,且p
答案:
FFFFFFFC , -4
程序的结果是什么?
#include
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
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1);
//&aa + 1 表示的跳过整个数组,指向数组aa后面的地址
int* ptr2 = (int*)(*(aa + 1));
//*(aa + 1) 等价于aa[1],是指第二行的首元素6的地址
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//10 *(ptr1-1)所以此时指向10的地址,%d打印就是10
//5 *(ptr2-1)所以此时指向5的地址,%d打印就是5
return 0;
}
答案:
10 , 5
程序的结果是什么?
#include
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解题思路:
#include
int main()
{
char *a[] = { "work","at","alibaba" };
//此时数组a中存的都是字符指针,而字符指针存的都是首元素地址,所以存的是'w','a','a'
char **pa = a;
//pa此时指向了a的首元素地址,又由于a原来就有*,所以pa要用**
pa++;
//pa++,指向了第二个元素的地址,也就是a[1]的地址,a[1]内部存放"at"常量字符串的首地址
printf("%s\n", *pa);
//at *pa解引用a[1]的地址,%s打印字符串at
return 0;
}
答案:
at
程序的结果是什么?
#include
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
int main()
{
char* c[] = { "ENTER" , "NEW" , "POINT", "FIRST" };
char** cp[] = { c + 3, c + 2, c + 1, c };
char*** cpp = cp;
printf("%s\n", **++cpp);
//POINT 此时cpp因为++后,*++cpp后从指向cp中的'c+3'变为了'c+2',而'c+2'对应的是数组c中的POINT,所以%s打印POINT
printf("%s \n", *-- * ++cpp + 3);
//ER 首先++的优先级最高,所以*++cpp后从c+2指向了cp中的c+1,而--后,c+1中的1被减去了,所以就是c
//又因为*所以c指向的是'c'对应的数组c中的"ENTER",又+3,指向了ENTER中的第二个E,所以%s打印出的结果是ER
printf("%s \n", *cpp[-2] + 3);
//ST *cpp[-2]+3即**(cpp+(-2))+3又等价**(cpp-2)+3,而*(cpp-2)指向的是cp中的c+3的地址
//又因为*所以指向了'c+3'对应的c中存的"FISRT",又+3,指向了FIRST当中的S,所以%s打印出的结果是ST
printf("%s \n", cpp[-1][-1] + 1);
//EW cpp[-1][-1]+1等价于*(*(cpp-1)-1)+1,首先*(cpp-1),此时指向了cp中的c+2地址,而c+2-1后得到c+1
//又因为*所以此时指向了'c+1'对应的数组c中存的"NEW",又+1,指向了NEW中的E,所以%s打印出的结果是EW
return 0;
}
答案:
POINT
ER
ST
EW