• 深度讲解指针的笔试题目


    主页:C语言的前男友

    知识讲解:C语言指针

    创作者:C语言的前男友

    开发环境:VS2022

    前言:前面学了好久的指针,今天来看一些组织的面试题,来帮助我们加深理解。今天主要看一些关于sizeof(),和strlen(),对数组,字符串的操作。通过练习深度理解指针。深刻理解数组与指针,数组名与指针,字符串与指针,二维数组指针之间的关系。欢迎大家前来指正,如果觉得作者写的还不错的话,请麻烦动动发财的小手,关注,点赞,收藏,评论

     

    目录

    一.一维数组

    (1) sizeof()的操作对象是数组名

    (2)sizeof()操作对象是指针

     (3)sizeof()的操作对象是普通变量或者普通变量类型

     二.指针类型对指针的影响

    1.指针的类型决定了指针在加减一个整数,指针移动的空间长度。

    2.指针的类型决定了指针在解引用时,一次能访问多少字节的空间。

     三.字符数组

     (1)strlen()的底层原理

    (2)strlen()和字符数组

     四.字符串数组

    (1)sizeof()和字符串数组

     (2)strlen()和字符串数组

    六.二维数组

     (1)二维数组的数组名

     最后:

    月是人间散客,卿是人间绝色,亦是人间难得。


    一.一维数组

    1. int a[] = { 1,2,3,4 };
    2. printf("%d\n", sizeof(a));
    3. printf("%d\n", sizeof(a + 0));
    4. printf("%d\n", sizeof(*a));
    5. printf("%d\n", sizeof(a + 1));
    6. printf("%d\n", sizeof(a[1]));
    7. printf("%d\n", sizeof(&a));
    8. printf("%d\n", sizeof(*&a));
    9. printf("%d\n", sizeof(&a + 1));
    10. printf("%d\n", sizeof(&a[0]));
    11. printf("%d\n", sizeof(&a[0] + 1));

    (1) sizeof()的操作对象是数组名

    数组名表示的是数组首元素的地址,但是有两个例外:

    1.数组名是 sizeof 的操作对象

    2. & + 数组名 ,

    上述两种情况比较特殊,此时的数组名表示的是,整个数组。也就是说,sizeof(数组名)计算的是整个数组的大小,& + 数组名,代表是的取出整个数组的地址。

    1. printf("%d\n", sizeof(a));
    2. printf("%d\n", sizeof(*&a));

     &a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小。

    (2)sizeof()操作对象是指针

    针对 sizeof 这个操作符,是用来求类型的大小的(单位:字节),当然也可以求指针的大小,我们知道一个普通指针在内存中占 4 个字节或者 8 个字节,至于到底是几个字节,取决于计算机平台,在32位平台是 4 字节,在 64 位平台是 8 字节。

    1. printf("%d\n", sizeof(a + 0));
    2. printf("%d\n", sizeof(a + 1));
    3. printf("%d\n", sizeof(&a));
    4. printf("%d\n", sizeof(&a + 1));
    5. printf("%d\n", sizeof(&a[0]));
    6. printf("%d\n", sizeof(&a[0] + 1));

    这里(a+0)如果是 a 单独是sizeof的操作对象,那么此时 a 就是代表整个数组,但是(a+0)就不代表整个数组。(a+0)就是一个普通指针,指向数组的第一个元素。也就是数组的首元素的地址。(a+1)就是数组第二个元素的地址。(&a)也是一个指针,是一个数组指针类型的指针,但仍然是一个指针。(&a +1 ): &a 是取出了整个数组的地址,是一个数组指针类型的指针,(&a +1 )跳过整个数组的地址。(这里的针对指针加上一个整数的运算,后面会单拎出来好好讲讲)所以(&a+1)在实际上还是一个指针。是指针大小就是,4 或 8 字节。&arr[0]实际上也是一样,拿到是首元素的地址,(&a[0] + 1):就是在首元素的指针上往后跳过一个元素,也就是第二个数组元素的地址,无论 &a[0] , &a[0] + 1都是指针类型。所以大小自然都是 4 或者 8 字节。

    32平台(X86)展示:

     64平台(X64 )展示:

     (3)sizeof()的操作对象是普通变量或者普通变量类型

    1. printf("%d\n", sizeof(*a));
    2. printf("%d\n", sizeof(a[1]));

    a并没有单独放在sizeof()里面,所以 a 是首元素的地址,(*a)也就是首元素了,a[1],数组访问第二个元素,都是每一个数组元素又都是 int 类型。所以这两个结果都是 4 。

     二.指针类型对指针的影响

    刚才我们遇到了,&a + 1 是越过了整个数组的地址,那到底为什么呢?下面我们就来好好分析一下。

    1.指针的类型决定了指针在加减一个整数,指针移动的空间长度。

    举例:

    1. int main()
    2. {
    3. int a = 0;
    4. char ch = 0;
    5. int arr[3] = { 0 };
    6. int* pa = &a;
    7. char* pc = &ch;
    8. int(*parr)[3] = &arr;
    9. printf("pa=%p,pa+1=%p\n", pa, pa + 1);
    10. printf("pc=%p,pc+1=%p\n", pc, pc + 1);
    11. printf("parr=%p,parr+1=%p\n", parr, parr + 1);
    12. return 0;
    13. }

    这个地方明显看出,pa 是整形指针,pa + 1只越过了 4 个字节。pc 是一个字符型指针,pc + 1越过了 1 个字节。parr 是一个数组指针,数组有三个元素,每个元素都是一个 int 型数据。所以数组的大小是 4*3=12,所以 parr + 1 越过了 12 个字节。

    2.指针的类型决定了指针在解引用时,一次能访问多少字节的空间。

    举例:

    1. int main()
    2. {
    3. int a = 0x11223344;
    4. int* pa = &a;
    5. char* pc = &a;
    6. short* ps = &a;
    7. printf("%x\n", *pa);
    8. printf("%x\n", *ps);
    9. printf("%x\n", *pc);
    10. return 0;
    11. }

    我们将十六进制数 11223344 存入a的四个字节空间,因为pa是整形指针,(*pa)一次就可以访问四个字节,所以,十六进制打印出来的(*pa)就是原数据11223344,ps 是短整型指针,(*ps)一次就可以访问两个字节,由于我的机器采用的是小端存储模式,所以在存储时是将数据的权重低位,放在了地址的低地址处,而读取内存还是从地址在高地址读取,所以十六进制打印出来的恢复数据的高低位就是 3344 ;pc是 char * 类型,(*pc)一次访问一个字节,也就是内存低位的一个字节,在我的机器上就是数据的权重低位,也就是44;

     三.字符数组

    1. char arr[] = { 'a','b','c','d','e','f' };
    2. printf("%d\n", strlen(arr));
    3. printf("%d\n", strlen(arr + 0));
    4. printf("%d\n", strlen(*arr));
    5. printf("%d\n", strlen(arr[1]));
    6. printf("%d\n", strlen(&arr));
    7. printf("%d\n", strlen(&arr + 1));
    8. printf("%d\n", strlen(&arr[0] + 1));

     (1)strlen()的底层原理

    strlen()是一个计算字符串长度的库函数,计算的原理是:接受字符串的首字符指针,直到找到字符串的结束标志也就是‘  \0 ’ 结束。

    (2)strlen()和字符数组

    由于字符数组的没有 ‘ \0 ’作为结束标志,所以strlen()函数在什么位置寻找到 ‘ \0 ’也是不能知道的。所以在计算长度的时候,自然也就是一个随机值。

    1. char arr[] = { 'a','b','c','d','e','f' };
    2. printf("%d\n", strlen(arr));
    3. // 从 arr 出开始找‘ \0 ’
    4. printf("%d\n", strlen(arr + 0));
    5. //仍然是从 arr 处开始找‘\0’
    6. printf("%d\n", strlen(&arr));
    7. // 由于 &arr 和 arr 虽然类型不同,但是都是指向数组的首元素地址,
    8. // 都是从数组的首元素地址处开始的寻找‘ \0 ’
    9. printf("%d\n", strlen(&arr + 1));
    10. // &arr + 1 从越过整个数组后的地址处开始寻找‘ \0 ’
    11. printf("%d\n", strlen(&arr[0] + 1));
    12. // &arr[0]仍然是指向首元素的地址,&arr[0] + 1 是从数组第一个元素开始寻找‘\0’

     因为&arr+1 跨过了一个数组,所以strlen(&arr + 1)的随机值会比strlen(arr),

    strlen(arr + 0),strlen(&arr) 小 6.而&arr[0]+1只只是跨过了一个字符,strlen(&arr[0]+1)所以会比strlen(arr),strlen(arr + 0),strlen(&arr) 小 1。

    代码运行结果:

     由于strlen()参数需要的是指针,所以下面的代码就是错误的,由于内存访问冲突引起的。

    1. printf("%d\n", strlen(*arr));
    2. printf("%d\n", strlen(arr[1]));

    代码运行结果:

     四.字符串数组

    1)sizeof()和字符串数组

    1. char arr[] = "abcdef";
    2. printf("%d\n", sizeof(arr));
    3. printf("%d\n", sizeof(arr+0));
    4. printf("%d\n", sizeof(*arr));
    5. printf("%d\n", sizeof(arr[1]));
    6. printf("%d\n", sizeof(&arr));
    7. printf("%d\n", sizeof(&arr+1));
    8. printf("%d\n", sizeof(&arr[0]+1));

    我们要知道字符串在数组里面是怎么存储的:

     我们知道注意:

    printf("%d\n", sizeof(arr));

    这里的arr是整个数组,而这里的数组有在最后多存了一个‘ \0 ’。所以加上‘ \0 ’一共七个字节。

    其他的还是和数组一样的分析。

    运行结果:

    X86环境

     X64环境:

     (2)strlen()和字符串数组

    1. char arr[] = "abcdef";
    2. printf("%d\n", strlen(arr));
    3. printf("%d\n", strlen(arr+0));
    4. printf("%d\n", strlen(*arr));
    5. printf("%d\n", strlen(arr[1]));
    6. printf("%d\n", strlen(&arr));
    7. printf("%d\n", strlen(&arr+1));
    8. printf("%d\n", strlen(&arr[0]+1));

    这里大家只要注意,strlen()求字符串的长度的原理就行了。

    六.二维数组

    1. int a[3][4] = {0};
    2. printf("%d\n",sizeof(a));
    3. printf("%d\n",sizeof(a[0][0]));
    4. printf("%d\n",sizeof(a[0]));
    5. printf("%d\n",sizeof(a[0]+1));
    6. printf("%d\n",sizeof(*(a[0]+1)));
    7. printf("%d\n",sizeof(a+1));
    8. printf("%d\n",sizeof(*(a+1)));
    9. printf("%d\n",sizeof(&a[0]+1));
    10. printf("%d\n",sizeof(*(&a[0]+1)));
    11. printf("%d\n",sizeof(*a));
    12. printf("%d\n",sizeof(a[3]));

     (1)二维数组的数组名

    二维数组的数组名,还是和一维数组差不过,除了sizeof( 数组名 )和 & + 数组名。其他的数组名都是,首元素的地址。注意:这个时候二维数组的首元素,不再是第一个元素,而是把二维数组看成一维数组,每一行看作一个一维数组,那么数组名是首元素的地址,也就是第一行一维数组的地址。

    1. int main()
    2. {
    3. int a[3][4] = { 0 };
    4. printf("%d\n", sizeof(a));
    5. //sizeof(a)数组名代表整个数组,
    6. //代表求整个数组的大小。
    7. printf("%d\n", sizeof(a[0][0]));
    8. //代表是求第一个数组元素的大小。
    9. printf("%d\n", sizeof(a[0]));
    10. //a是二维数组的数组名,是数组的第一行的地址,
    11. // a[0]相当于*(a+0),找到了数组第一行。
    12. //所以sizeof(a[0])算的是二维数组第一行的大小。
    13. printf("%d\n", sizeof(a[0] + 1));
    14. //a[0]其实就是二维数组第一行的数组名,
    15. // (a[0]+1)就是一个指向二维第一行第二个元素的指针。
    16. //算的是一个指针的大小。
    17. printf("%d\n", sizeof(*(a[0] + 1)));
    18. //在(a[0]+1)的基础上在解引用,
    19. //就是二维第一行第二个元素。
    20. printf("%d\n", sizeof(a + 1));
    21. //a是一个指向第一行数组的指针,
    22. // a+1就是一个指向第二行数组的指针。
    23. //算的是一个指针的大小
    24. printf("%d\n", sizeof(*(a + 1)));
    25. //(a+1)是指向二维数组第二行的地址,
    26. // 解引用以后就是找到了第二行
    27. //所以算的是第二行的大小
    28. printf("%d\n", sizeof(&a[0] + 1));
    29. //&a[0]取出第一行的地址,
    30. //等价于数组名 a ,所以等价于 a + 1
    31. printf("%d\n", sizeof(*(&a[0] + 1)));
    32. //等价于*(a+1)
    33. printf("%d\n", sizeof(*a));
    34. //等价于a[0]
    35. printf("%d\n", sizeof(a[3]));
    36. //等价于 *(a+3),虽然数组没有第四行,
    37. // 但是*(a+3)类型能够确定,
    38. // 就是 int [4],就是整形四元素数组。
    39. return 0;
    40. }

    运行效果(X86):

     最后:

    月是人间散客,卿是人间绝色,亦是人间难得。

     

     

  • 相关阅读:
    武汉新时标文化传媒有限公司“土味”正在崛起,短视频的春天来了
    因为一行Log日志导致的线上P1事故
    UG\NX二次开发 选择基准平面 UF_UI_select_with_single_dialog
    .NET性能优化-推荐使用Collections.Pooled
    hibernate ehcache.xml
    Stellar Toolkit for MySQL 9.0 Crack 3in1
    【分布式能源的选址与定容】基于非支配排序多目标粒子群优化算法求解分布式能源的选址与定容附Matlab代码
    Linux中间件之分析redis的跳表实现
    Google Earth Engine(GEE)——一个免费下载Landsat影像的APP
    2024.4.23 关于 LoadRunner 性能测试工具详解 —— VUG
  • 原文地址:https://blog.csdn.net/qq_63943454/article/details/126749399