当前位置 博文首页 > HyDraZya的博客:【C语言进阶】C指针详解之练习(附解析)

    HyDraZya的博客:【C语言进阶】C指针详解之练习(附解析)

    作者:[db:作者] 时间:2021-09-05 09:43

    目录

    一、数组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个字节的空间大小。

    ?那么接下去我们就来看题目叭!

    一、数组sizeof/strlen的练习

    写出下面程序执行的结果:

    //一维数组
    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]));
    

    看到这么多的代码是不是一下子有点头疼?没事,我们一点一点来进行分析:

    (1)整形数组-sizeof函数????????

    #include <stdio.h>
    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;
    }

    结果:


    (2)字符数组-sizeof函数

    #include <stdio.h>
    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;
    }

    ?结果:

    ?


    (3)字符数组-strlen函数

    #include <stdio.h>
    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;
    }

    ?结果:


    (4)字符串数组-strlen函数

    #include <stdio.h>
    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;
    }

    结果:?


    (5)字符串数组-strlen函数

    #include <stdio.h>
    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;
    }

    结果:


    ?(6)指针变量-sizeof函数

    #include <stdio.h>
    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;
    }

    结果:


    ?(7)指针变量-strlen函数

    #include <stdio.h>
    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;
    }

    结果:


    (8)?二维数组-sizeof函数

    在进行这段代码的剖析前,我们先来了解一些知识:

    二维数组的数组名表示首元素地址时,首元素指的是第一行数组,也就是说首元素是数组,数组名表示的是第一行数组的地址。

    ?代码:

    #include <stdio.h>
    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.看代码还是代码

    (1)笔试题1

    程序的结果是什么?

    #include <stdio.h>
    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 <stdio.h>
    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


    (2)笔试题2

    #include <stdio.h>
    //由于还没学习结构体,这里告知结构体的大小是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 <stdio.h>
    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


    (3)笔试题3

    程序的结果是什么?

    #include <stdio.h>
    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


    (4)笔试题4

    程序的结果是什么?

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

    解题思路:

    #include <stdio.h>
    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


    (5)笔试题5

    程序的结果是什么?

    #include <stdio.h>
    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 <stdio.h>
    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<a 得到-4,所以用%d打印就是-4,
    	//-4原码:10000000000000000000000000000100
    	//反码是:11111111111111111111111111111011
    	//补码是:11111111111111111111111111111100
    	//此时通过补码直接打印地址,所以%p打印的就是FFFFFFFC
    	return 0;
    }

    答案:

    FFFFFFFC , -4


    (6)笔试题6

    程序的结果是什么?

    #include <stdio.h>
    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 <stdio.h>
    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


    (7)笔试题7

    程序的结果是什么?

    #include <stdio.h>
    int main()
    {
    	char* a[] = { "work","at","alibaba" };
    	char** pa = a;
    	pa++;
    	printf("%s\n", *pa);
    	return 0;
    }
    

    解题思路:

    #include <stdio.h>
    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


    (8)笔试题8

    程序的结果是什么?

    #include <stdio.h>
    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 <stdio.h>
    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;
    }