指针笔试题及总结

前言

指针是C的精髓,没有指针,那么C就没有它的灵魂。

因为指针的灵活性,使它有更多的玩法,也导致了学习指针的难度。

因此,本文特别选了几组常见的笔试题,来深入体会指针。

一、重温要点

sizeof--关键字,编译时运算符,用来判断变量和数据的大小

注意:sizeof是在编译时执行,将数据替换为相应类型,所以不会执行结果

strlen--库函数

用于计算字符串的长度,遇到'\0'停止

数组名

在一般情况下数组名表示首元素地址

在俩种特殊情况下除外

  • sizeof(数组名)  sizeof后单独放数组名,这里计算的是整个数组的大小
  • &数组名  取出的是整个数组的地址。如果要接收结果,那么应该用数组指针来接收

指针的大小

不论指针的类型,在32为平台上为4,64位平台上位8

二、指针和一维数组

求下列各组的结果

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;
}

题解:

一维整形数组,有4个元素,每个元素大小为4

  • sizeof单独放数组名,计算整个数组大小,得4*4=16字节
  • sizeof后没有单独放数组名,所以a表示首元素地址,首元素地址+0,依旧表示首元素地址,大小为     4或者8(下文简写4/8)
  • sizeof后没有单独放数组名,所以a为首元素地址,*a表示首元素,相当于a[0],为整形   4
  • a表示首元素地址,首元素地址+1,表示第二个元素的地址,大小为    4 / 8
  • a[1]       表示第二个元素,就是 数据2 的大小      4
  • &a          &a,为整个数组的地址,是指针    4/8
  • *&a        *和&是一对互逆运算符,二者作用后相当于a,可以写成sizeof(a)   16
  • &a+1      &a表示整个数组的地址,+1后还是地址,是指针      4/8
  • &a[0]      取出第一个元素的地址,是指针     4/8
  • &a[0]+1  先取出第一个元素的地址,再+1,第二个元素的地址,是指针  4/8

答案:16    4/8     4     4/8    4     4/8    16     4/8     4/8     4/8

三、字符数组

1.字符数组和sizeof

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));
}

题解:

字符数组,元素为{a,b,c,d,e,f}, 6个元素,每个元素大小为1字节

  • 数组名单独放在sizeof内部,计算整个数组的大小     6
  • arr+0  arr是首元素地址,+0后还是首元素地址,是指针   4/8
  • *arr 首元素地址解引用,是第一个元素  char类型    1
  • arr[1]    ,第二个元素     1
  • &arr   取出整个数组的地址, 是指针        4/8
  • &arr+1 

再内存中,跳过6个字节,但是&a+1 依旧是指针  4/8

  • &a[0]+1, &a[0]表示第一个元素地址,+1 为第二个元素地址  4/8

 答案:6      4/8     1     1    4/8   4/8    4/8

2.字符数组和strlen

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));
}

题解:

strlen求长度,遇到'\0'  停止    

  • 从首元素地址往后找,直到遇到'/0'                               长度是随机值 
  • arr是首元素地址,首元素地址+0 ,还是首元素地址     随机值
  • *arr 是字符  'a'  字符A的ASCii为97,编译器就从地址97处访问 ,空间不属于数组非法访问  
  • arr[1],是字符‘b’,同上                                                    非法访问
  • &arr   取出整个数组的地址,但是strlen的参数会强制类型转换为char* 每次还是访问一个字节,所以 同1,2 长度是                                                 随机值       
  • &arr+1  从字符f 的后一个元素向后访问   长度为随机值减去 6
  • &arr[0]+1 第一个元素的地址+1 第二个元素的地址         随机值-1

答案:随机值        随机值        非法访问           非法访问         随机值       随机值-6      随机值-1

3.字符串和sizeof

int main()
{
	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));
}

题解:字符串数组元素为{a,b,c,d,e,f,'\0'}  7个元素,p为char类型的指针变量

  • p为首元素地址  是指针  4/8
  • p+1  首元素地址+1  第二个元素地址  是指针  4/8
  • *p第一个元素 字符'a'   char类型    1
  • p[0]  相当于*p  是字符'a'  char类型    1
  • &p p为首元素地址, &p是一个二级指针,是指针    4/8
  • &p+1  一级指针的地址 +1  是指针   4/8 
  • &p[0]+1   &p[0]是 首元素地址   +1  是第二个元素的地址  是指针  4/8 

答案:4/8    4/8   1    1    4/8   4/8   4/8 

4.字符串和strlen

int main()
{
	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;
}

题解:p为字符串首元素地址,字符串元素{a,b,c,d,e,f,'\0'}

  • 求字符串长度     6
  • p+1 首元素地址+1  从第二个元素开始计算,直到'\0'           5
  • *p 字符  ’a'  ,ascii为97 ,从地址97处向后访问   是非法访问 
  • p[0]  相当于*p  同上 非法访问
  • &p 是首元素地址的地址 二级指针      随机值
  • &p+1   首元素地址的地址+1 ,跳过4/8个字节   是随机值,并且与&p没有关系,因为不能知道在这4/8字节中存在 '/0'
  • &p[0]+1,第一个元素的地址+1 第二个元素的地址            5

答案:6        5       非法访问         非法访问        随机值          随机值             5

四、二维数组

难点

int main()
{
	int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	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]));
}

题解:

二维数组 ,在内存中是一块连续的空间,12个元素,每个元素为int类型

a[1]相当于一维数组名,指向第一行首元素的地址

a[2]相当于一维数组名,指向第二行首元素的地址

  • sizeof后单独数组名,表示整个数组的大小,4*12=48
  • a[0][0]   a[0]是第一个一维数组的数组名,a[0][0]表示第一行第一个元素  int 类型  4
  • a[0]  相当于第一个元素的数组名, sizeof(数组名)表示整个第一行数组大小   16
  • a[0]+1 a[0]没有单独放在sizeof内部 表示第一行第一个数组的地址,+1 是第二个数组的地址,是指针  4/8 
  • *(a[0]+1)第一行第二个元素地址解引用,第一行第二个元素 int 类型 4
  • a没有单独放在sizof内部,表示首元素地址,二维数组首元素是第一行地址,第一行地址+1,是第二行地址,是指针      4/8  
  • (a+1)是第二行地址 类型是 int(*)[4],解引用后是int [4],大小是      16
  • &a[0] a[0] 相当于维数组数组名 &数组名的类型是数组指针,+1跳过一个数组指针的大小,是指针                              4/8
  • *(&a[0]+1)这里不存在越界访问,因为sizeof在编译时进行数据替换,计算一个数组值的大小。  类型是 int(*)[4],   大小是16
  • *a   a是首元素地址,二维数组首元素的地址是第一行地址 类型是int(*)[4]  , *a就是第一行   16 
  • a[3]   不存在数组越界访问   理由同上  a[3]的类型是int (*) [4]    大小是16

 答案:48   4  16  4/8     4   4/8  16  4/8    16   16  16 

五、常见笔试

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

创建一个5个元素的整形数组

&a表示整个元素的地址 类型是int(*)[5],  +1跳过4*5  20个字节,指向5后面一位的地址

 然后将ptr强制类型转换为int*   指针解引用从原本访问20字节变为访问4字节,所以ptr-1访问就是元素5的地址  

答案 : 2      5

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;
}

&a取出整个数组的地址,类型是int(*)[4],+1跳过16个字节,强制类型装换为int*

ptr1[-1]就是元素4

a表示首元素地址,a强制类型转换为整形  整形+1,跳过一个字节 ,在强制类型转换为int*类型的指针,解引用时,一次访问4个字节(默认小端存储)得到16进制数字 2 00 00 00

答案:4,2  00 00 00

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

二维数组中存在逗号表达式,所以相当于  {1,3,5,0,0,0}

a[0]相当于一维数组的数组名,p[0]是第一行一个元素   1

答案:1

int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

题解:

a是指针数组,数组有三个元素 。

数组名a是首元素地址,用二级指针pa来接收

pa++,跳过一个指针的字节(32位4,64位8),指向下一个指针 at的地址

打印字符串at

答案:at

总结

指针是C的重点,也是难点

熟练使用指针,是必要的

指针和数组,二级指针和二维数组经常让我们搞不清,唯有多练多写,才能柔韧有余!

本文旨在帮助大家不再害怕指针,指针的练习远远还不够,希望大家能亲历亲为

代码++

offer++

猜你喜欢

转载自blog.csdn.net/m0_73299809/article/details/129331323