目录
9.指针和数组面试题的解析
在进行面试题的解析之前,我想先介绍一下 sizeof 和 strlen 函数
- sizeof 计算大小,主要看类型,根据类型来判断字节大小
- strlen 是库函数,求字符串的长度,统计的是在字符串中\0之前的字符的个数
如果没有\0,就会一直往后找……
使用strlen函数传参传的是地址
一维数组
#include<stdio.h>
int main()
{
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));
return 0;
}
运行结果:
解释如下:
printf("%d\n", sizeof(a));//->16
//在sizeof中,数组名表示整个数组,因此大小是4*4
printf("%d\n", sizeof(a + 0));//->4/8
//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
//首元素地址+0,还是首元素地址,大小是4/8个字节
printf("%d\n", sizeof(*a));//->4
//a此时不属于特殊情况,因此a代表首元素地址
//*解引用操作符,*a代表首元素类型是int,大小是4个字节
printf("%d\n", sizeof(a + 1));//->4/8
//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
//首元素地址+1,是第二个元素地址,大小是4/8个字节
printf("%d\n", sizeof(a[1]));//->4
//a[1]表示第二个元素,类型是int,大小是4个字节
printf("%d\n", sizeof(&a));//->4/8
//&a是数组的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*&a));//->16
//*和&相互抵消,所以相当于sizeof(a)
//在sizeof中,数组名表示整个数组,因此大小是4*4
// 理解二:
//&a-->int(*)[4]
//对数组指针解引用,其实就是访问数组
printf("%d\n", sizeof(&a + 1));//->4/8
//&a是数组的地址,&a + 1还是地址,是地址就是4/8个字节
printf("%d\n", sizeof(&a[0]));//->4/8
//a[0]代表数组元素,&取地址符,即&a[0]的地址大小是4/8个字节
printf("%d\n", sizeof(&a[0] + 1));//->4/8
//a[0]代表数组元素,&取地址符
//&a[0] + 1代表第二个元素的地址,大小是4/8个字节
字符数组
1.将单个字符放入数组中
1.1使用sizeof
#include<stdio.h>
int main()
{
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));
return 0;
}
运行结果:
解释如下:
printf("%d\n", sizeof(arr));//->6
//在sizeof中,数组名表示整个数组,因此大小是1*6
printf("%d\n", sizeof(arr + 0));//->4/8
//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
//首元素地址+0,还是首元素地址,大小是4/8个字节
printf("%d\n", sizeof(*arr));//->1
//a此时不属于特殊情况,因此a代表首元素地址
//*解引用操作符,*a代表首元素类型是char,大小是1个字节
printf("%d\n", sizeof(arr[1]));//->1
//arr[1]代表第二个元素,类型是char,大小是1个字节
printf("%d\n", sizeof(&arr));//->4/8
//&arr代表整个数组的地址,是地址大小就是4/8个字节
printf("%d\n", sizeof(&arr + 1));//->4/8
//&arr代表整个数组的地址,&arr + 1后,仍然表示地址,是地址大小就是4/8个字节
printf("%d\n", sizeof(&arr[0] + 1));//->4/8
//&arr[0]表示数组首元素的地址,&arr[0] + 1表示第二个元素的地址,是地址大小就是4/8个字节
2.使用 strlen 库函数
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
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));
return 0;
}
运行结果:
解释如下:
char arr[] = { 'a','b','c','d','e','f' };
//strlen是库函数
//求字符串的长度,统计的是在字符串中\0之前的字符的个数
// 如果没有\0,就会一直往后找……
printf("%d\n", strlen(arr));//->随机值
//因为字符数组中没有\0,所以在求字符串长度时,会一直往后找,因此是随机值
printf("%d\n", strlen(arr + 0));//->随机值
//数组名是首元素地址,arr + 0还是首元素地址,所以会一直往后找\0,因此是随机值
//printf("%d\n", strlen(*arr));//->error
//arr是数组首元素的地址,*arr就是数组首元素,即'a'->97
//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
//那就是将97作为地址传参
//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
//printf("%d\n", strlen(arr[1]));//->error
//arr[1]是数组第二个元素,即'b'->98
//strlen 函数参数部分需要传一个地址,当我们传递的是'b'时,字符'b'的ASCII值是98
//那就是将98作为地址传参
//strlen就会从98这个地址开始统计字符串长度,这就属于非法访问内存
printf("%d\n", strlen(&arr));//随机值
//&arr是数组的地址,类型是 char(*)[6],数组的地址和数组首元素的地址,值是一样的
//那么传递给strlen 函数后,依然是从数组的第一个元素的位置开始往后统计
//strlen 的参数是 const char * str
printf("%d\n", strlen(&arr + 1));//->随机值
//&arr是指向首元素的地址,&arr + 1跳过整个数组,然后开始向后数
printf("%d\n", strlen(&arr[0] + 1));//->随机值
//&arr[0]是第一个元素的地址,&arr[0] + 1是第二个元素的地址
//从'b'的位置开始向后数
2.将字符串放在数组中
2.1使用 sizeof
#include<stdio.h>
int main()
{
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));
return 0;
}
运行结果:
解释如下:
char arr[] = "abcdef";
//数组是[a b c d e f \0] 字符串数组隐藏着 \0
printf("%d\n", sizeof(arr));//->7
//在sizeof中,数组名表示整个数组,类型是char [7]
//因此大小是7
printf("%d\n", sizeof(arr + 0));//->4/8
//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
//首元素地址+0,还是首元素地址,大小是4/8个字节
printf("%d\n", sizeof(*arr));//->1
//arr是首元素地址,*arr表示数组元素,类型是char,大小是1个字节
printf("%d\n", sizeof(arr[1]));//->1
//arr[1]代表数组第二个元素,类型是char,大小是1个字节
printf("%d\n", sizeof(&arr));//->4/8
//&arr 表示整个数组的地址,是地址就是4/8
printf("%d\n", sizeof(&arr + 1));//->4/8
//&arr 表示整个数组,&arr + 1表示跳过整个数组后的地址
//但还是属于地址,是地址就是4/8
printf("%d\n", sizeof(&arr[0] + 1));//->4/8
//&arr[0]表示第一个元素的地址,&arr[0] + 1表示第二个元素的的地址
//是地址大小就是4/8个字节
2.2 使用 strlen 库函数
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
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));
return 0;
}
运行结果:
解释如下:
char arr[] = "abcdef";
//数组是[a b c d e f \0] 字符串数组隐藏着 \0
//strlen是库函数
//求字符串的长度,统计的是在字符串中\0之前的字符的个数
// 如果没有\0,就会一直往后找……
printf("%d\n", strlen(arr));//->6
//arr是数组首元素地址,strlen 从数字首元素地址开始向后找 \0
//即大小是6个字节
printf("%d\n", strlen(arr + 0));//->6
//arr是数组首元素地址,arr + 0还是数组首元素地址
//strlen 从数字首元素地址开始向后找 \0,即大小是6个字节
//printf("%d\n", strlen(*arr));//->error
//*arr 相当于 arr[0],即从数组首元素开始向后找 \0
//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
//那就是将97作为地址传参
//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
//printf("%d\n", strlen(arr[1]));//->error
//arr[1] 是数组第二个元素,即从数组第二个元素开始找\0
//strlen 函数参数部分需要传一个地址,当我们传递的是'b'时,字符'b'的ASCII值是98
//那就是将98作为地址传参
//strlen就会从98这个地址开始统计字符串长度,这就属于非法访问内存
printf("%d\n", strlen(&arr));//->6
//&arr表示数组地址,数组地址的开始就是数组首元素的地址
//从数组首元素的地址开始向后找\0
//即大小就是6个字节
printf("%d\n", strlen(&arr + 1));//->随机值
//&arr表示数组地址,&arr + 1表示跳过整个数组,一个数组的大小是7
//从数组后的地址开始找\0 ,此时,并不知道哪儿有\0
//因此是随机值
printf("%d\n", strlen(&arr[0] + 1));//->4/8
//&arr[0]表示数组首元素地址,&arr[0] + 1表示数组第二个元素的地址
//从第二个元素往后数,即大小是5个字节
3.字符指针
3.1使用 sizeof
#include<stdio.h>
int main()
{
const 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));
return 0;
}
运行结果:
解释如下:
const char* p = "abcdef";
//字符指针,指针变量指向的是第一个字符的地址
printf("%d\n", sizeof(p));//->4/8
//p是指针变量,大小是4/8个字节
printf("%d\n", sizeof(p + 1));//->4/8
//p是指针变量,指向第一个字符
//p 是 char * 类型,+1 跳过一个字节
//p + 1指向第二个字符,即字符 b 的地址
//是地址大小就是4/8个字节
printf("%d\n", sizeof(*p));//->1
//*p是首元素,字符 a ,大小是 1 个字节
printf("%d\n", sizeof(p[0]));//->1
//p[0] ->* (p+0)
//表示数组首元素,字符 a ,大小是 1 个字节
printf("%d\n", sizeof(&p));//->4/8
//&p表示指针变量p的地址,即二级指针
//同样属于地址,大小是4/8个字节
printf("%d\n", sizeof(&p + 1));//->4/8
//&p表示指针变量p的地址,即二级指针
//&p + 1同样属于地址,大小是4/8个字节
printf("%d\n", sizeof(&p[0] + 1));//->4/8
//&p[0]->&(*(p+0))->p
//p+1代表第二个字符 b 的地址
//p+1 仍是指针变量,大小是4/8个字节
3.2 使用 strlen 函数
#include<stdio.h>
#include<string.h>
int main()
{
const char* p = "abcdef";
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));
return 0;
}
运行结果:
解释如下:
const char* p = "abcdef";
//字符指针,指针变量指向的是第一个字符的地址
printf("%d\n", strlen(p));//->6
//p是指针变量,存的是 a 的地址,遇到 \0 停下来
printf("%d\n", strlen(p + 1));//->5
//p是指针变量
//p+1存的是 b 的地址,遇到 \0 停下来
//printf("%d\n", strlen(*p));//->error
//*p表示字符 a ,即从 a 开始向后找 \0
//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
//那就是将97作为地址传参
//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
//printf("%d\n", strlen(p[0]));//->error
//p[0]->*(p+0),表示数组首元素,字符 a ,,即从 a 开始向后找 \0
//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
//那就是将97作为地址传参
//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
printf("%d\n", strlen(&p));//->随机值
//&p表示取出指针变量 p 的地址,即二级指针
//不知道指针变量 p 的地址,不知道啥时候可以找到\0,因此是随机值
printf("%d\n", strlen(&p + 1));//->随机值
//&p表示取出指针变量 p 的地址,即二级指针
//&p+1表示指针变量 p 向后移动,和字符串没有任何关系
//不知道指针变量 p 的地址,不知道啥时候可以找到\0,因此是随机值
printf("%d\n", strlen(&p[0] + 1));//->5
&p[0]->&(*(p+0))->p
//p+1代表第二个字符 b 的地址,遇到 \0 停下来
二维数组
#include<stdio.h>
int main()
{
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]));
return 0;
}
运行结果:
解释如下:
int a[3][4] = { 0 };
//二维数组相当于每行是一个一维数组
printf("%d\n", sizeof(a));//->48
//sizeof直接+数组名,数组名此时表示整个数组,3*4*4=48
printf("%d\n", sizeof(a[0][0]));//->4
//a[0][0]表示二维数组第一行第一个元素,类型是 int,大小是 4 个字节
printf("%d\n", sizeof(a[0]));//->16
//a[0]表示二维数组的第一行,计算的是整个数组的大小
//第一行有四个元素,即4*4=16
printf("%d\n", sizeof(a[0] + 1));//->4/8
//a[0]是二维数组中的第一行(即一维数组的数组名),此时并没有单独放在 sizeof 内部,也没有&
//因此,a[0]表示数组首元素地址,即a[0][0]的地址
//a[0] + 1表示一维数组的第二个元素的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(a[0] + 1)));//->4
//a[0] + 1表示一维数组的第二个元素的地址
//*(a[0] + 1))表示数组第二行元素,类型是 int ,大小是 4 个字节
printf("%d\n", sizeof(a + 1));//->4
//a此时没有单独放在 sizeof 里面,a 表示第一行的一维数组的地址,类型是 int(*)[4]
//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]表示数组第一行的地址,&a[0] + 1表示数组第二行的地址
//地址就是4/8个字节
printf("%d\n", sizeof(*(&a[0] + 1)));//->16
//&a[0]表示数组第一行的地址,&a[0] + 1表示数组第二行的地址
//*(&a[0] + 1))表示数组第二行的元素,共四个元素,即4*4=16
printf("%d\n", sizeof(*a));//->16
//a此时没有单独放在 sizeof 里面,a 表示第一行的一维数组的地址,类型是 int(*)[4]
//*a表示一维数组内的元素,一共四个元素,即4*4=16
printf("%d\n", sizeof(a[3]));//->16
//a[3]的类型是int [4],即4*4=16
对于最后一个,我想再解释一下:sizeof 只关注类型。
10.指针笔试题
笔试题1:
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:
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p = (struct Test*)0x100000;//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;
}
运行结果:
解释如下:
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p = (struct Test*)0x100000;//p 是结构体指针
//假设p 的值为0x100000。如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);//+1跳过一个结构体(20个字节)
printf("%p\n",(unsigned long) p + 0x1);//强制转换为长整型,整型加1就是1 0x100000+1->0x100001
printf("%p\n",(unsigned int *) p + 0x1);//int ,+1 跳过4个字节 0x100004
return 0;
}
笔试题三:
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[3][2] = { (0,1),(2,3),(4,5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
运行结果:
解释如下:
笔试题五:
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p\n", &p[4][2]-&a[4][2]);
printf("%d\n", &p[4][2] - &a[4][2]);
return 0;
}
运行结果:
解释如下:
笔试题六:
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;
}
运行结果:
解释如下:
笔试题七:
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
运行结果:
解释如下:
笔试题八:
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;
}
运行结果:
这个题就留给大家自己思考吧!!!!!