• 【C】(笔试题)指针与数组,指针


    在这里插入图片描述
    博客主页:心辛向荣
    系列专栏:【从0到1,C语言学习】
    一句短话:你若盛开,蝴蝶自来!
    博客说明:尽己所能,把每一篇博客写好,帮助自己熟悉所学知识,也希望自己的这些内容可以帮助到一些在学习路上的伙伴,文章中如果发现错误及不足之处,还望在评论区留言,我们一起交流进步!😊

    前言

    🍁这篇博客的重点是对于指针以及数组的理解与运用,掌握这些题目,对于指针与数组的理解与认识会有很大的提高!

    一.关于指针和数组的笔试题

    • 数组名的意义:
    1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
    2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
    3. 除此之外所有的数组名都表示首元素的地址。
    • strlen和sizeof的区别:
    1. strlen是求字符串长度的,关注的是字符串中的\0,计算的是\0之前出现的字符的个数

    ​ strlen是库函数,只针对字符串

    1. sizeof只关注占用内存空间的大小,不在乎内存中放的是什么

    ​ sizeof是操作符

    • 正确理解题目:

    对于这部分的理解,sizeof(&…)计算的是一个指针类型的大小,也就是地址,地址的大小在32位环境(x86)下是4字节,在64位环境下(x64)是8字节;但对应下面的例题,不能只是单单理解打印结果,更重要的的是理解到这个地址到底是谁的地址;同样在理解strlen求字符串长度时,也要对地址有正确的认识和理解!

    1.一维数组

    #include<stdio.h>
    int main()
    {
    	int a[] = { 1,2,3,4 };
    	printf("%d\n", sizeof(&a + 1));//4/8
    	//&a取出的是数组的地址
    	//&a对应的指针类型是int(*)[4]
    	//&a+1 是从数组a的地址向后跳过了一个(4个整型元素)数组的大小
    	//&a+1还是地址,是地址就是4/8字节
    	printf("%d\n", sizeof(&a[0]));//4/8
    	//&a[0]就是第一个元素的地址
    	//计算的是地址的大小
    	printf("%d\n", sizeof(&a[0] + 1));//4/8
    	//&a[0]+1是第二个元素的地址
    	//大小是4/8个字节
    	//&a[0]+1 ---> &a[1]
    	printf("%d\n", sizeof(a));//16
    	//sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
    	printf("%d\n", sizeof(a + 0));//4
    	//a不是单独放在sizeof内部,也没有取地址,
    	//所以a就是首元素的地址,a+0还是首元素的地址
    	//是地址,大小就是4/8个字节
    	printf("%d\n", sizeof(*a));//4
    	//*a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素
    	//首元素类型是整形,大小是4个字节
    	printf("%d\n", sizeof(a + 1));
    	//这里的a是数组首元素的地址
    	//a+1是第二个元素的地址
    	//sizeof(a+1)就是地址的大小
    	printf("%d\n", sizeof(a[1]));//4
    	//计算的是第二个元素的大小
    	printf("%d\n", sizeof(&a));//4/8
    	//&a取出的数组的地址,数组的地址,也是个地址
    	printf("%d\n", sizeof(*&a));//16
    	//第1种理解方法
    	//&a----> int(*)[4]
    	//&a拿到的是数组名的地址,类型是 int(*)[4],是一种数组指针
    	//数组指针解引用找到的是数组
    	//*&a ---> a
    	//
    	//第2种理解方法
    	//&和*抵消了
    	//*&a ---> a
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    运行结果:

    img

    2.字符数组

    #include<stdio.h>
    int main()
    {
    	char arr[] = { 'a','b','c','d','e','f' };
    	printf("%d\n", sizeof(arr));//6
    	//sizeof(数组名),计算整个数组的大小
    	printf("%d\n", sizeof(arr + 0));//4/8
    	//arr + 0 是数组首元素的地址
    	printf("%d\n", sizeof(*arr));//1
    	//*arr就是数组的首元素,大小是1字节
    	//*arr --> arr[0]
    	//*(arr+0) --> arr[0]
    	printf("%d\n", sizeof(arr[1]));//1
    	//计算数组第二个元素的大小
    	printf("%d\n", sizeof(&arr));//4/8
    	//&arr是数组的地址,是地址就是4/8个字节
    	printf("%d\n", sizeof(&arr + 1));//4/8
    	//&arr + 1是数组后的地址
    	printf("%d\n", sizeof(&arr[0] + 1));//4/8
    	//&arr[0] + 1是第二个元素的地址
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行结果:

    img

    #include<stdio.h>
    #include <string.h>
    int main()
    {
    	char arr[] = { 'a','b','c','d','e','f' };
    
    	printf("%d\n", strlen(arr));//随机值
    	//没有'\0'作为结束标志
    	printf("%d\n", strlen(arr + 0));//随机值
    
        //printf("%d\n", strlen(*arr));//--> strlen('a');-->strlen(97);//野指针
        // 	   程序会报错
        //printf("%d\n", strlen(arr[1]));//-->strlen('b')-->strlen(98);
    
    	printf("%d\n", strlen(&arr));//随机值
    	printf("%d\n", strlen(&arr + 1));//随机值-6
    	printf("%d\n", strlen(&arr[0] + 1));//随机值-1
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

    img

    #include<stdio.h>
    #include<string.h>
    int main()
    {
    	char arr[] = "abcdef";
    	
    	//[a b c d e f \0]
    	printf("%d\n", strlen(arr));//6
    	printf("%d\n", strlen(arr + 0));//6
    	//printf("%d\n", strlen(*arr));//err
    	//  野指针,程序会报错
    	//printf("%d\n", strlen(arr[1]));//err
    	printf("%d\n", strlen(&arr));//6
    	printf("%d\n", strlen(&arr + 1));//随机值
    	printf("%d\n", strlen(&arr[0] + 1));//5
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行结果:

    img

    #include<stdio.h>
    int main()
    {
    	char arr[] = "abcdef";
    
    	//[a b c d e f \0]
    	printf("%d\n", sizeof(arr));//7
    	printf("%d\n", sizeof(arr + 0));//4/8
    	printf("%d\n", sizeof(*arr));//1
    	printf("%d\n", sizeof(arr[1]));//1
    	printf("%d\n", sizeof(&arr));//4/8
    	printf("%d\n", sizeof(&arr + 1));//4/8
    	printf("%d\n", sizeof(&arr[0] + 1));//4/8
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:

    img

    #include<stdio.h>
    int main()
    {
    	char* p = "abcdef";
    	printf("%d\n", sizeof(p));//4/8
    	printf("%d\n", sizeof(p + 1));//4/8
    	printf("%d\n", sizeof(*p));//1
    	printf("%d\n", sizeof(p[0]));//1
    	printf("%d\n", sizeof(&p));//4/8
    	printf("%d\n", sizeof(&p + 1));//4/8
    	printf("%d\n", sizeof(&p[0] + 1));//4/8
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行结果:

    img

    #include<stdio.h>
    #include<string.h>
    int main()
    {
    	char* p = "abcdef";
    	printf("%d\n", strlen(p));//6
    	printf("%d\n", strlen(p + 1));//5
    	//printf("%d\n", strlen(*p));//野指针,报错
    	//printf("%d\n", strlen(p[0]));//野指针,报错
    	printf("%d\n", strlen(&p));//随机值
    	printf("%d\n", strlen(&p + 1));//随机值
    	//由于p的地址占4/8格字节,无法确定'\0'会不会在其中出现
    	//所以推断不出这俩个随机值之间的关系
    	//这俩个随机值之间相差的值也是一个随机值
    	printf("%d\n", strlen(&p[0] + 1));//5
    	//p[0]-->*(p+0)
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行结果:

    img

    3.二维数组

    #include<stdio.h>
    int main()
    {
    	int a[3][4] = { 0 };
    	printf("%d\n", sizeof(a));//48
    	printf("%d\n", sizeof(a[0][0]));//4
    	printf("%d\n", sizeof(a[0]));//16
    	//a[0]是第一行这个一维数组的数组名,
    	//单独放在sizeof内部,a[0]表示第一个整个这个一维数组;
    	//sizeof(a[0])计算的就是第一行的大小
    	printf("%d\n", sizeof(a[0] + 1));//4/8
    	//a[0]并没有单独放在sizeof内部,也没取地址,a[0]就表示首元素的地址
    	//就是第一行这个一维数组的第一个元素的地址,
    	//a[0] + 1就是第一行第二个元素的地址
    	printf("%d\n", sizeof(*(a[0] + 1)));//4
    	//a[0] + 1就是第一行第二个元素的地址
    	//*(a[0] + 1))就是第一行第二个元素
    	printf("%d\n", sizeof(a + 1));//4/8
    	//a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址
    	//a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
    	//a+1就是跳过第一行,表示第二行的地址
    	printf("%d\n", sizeof(*(a + 1)));//16
    	//*(a + 1)是对第二行地址的解引用,拿到的是第二行
    	//*(a+1)-->a[1]
    	//sizeof(*(a+1))-->sizeof(a[1])
    	printf("%d\n", sizeof(&a[0] + 1));//4/8
    	//&a[0] - 对第一行的数组名取地址,拿出的是第一行的地址
    	//&a[0]+1 - 得到的是第二行的地址
    	printf("%d\n", sizeof(*(&a[0] + 1)));//16
    	printf("%d\n", sizeof(*a));//16
    	//a表示首元素的地址,就是第一行的地址
    	//*a就是对第一行地址的解引用,拿到的就是第一行
    	printf("%d\n", sizeof(a[3]));//16
    	//这里并不会造成越界访问,sizeof在计算时关心的的是a[3]的类型
    	//并不会真的去访问a[3],计算它和a[0]是一样的
    	printf("%d\n", sizeof(a[0]));//16
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行结果:

    img

    二.关于指针的笔试题

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:

    img

    解析:

    &a+1得到的是数组之后的地址,将这个地址强制类型转换为int * 类型,并赋值给int * 类型的prt,此时 * prt只能访问一个整形元素的空间;

    img

    *(a+1)中的a是数组首元素地址,a + 1是数组第二个元素的地址,由于prt只有一个整形空间的访问权限,prt - 1得到数组最后一个元素的地址。

    题2:

    #include <stdio.h>
    //已知,在32位环境下(x86)
    //结构体Test类型的变量大小是20个字节
    struct Test
    {
    	int Num;
    	char* pcName;
    	short sDate;
    	char cha[2];
    	short sBa[4];
    }* p = (struct Test*)0x100000;
    //假设p 的值为0x100000;如下表表达式的值分别为多少?
    int main()
    {
    	printf("%p\n", p + 0x1);
    	//0x100000+20-->0x100014
    	printf("%p\n", (unsigned long)p + 0x1);
    	//1,048,576+1 --> 1,048,577
    	//0x100001
    	printf("%p\n", (unsigned int*)p + 0x1);
    	//0x100000+4-->0x100004
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果:

    img

    解析:

    p的类型为struct Test * ,p+1跳过一个结构体(20字节)空间的大小,20转化成16进制就是14;

    unigned int 是一个无符号整形,进行整形运算即可;

    将p由结构体指针类型强制类型转化为unigned int* 类型,此时p+1跳过4个字节

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:

    img

    解析:

    prt1是数组后的地址,类型为int * ,prt1[-1]–>*(prt-1),prt-1向前跳过四个字节;

    将数组首元素地址强制类型转换为一个整形,此时 + 1进行的是整形运算;再将 + 1后的结果强制类型转换为int 类型赋值给prt2,此时prt2是a向后跳过一个字节的地址,* prt2访问4个字节的内容,

    而在vs环境下,数据采用的是小端存储模式,是倒着存的,看下图理解:

    img

    %x以16进制的形式打印。

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:

    img

    解析:

    这道题有一个忽悠人的点,题中的初始化用的是( )而不是 { } ;而题目( )中的是逗号表达式;

    所以数组当中的内容实际上相当于int a[3][2] = {1,2,3};

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:

    img

    解析:

    a是二维数组首元素地址,也就是二维数组第一行的地址,其类型为int ( * ) [5],具有5个字节的访问权限;将赋值给p,p的类型为int ( * )[4],具有4个字节的访问权限;

    指针 - 指针计算出的俩指针之间元素的个数,高地址 - 低地址得到的结果是整数,反之是负数;参照下图理解:

    img

    从图中可以观察到,俩个地址之间的元素个数为4,低地址 - 高地址,所以应该是 - 4;

    将结果以%p和%d打印;%d打印出的结果为 - 4;

    将 - 4以地址的形式打印;%p可以理解为一个无符号数,求出 - 4的补码转换为16进制即可;

    原码:10000000 00000000 00000000 00000100

    反码:11111111 11111111 11111111 11111011

    补码:11111111 11111111 11111111 11111100

    补码的十六进制:ff ff ff fc

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:

    img

    解析:

    &aa+1得到的是数组后的地址,将其强制类型转换为int * 类型,赋值给prt1,此时prt1具有4个字节的访问权限;所以prt1-1得到数组最后一个元素的地址;

    aa+1(相当于&arr[1])得到二维数组第二行的地址,* *(aa+1)得到第二行(相当于 * &aa[1]–>aa[1]);此时 ** (aa + 1 )并没有加&或者放在sizeof中,所以此时它相当于第二行数组首元素地址,题目这里进行强制类型转换属于迷惑行为,有没有都一样;将其赋值给prt2,prt2-1就是第一行最后一个元素的地址了!

    题7:

    #include <stdio.h>
    int main()
    {
    	char* a[] = { "work","at","alibaba" };
    	char** pa = a;
    
    	pa++;
    	printf("%s\n", *pa);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果:

    img

    解析:

    看图更直观,如下,

    img

    题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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行结果:

    img

    解析:

    要注意,如:++cpp,会将cpp自身的值改变;而cpp+1不会改变cpp本身;要注意区分,防止做题时混淆,具体理解如下图:

    img

    结语:

    各位小伙伴,看到这里就是缘分嘛,希望我的这些内容可以给你带来那么一丝丝帮助,可以的话三连支持一下呗😁!!! 感谢每一位走到这里的小伙伴,我们可以一起学习交流,一起进步😉!!!加油🏃!!!

    img

  • 相关阅读:
    阿杰的晚餐
    Spring Web MVC入门
    精品基于NET实现的数码新闻发布系统
    深圳大学计软《程序设计基础》实验三 选择结构
    docker-Dockerfile
    腾讯云服务器简介_CVM优势_常见问题解答
    stack类别
    【并发编程】史上最详细的ReentrantReadWriteLock源码刨析
    python jieba分词,一次性添加多个词
    LeetCode:1337. 矩阵战斗力最弱的 K 行、11. 盛最多水的容器、剑指 Offer 51. 数组中的逆序对题解
  • 原文地址:https://blog.csdn.net/Trong_/article/details/125601713