C语言条记(指针标题)例题+图解

[复制链接]
发表于 2026-1-4 19:21:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
        本文分为两部门 ,第一部门为数组、字符串、字符指针在sizeof和strlen中的辨析,第二部门是一些笔试标题。如有错误,请品评指正。
  目次
1.第一部门
1.1.数组名的利用
1.1.1一维整型数组在sizeof中的利用
1.1.2一维字符数组在sizeof中的利用
1.1.3一维字符数组在strlen中的利用
1.1.4.一字符串在sizeof中的利用
1.1.5.一字符串在strlen中的利用
1.1.6二维数组在sizeof中的利用
1.2字符指针在sizeo和strlen中的利用
1.2.1 字符指针在sizeof中的利用
1.2.2 字符指针在strlen中的利用
2.第二部门(指针笔试题)


1.第一部门

注意:
   1.sizeof()是操纵符,只关注占用的内存空间的巨细,单位是字节,不访问内存中的存储的内容。
2.strlen()是库函数,用来求字符串的长度,统计的是字符'\0'出现之前的字符的个数,当函数在内存中找到’\0‘才算竣事,以是strlen大概会越界访问。
  3.以下全部的代码均在32位的情况中,因此地点为4个字节。
  3.数组名字的意义
           1. sizeof(数组名),这里的数组名表现整个数组,盘算的是整个数组的巨细。
        2. &数组名,这里的数组名表现整个数组,取出的是整个数组的地点。
        3. 除此之外全部的数组名都表现首元素的地点。
  1.1.数组名的利用

1.1.1一维整型数组在sizeof中的利用

  1. int main()
  2. {
  3.         int a[] = { 1,2,3,4 };
  4.         printf("%d\n", sizeof(a));  //16 ()中只有数组名,表示整个数组,所测量的是整个数组
  5.         printf("%d\n", sizeof(a + 0));//a+0就是数组首元素的地址,地址的大小为4个字节
  6.         printf("%d\n", sizeof(*a));//*a表是的数组首元素,是一个整型变量 4个字节
  7.         //a不是单独存在时,表示的首元素地址,*a就是元素  1
  8.         printf("%d\n", sizeof(a + 1));//a为首元素地址,+1之后还是地址  4个字节
  9.         printf("%d\n", sizeof(a[1]));// 数组第二个元素  4个字节
  10.         printf("%d\n", sizeof(&a));//&a数组指针,地址为 4个字节
  11.         printf("%d\n", sizeof(*&a));// * & 两者相互抵消,是整个数组, 16个字节
  12.         printf("%d\n", sizeof(&a + 1));//是地址,指向的是第二个元素, 4个字节
  13.         printf("%d\n", sizeof(&a[0])); //首元素地址  4个字节
  14.         printf("%d\n", sizeof(&a[0] + 1)); //第二个元素地址  4个字节
  15.         return 0;
  16. }
复制代码
1.1.2一维字符数组在sizeof中的利用

  1. int main()
  2. {
  3.         char arr[] = { 'a','b','c','d','e','f' };
  4.         //char类型数据大小为一个字节
  5.         printf("%d\n", sizeof(arr));// 6个字节,arr单独存放,是整个数组
  6.         printf("%d\n", sizeof(arr + 0));//4个字节,(arr+0)表示为首元素地址
  7.         printf("%d\n", sizeof(*arr));// 1个字节 ,arr表示首元素地址,*arr为首元素'a'
  8.         printf("%d\n", sizeof(arr[1]));// 1个字节,第二个元素'b'
  9.         printf("%d\n", sizeof(&arr));//4个字节  数组指针,&arr为地址
  10.         printf("%d\n", sizeof(&arr + 1));//4个字节  地址加1还是地址,数据类型不变
  11.         printf("%d\n", sizeof(&arr[0] + 1));//4个字节 取首元素的地址,+1还是地址
  12.         return 0;
  13. }
复制代码
1.1.3一维字符数组在strlen中的利用

        strlen 的返回值是字符的个数,情势参数是字符指针
  1. size_t strlen ( const char * str );
复制代码
    size_t                无符号整型,返回值
   const char * str  字符指针,情势参数  const为关键字限定str无法修改
  1. int main()
  2. {
  3.         char arr[] = { 'a','b','c','d','e','f' };
  4.         printf("%d\n", strlen(arr));        //随机值,arr为数组的首元素地址,但是数组中没有'\0'
  5.         printf("%d\n", strlen(arr + 0));//arr+0,arr是地址+0之后还是地址,函数无法接受,
  6.         printf("%d\n", strlen(*arr));        //表示是'a'— 97,非法访问,
  7.         printf("%d\n", strlen(arr[1])); //表示是'b'— 98,非法访问
  8.         printf("%d\n", strlen(&arr));   //数组指针,是指针会报警的,但是从数组指针开始计算还是随机值
  9.         printf("%d\n", strlen(&arr + 1));// 数组指针,+1 之后地址跳过数组,随机值
  10.         printf("%d\n", strlen(&arr[0] + 1));//表示的是第二个元素的地址,也是随机值
  11.         return 0;
  12. }
复制代码
1.1.4.一字符串在sizeof中的利用

  1. int main()
  2. {
  3.         char arr[] = "abcdef";
  4.         //数组中的元素是 a b c d e f \0  共7个元素
  5.         printf("%d\n", sizeof(arr));        // 7个字节,数组名字单独存放就是整个数组
  6.         printf("%d\n", sizeof(arr + 0));//4个字节 arr+0表示&arr[0],首元素地址
  7.         printf("%d\n", sizeof(*arr));        // 一个字节
  8.         printf("%d\n", sizeof(arr[1])); // 一个字节
  9.         printf("%d\n", sizeof(&arr));        // 四个字节
  10.         printf("%d\n", sizeof(&arr + 1));// 四个字节
  11.         printf("%d\n", sizeof(&arr[0] + 1));// 四个字节
  12.         return 0;
  13. }
复制代码
1.1.5.一字符串在strlen中的利用

        数组名在strlen函数中仍然是首个元素的地点。
  1. int main()
  2. {
  3.         char arr[] = "abcdef";
  4.         //数组中的元素是 a b c d e f \0  共7个元素
  5.         printf("%d\n", strlen(arr)); // 6 字符串中有6个字符 '\0' 不算在内
  6.         printf("%d\n", strlen(arr + 0));//6 表示&arr[0],首元素的地址,
  7.         printf("%d\n", strlen(*arr));//*arr是'a',非法访问
  8.         printf("%d\n", strlen(arr[1]));//arr[1]是'b',属于非法访问
  9.         printf("%d\n", strlen(&arr));//6  &arr是数组指针,
  10.         printf("%d\n", strlen(&arr + 1));// 5 &arr是数组指针,但是其指向的仍然是数组的首元素处
  11.         // +1 之后指向数组的第二个元素,跳过了第一个元素  
  12.         printf("%d\n", strlen(&arr[0] + 1));// 5  一个元素的地址 +1 跳过了 第一个元素
  13.         return 0;
  14. }
复制代码
1.1.6二维数组在sizeof中的利用

        二维数组的数组名表现的是首行元素的地点,是数组指针。arr[0]是第一行数组。
  1. int main()
  2. {
  3.         int a[3][4] = { 0 };
  4.         printf("%d\n", sizeof(a));// 48个字节,一共12个元素,一个元素 4个字节
  5.         printf("%d\n", sizeof(a[0][0]));// 第一个元素, 4 个字节
  6.         printf("%d\n", sizeof(a[0]));// 第一行数组  a[0]是第一行数组的名字,单独存在即一个数组  16个字节
  7.         printf("%d\n", sizeof(a[0] + 1));//a[0]为首元素地址,+1跳到第二个元素地址,4个字节
  8.         printf("%d\n", sizeof(*(a[0] + 1)));// 4个字节  第二个元素
  9.         printf("%d\n", sizeof(a + 1));// a + 1表示是a[1]是第二行地址 4个字节
  10.         printf("%d\n", sizeof(*(a + 1)));// 表示的是第二行的数组, 16个字节
  11.         printf("%d\n", sizeof(&a[0] + 1));// &a[0]是第一行的地址,+1 指是第二行的地址, 4个字节
  12.         printf("%d\n", sizeof(*(&a[0] + 1)));//第二行有四个元素, 16个字节
  13.         printf("%d\n", sizeof(*a));//a为首元素地址,就是第一行的地址, 16个字节
  14.     printf("%d\n", sizeof(a[3]));//访问的是数组的第四行元素,数组越界,但是可以计算,
  15.     //sizeof()中表达式是不会计算的,不会访问真是的空间,按照类型出的结果。
  16.         // *a -- *(a+0) -- a[0]
  17.         return 0;
  18. }
复制代码
        sizeof()操纵符的运算是在编译完成的,表达式的运算是在运行时完成的,先编译链接后运行,因此,sizeof只是按照()中式子的范例巨细得出结果。
1.2字符指针在sizeo和strlen中的利用

1.2.1 字符指针在sizeof中的利用

  1. int main()
  2. {
  3.         char *p = "abcdef";
  4.         printf("%d\n", sizeof(p));//p为指针 4个字节
  5.         printf("%d\n", sizeof(p + 1));// +1 跳出整个字符串地址4个字节
  6.         printf("%d\n", sizeof(*p));// p 指向的第一个元素 *p 就为 'a'  1个字节
  7.         printf("%d\n", sizeof(p[0]));// p[0] --*(p+0),首个元素   1个字节
  8.         printf("%d\n", sizeof(&p));//  二级指针   4个字节
  9.         printf("%d\n", sizeof(&p + 1));// 二级指针 +1 还是指针 4个字节
  10.         printf("%d\n", sizeof(&p[0] + 1));//第一个元素的指针 +1 表示的第二个元素的指针 4个字节
  11.         return 0;
  12. }
复制代码
1.2.2 字符指针在strlen中的利用

  1. int main()
  2. {
  3.         char *p = "abcdef";
  4.         printf("%d\n", strlen(p));// p是一个指向'a'的一个字符指针, 6
  5.         printf("%d\n", strlen(p + 1));// 5 指针+1  指针指向的是 'b'
  6.         printf("%d\n", strlen(*p));// 非法访问
  7.         printf("%d\n", strlen(p[0]));// 相当于 *(p+0)非法访问
  8.         printf("%d\n", strlen(&p));// 二级指针 随机值
  9.         printf("%d\n", strlen(&p + 1));// 随机值
  10.         printf("%d\n", strlen(&p[0] + 1)); //5 &p[0]相当于p了 , p+1指向第二个元素
  11.         return 0;
  12. }
复制代码
2.第二部门(指针笔试题)

本部门共八个标题,
标题一

  1. int main()
  2. {
  3.         int a[5] = { 1, 2, 3, 4, 5 };
  4.         int *ptr = (int *)(&a + 1);           // &a+1 已经跳出整个数组了
  5.         //强制转换成int*类型的指针
  6.         printf("%d,%d", *(a + 1), *(ptr - 1));//(a+1)中的a为首元素地址 a+1 只想第二个元素
  7.         return 0;                                                          
  8. }
  9. //结果为 2,5  
复制代码
分解
(1)第4行
  1. int *ptr = (int *)(&a + 1);   
复制代码

        &a为数组指针,指向的是数组的首元素地点;以是&a+1就可以跳出整个数组,+1它的步长就是整个数组的长度。
        (int*)为逼迫转换范例,(int *)(&a + 1)可以把数组指针&a+1逼迫转换成整型指针,而且将指针的值赋予ptr,那么ptr的值和(&a+1)是一样的,但是其范例,步长是不一样的
  1. printf("%d,%d", *(a + 1), *(ptr - 1))
复制代码
        a+1中的a表现首元素的地点,a+1表现是第二个元素的地点,*(a+1)解引用的就是 2
*(ptr-1),ptr的地点向低地点移动四个字节,指向的是第五个元素。*(ptr-1)为5。
标题二(指针加1和数值加1)

  1. #include <stdio.h>
  2. struct Test
  3. {
  4.         int Num;
  5.         char *pcName;
  6.         short sDate;
  7.         char cha[2];
  8.         short sBa[4];
  9. }*p;
  10. //假设p 的值为0x100000。 如下表表达式的值分别为多少?
  11. //结构体的大小是20字节
  12. int main()
  13. {
  14.         int p = 0x100000;
  15.         printf("%p\n", p + 0x1);
  16.         //p+1 0x100000+0x1=0x100001
  17.         printf("%p\n", (unsigned long)p + 0x1);
  18.         //强制转换成 无符号整型 0x100001
  19.         printf("%p\n", (unsigned int*)p + 0x1);
  20.         //强制转换成无符号整型指针 +1 就是+0x4  结果为 0x100004
  21.         return 0;
  22. //%p打印地址32位机器 为8位数 %x打印数值,不会有前面的00
  23. }
复制代码
     上述步调结果为0x00100001 0x00100001 0x00100004 
         *p为布局体指针,但是此题中根本没有利用到布局体指针的巨细和指向等题目,
  1. printf("%p\n", p + 0x1);
复制代码
        0x1对于十进制来说就是1;但是输出的是%p情势,以地点的方式输出,要输出8位数字。
p=0x100000+0x1=0x100001;输出的是0x00100001。
  1. printf("%p\n", (unsigned long)p + 0x1);
复制代码
        (unsigned long)p 将p逼迫转换成无符号整型,但是输出的仍然是地点的情势,因此和上面+1盘算一样,结果都是 0x00100001。
  1. printf("%p\n", (unsigned int*)p + 0x1);
复制代码
     (unsigned int*)p 将p逼迫转换成无符号指针范例,其巨细为4个字节,+1步长为4个字节,
因此p+1就是指针加4,结果就是0x00100001+0x4,输出为0x00100001。
标题三

  1. //机器为小端存储
  2. int main()
  3. {
  4.         int a[4] = { 1, 2, 3, 4 };
  5.         int *ptr1 = (int *)(&a + 1);
  6.         int *ptr2 = (int *)((int)a + 1);//a为数组的第一个元素的指针,强制转换成int 图解
  7.         //数组a 的存储方式 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
  8.         printf("%x,%x", ptr1[-1], *ptr2);//ptr[-1]-- *(ptr-1)
  9.         return 0;
  10. }
  11. //结果 0x4 ,0x2000000
  12. //%x就是打印16进制的字符
复制代码
        数组元素的存储方式为小端存储,小端存储就是数据低位放在地点值,数据高位放在高地点。
a[4]数组在内存中的存储方式:每一个小方块为一个字节,有8个bit的内存。


       
  1. int *ptr1 = (int *)(&a + 1);
复制代码
            &a为数组指针,指向的是数组的首元素地点;以是&a+1就可以跳出整个数组,+1它的步长就是整个数组的长度。
  1. int *ptr2 = (int *)((int)a + 1)
复制代码
          a为数组名,是首元素地点,逼迫转化成int范例,+1就是数字+1,逼迫转换成int*范例,a在原来地点的根本上向高地点挪动一个字节,相当于指向第一个整型元素的第2个字节。*ptr,输出的是整型元素,必要从低地点到高地点四个字节,从第一个元素的第二个字节,到第二个元素的第一个字节,因此 *ptr就是 0x2000000 。
  标题四

  1. #include <stdio.h>
  2. int main()
  3. {
  4.         int a[3][2] = { (0, 1), (2, 3), (4, 5) };
  5.         //int a[3][2] = {1,3,5};
  6.         int *p;
  7.         p = a[0];//a[0]是第一行的数组名,是第一行的首元素地址
  8.         printf("%d", p[0]);//p[0] -- *(p+0)
  9.         return 0;
  10. }
复制代码
   结果为 1 。
  1. int a[3][2] = { (0, 1), (2, 3), (4, 5) }
复制代码
         (0,1)逗号表达式,表达式从左向右,末了表达式的结果就是整个表达式的结果。那么得出:
  1. int a[3][2] = { 1, 3, 5 }
复制代码
  1. p = a[0]
复制代码
        a[0]为二维数组首行数组的名字,数组名字就是首元素的地点,将首元素地点赋值给p,输出的自然是首元素1啊。
标题五

  1. int main()
  2. {
  3.         int a[5][5];
  4.         int(*p)[4];//数组指针
  5.         p = a;//
  6.         printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
  7.         return 0;//二者的步长一致,相差为4  图解
  8.         //fffffffc  -4
  9. }
复制代码

  1. a等价于 int (*)[5] 指向是元素为5的数组
  2. p等价于 int (*)[4] 指向是元素为4的数组
  3. 根据图&p[4][2] - &a[4][2]相差四个元素 结果为-4
  4. -4 的原码 10000000 00000000 00000000 00000100
  5.      反码 10000000 00000000 00000000 00000011
  6.      补码 11111111 11111111 11111111 111111100
  7.     地址不会区分原码、反码 、补码,地址就是内存中存储的补码。
  8.     转换成16进制 ff ff ff fc 
复制代码
  1. p = a;
复制代码
        将a的int (*)[5]的数值强行赋值给了p;p的范例是int (*)[4],指向的是四个指针的数组。
a为首元素地点,是第一行的数组指针,范例是int (*)[5]。
数组在内存中存储的排序为

        逼迫复制后p就为a地点,但是p指向的是int[4]范例的数组,因此步长为4个整型数据,假设所模拟后的数组为4个元素为一行。

图解
  1. &p[4][2] - &a[4][2],
复制代码

        可以看出,此中心相差4个步长,数值为-4。
标题六

  1. int main()
  2. {
  3.         int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  4.         int *ptr1 = (int *)(&aa + 1);                  
  5.         int *ptr2 = (int *)(*(aa + 1));//a为首行元素的地址,+1 跳过是整个第一行的地址
  6.         //指向的是第二行的首元素的地址, ptr2为整型地址,-1 向低地址处跳动一个整型指向 5
  7.         printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));  
  8.         return 0;
  9.         //结果为 10,5
  10. }
复制代码
  1. int *ptr1 = (int *)(&aa + 1);
复制代码
        &aa为数组指针 +1,跳过是整个数组,逼迫装换成整型指针,ptr1为整型指针
  1. *(ptr1 - 1)
复制代码
        式子中ptr1为整型指针 -1 表现指针向低地点移动四个字节,指向的数组末了一个元素10的地点。 
  1. int *ptr2 = (int *)(*(aa + 1));
复制代码
        a为首行元素的地点,+1 跳过是整个第一行的地点,逼迫转换成整型指针,指向的数组第二行第一个元素的地点,-1向低地点处移动四个字节,相当于 指向一第行末了一个元素的地点。
标题七

  1. #include <stdio.h>
  2. int main()
  3. {
  4.         char *a[] = { "work","at","alibaba" };
  5.         char**pa = a;//a为数组名,指向的是数组第一个元素字符串的首个字符的地址,a为二级指针
  6.         //  pa++ 相当于 a++ a+1 相当于a[1]表示数组中的第二个元素  *(a[1])为字符串"at"首字符地址
  7.         pa++;
  8.         printf("%s\n", *pa);
  9.         return 0;
  10. }
复制代码
  1. char**pa = a;
复制代码
        a是数组名是指针数组首元素的地点,也是二级指针,将a赋值给pa ,pa也是字符指针 +1 ,表现相当于 pa指向指针数组的第二个元素的首个字符的指针,解引用之后得到的是吗“at”字符串的首字符的地点,因此可以直接输出全部。
标题八

  1. int main()
  2. {
  3.         char *c[] = { "ENTER","NEW","POINT","FIRST" };//指针数组 存储的是一级指针
  4.         char**cp[] = { c + 3,c + 2,c + 1,c };//指针数组 存储的二级指针
  5.         char***cpp = cp;//三级指针,将cp数组的首元素地址赋值给cpp
  6.         printf("%s\n", **++cpp);
  7.         printf("%s\n", *--*++cpp + 3);
  8.         printf("%s\n", *cpp[-2] + 3);
  9.         printf("%s\n", cpp[-1][-1] + 1);
  10.         return 0;
  11. }
  12. //结果
  13. //POINT
  14. //ER
  15. //ST
  16. //EW
复制代码
  1. printf("%s\n", **++cpp);
复制代码
        cpp指向的是cp数组首元素的地点 ++cpp指向的使得cp数组的第二个, c+2,表现的数组c的第三个元素,因此输出是“ POINT”   .
  1. printf("%s\n", *--*++cpp + 3);
复制代码
        ++cpp,指向的是数组cp的第三个元素,c+1,解引用 为 c+1,在--(c+1)就为 c,解引用之后就为数组c的首元素地点 ,+3跳过前三个字符,输出的就是 ER。
  1. printf("%s\n", *cpp[-2] + 3);
复制代码
        cpp[-2]相当于 *(cpp-2);那么 **(cpp-2)就是数组c中的末了一个元素 FIRST,+3跳过前三个字符,结果就是 ST。
  1. printf("%s\n", cpp[-1][-1] + 1);
复制代码
        cpp[-1][-1] ,相当于 *(*(cpp-1)-1),*(cpp-1)指向的是 c+2 ,  *(c+2-1),指向是第二个元素,+1跳过一个字符,输出为EW。
           标题可以加深理指针的只是,如有错误,还盼望各位指正。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表