C语言 指针(5) 数组和指针题解析

 

目录

前言

一、sizeof和strlen

二、数组和指针笔试题解析

2.1、一维数组

2.2 字符数组

2.3 二维数组

三、指针运算笔试题解析

总结


前言

今天我们继续来了解指针,讲一下sizeof和strlen的区别,数组和指针题解析


一、sizeof和strlen

我们之前在讲了sizeof操作符,sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的大小。

sizeof 只关注占⽤内存空间的大小,不在乎内存中存放什么数据。
#inculde <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a));//4printf("%d\n", sizeof a);//4printf("%d\n", sizeof(int));//4return 0;
}

输出结果都是4,而且不计算sizeof里面的内容:

#inculde <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a=a+3));//4return 0;
}
a的值还是10,sizeof 只关注占⽤内存空间的大小,不会计算表达式。
strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:
size_t strlen ( const char * str );
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符,直到找到为止,所以可能存在越界查找。
#include <stdio.h>
int main()
{char arr1[3] = {'a', 'b', 'c'};char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr1));return 0;
}

在arr1字符数组中,没有\0,所以strlen的结果为随机值,sizeof中数组名为数组大小,有三个字符,所以sizeof的结果为3。

在arr2字符串数组中,有'a','b','c','\0',所以strlen的结果为3,sizeof值同上。

sizeof 和 strlen的对⽐:
sizeof
strlen
1. sizeof是操作符
2. sizeof计算操作数所占内存的大小,单位是字节
3. 不关注内存中存放什么数据
1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界

二、数组和指针笔试题解析

2.1、一维数组

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

让我们依次来分析:

sizeof(  )                               解析   结果
           asizeof()里数组名就是数组的大小    16
          a+0a为首元素的地址,加0还是首元素的地址,地址大小就是指针变量大小    4/8
          *aa为首元素的地址,解引用就是首元素,整型数组元素大小为4    4
          a+1a为首元素的地址,+1跳过一个整型,就是第二个元素的地址,地址大小还是指针变量大小    4/8
         a[1]就是第二个元素,元素大小为整型     4
          &a在&后a为整个数组的地址,地址大小还是指针变量的大小    4/8

         *&a

*和&先抵消,所以就是a,sizeof()里数组名就是数组的大小    16
         &a+1&a为数组的地址,+1跳过整个数组,但还是一个地址,大小就是指针变量大小    4/8
         &a[0]

a[0]为数组第一个元素,取地址,就是数组第一个元素的地址。

    4/8
         &a[0]+1&a[0]为首元素的地址,+1就是第二个元素的地址。     4/8

2.2 字符数组

代码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));
sizeof( )                                解析      结果
            arr数组名就是数组的大小,char类型为1个字节        6
            arr+0arr为首元素的地址,+0还是首元素的地址      4/8
            *arrarr为首元素的地址,*解引用就是首元素,元素大小为char        1
            arr[1]arr[1]为第二个元素的大小        1
            &arr&后arr为整个数组的地址,地址大小      4/8
            &arr+1&后arr为整个数组的地址,+1跳过整个数组,还是一个地址大小      4/8
            &arr[0]+1&arr[0]为首元素的地址,+1跳过一个char类型,就是第二个元素地址大小      4/8

代码2:

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( )                            解析     结果
            arrarr为首元素的地址,strlen计算数组的长度,但是数组中没有\0,,就会导致越界访问,结果就是随机的     随机值
            arr+0arr+0还是首元素的地址,但是和第一个arr一样,结果为随机值     随机值
            *arr

*解引用arr为首元素a,但是strlen需要一个指针,a的ASCII码值给strlen,strlen得到的就是野指针。

      error
            arr[1]

arr[1]就是数组元素b,与上面*arr一样,

strlen得到的就是野指针。

      error
             &arr&arr为数组的地址,但是数组没有\0,所以结果还是随机值      随机值
            &arr+1&arr为数组的地址,+1跳过整个数组,然后strlen还向后访问,遇到\0停止,结果还是随机值      随机值
           &arr[0]+1 &arr[0]+1为第二个元素的地址,strlen向后遇到\0停止,结果是随机值      随机值

代码3:

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));
         sizeof()                               解析          结果
             arrsizeof中数组名为整个数组的大小,计算数组的大小为7           7
             arr+0arr为首元素的地址,+0还是首元素的地址,地址大小为指针变量大小          4/8
             *arr*arr为首元素,元素大小为char类型            1
             arr[1]arr[0]为数组首个元素,元素为char类型            1
             &arr&arr为整个数组的地址,地址大小          4/8
             &arr+1&arr为数组的地址,+1跳过整个数组,还是一个地址          4/8
            &arr[0]+1&arr[0]为首元素的地址,+1跳过一个char,就是第二个元素的地址          4/8

代码4:

char arr[] = "abcdef";
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( )                             解析      结果
             arrarr为数组首元素地址,向后遇到\0停,数组字符串有\0,\0之前有6个字符        6
             arr+0arr+0还是数组首元素的地址,结果与arr一样        6
             *arr*arr为数组首元素a,a的ASCII值给strlen得到的就是野指针。      error
             arr[1]arr[1]为数组第二个元素b,strlen得到的也是个野指针。      error
             &arr&arr为数组的地址,从第一个元素往后遇到\0停止,有6个字符        6
             &arr+1&arr为数组的地址,+1跳过整个数组,然后strlen后遇到\0停止,所以结果为随机值      随机值
            &arr[0]+1&arr[0]为第一个元素的地址,+1跳过一个元素,strlen第二个元素往后,有5个字符         5

代码5:

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));
          sizeof( )                             解析      结果
             pp为字符串的地址,就是地址的大小       4/8
             p+1p为字符串的地址,+1就是b的地址,就是地址大小       4/8
             *p*p的类型为char *,解引用就是char         1
             p[0]

1.p[0]--*(p+0)--*p,与上面*p一样,大小为char 

2.字符串可以看成一个数组,p就是数组名,p[0]就是第一个元素,大小为char

         1
             &p&p为指针变量p的地址,也是一个指针变量        4/8
             &p+1&p为指针变量p的地址,+1为跳过指针变量地址,还是一个地址        4/8
            &p[0]+1&p[0]可以看成字符串数组第零个元素的地址,+1为第一个元素的地址。        4/8

代码6:

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

2.3 二维数组

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]));
          sizeof( )                             解析      结果
              aa在sizeof里表示,整个数组的大小,数组有12个整型元素       48
              a[0][0]a[0][0]为数组的第一个元素,元素为整型        4
              a[0]a[0]为首行元素,相当于a[0]为数组名,sizeof计算的数组大小        16
              a[0]+1a[0]没有放在sizeof里,a[0]为数组首元素a[0][0]的地址,+1跳过一个char类型,为a[0][1]的地址,地址大小。        4/8
              *(a[0]+1)a[0]+1为a[0][1]的地址,解引用为元素的大小,为char类型        1
               a+1a为二维数组首地址就是首行地址,第一行的地址,a+1,跳过一行,指向了第二行,a+1是第二行的地址,a+1是数组指针,地址大小        4/8
             *( a+1)*(a+1)--a[1],在sizeof内a[1]相当于第二行数组的首元素的地址,就是第二行数组大小       16
             &a[0]+1a[0]为首行元素,&a[0]就是取出第一行数组的地址,+1就是跳过一个数组,就是第二行数组的地址       4/8
            *(&a[0]+1)&a[0]+1为第二行数组的地址,解引用就是第二行的大小       16
                *aa作为数组名并没有单独放在sizeof内部,a表示数组首元素的地址,是二维数组首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小        16
               a[3]a[3]无需真实存在,仅仅通过类型的推断就能算出长度        16

三、指针运算笔试题解析

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

&a+1为数组跳过整个数组留下了的地址

所以*(a+1)为a[1]为数组第二个元素2,*(ptr-1)其中ptr-1为ptr为整型指针,-1就往前跳一个整型的地址,解引用就是5。

题目2:
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
struct Test
{int Num;char *pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}

其中0x1为16进制的1,p为结构体指针0x100000

p+1,p跳过一个结构体指针类型大小为20,为0x100014。

把p强制类型转换为unsigned long整型,整型直接加1为0x100001

把p强制类型转换为unsigned int*,大小为4个字节,加1为0x100004。

 

题目3:

#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,3,5

p为首行元素的地址,p[0]--*(p+0)就是第一个元素。

题目4

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

其中p的类型为int * [4],而a的类型为int *[5],所以p[4][2]就是p在a数组里面以四个整型为一组,到第四个数组中的第二个元素,相当于a中的a[3][2],取出地址相减就是元素个数,但是a[4][2]的地址更大结果就是-4

但是通过%p打印,我们先求得-4的补码为11111111111111111111111111111100,地址是十六进制,转换为就是FFFFFFFC

题目5:

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

&aa为数组的地址,+1跳过整个数组到最后,ptr-1就表示先前跳一个地址再解引用就是10

aa为数组首元素的地址,为二维数组第一行的地址,+1跳过第一行数组,再接引用就是第二行首元素的地址,也可以换成aa[1]就是第二行首元素的地址,ptr2-1就是第一行最后一个元素地址,再接引用就是5

题目6:

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

a为指针数组,存放的是三个字符串的地址,pa为二级指针存放的是数组首元素的地址,+1跳过一个char *类型就是存放数组第二个字符串的地址,*pa就是第二个元素的地址

题目7:

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

下面就是在内存中的大小:

**++cpp:cpp原先指向的是cp首元素的地址,++后变为cp+1的地址,*cpp就是cp中c+2的内容,再解引用*(c+2)就是POINT字符串的地址,最后打印的就是POINT。

*--*++cpp+3:cpp现在指向的是cp+1的地址,++cpp就是cp+2的地址,*cpp就是c+1,--(c+1)就变为c,然后就变为*c就是ENTER字符串的地址,再加3就变为E的地址,最后打印ER。

*cpp[-2]+3:cpp[-2]--*(cpp-2)现在cpp指向的是cp+2的地址,减2就指向cp的地址,*cp就是c+3,*(c+3)就是FIRST的地址,最后加三就是S的地址,最后打印ST。

cpp[-1][-1]+1:cpp[-1][-1]--*(*(cpp-1)-1),由于cpp指向的是cp+2的地址,-1指向的是cp+1的地址,解引用后为c+2,在减1变为c+1,在解引用变为NEW字符串的地址,最后加1为E的地址,最后打印EW。


总结

今天我们讲了指针最后的内容,讲了sizeof和strlen的区别和用法,和指针运算笔试题。希望对你有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536092.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跨境电子商务支付与结算的支撑系统

​1、跨境电子商务支付与结算的核心系统。 核心系统是用户执行跨境电子商务支付的核心模块&#xff0c;包括以下具体流程。 ​ ​①用户从跨境电子商务支付应用启动跨境电子商务支付流程。 ②跨境电子商务支付应用根据应用和用户选择的支付工具&#xff0c;来调用对应的支付产…

与鲸同行,智领未来!和鲸科技高校市场渠道合作伙伴正式开启招募

AI 浪潮来袭&#xff0c;技术日新月异&#xff0c;校企合作已成为高校培养符合产业需求的应用型人才、加速科研创新与成果转化的关键途径。从单一应用到多元化布局&#xff0c;各企业更需要技术领先、战略协同的领域伙伴协力共进。 和鲸科技以“协同平台实践社区竞赛”三位一体…

【数据结构】Set的使用

文章目录 一、Set的使用1.Set的常用方法&#xff1a;1.boolean add(E e)2.void clear()3.boolean contains(Object o)4.boolean remove(Object o)5.int size()6.boolean isEmpty()7.Object[] toArray()8.boolean containsAll(Collection<?> c)9.boolean addAll(Collecti…

C语言函数—关于静态库

具体的函数声明和定义请参考上一篇文章 如果我们成为了库的开发者&#xff0c;要卖给别人C语言库&#xff0c;该怎么办呢&#xff1f; A不会写减法&#xff0c;想找你买一个函数 但是&#xff0c;他给的太少了&#xff0c;你不想把源码卖给他 那怎么办呢&#xff1f; 首先&…

Android SDK 开发 云托管

开发SDK,拒绝重复造轮子。 本文陈述两种SDK开发方式&#xff0c;第一种AAR方式&#xff1b;第二种远程依赖方式。 具体步骤分为&#xff1a; 一、如何开发SDK&#xff1f; 二、如何打包AAR&#xff1f; 三、如何打包AAR&#xff1f; 四、如何进行SDK远程托管&#xff1f; 五、如…

【JavaScript】数据类型转换 ① ( 隐式转换 和 显式转换 | 常用的 数据类型转换 | 转为 字符串类型 方法 )

文章目录 一、 JavaScript 数据类型转换1、数据类型转换2、隐式转换 和 显式转换3、常用的 数据类型转换4、转为 字符串类型 方法 一、 JavaScript 数据类型转换 1、数据类型转换 在 网页端 使用 HTML 表单 和 浏览器输入框 prompt 函数 , 接收的数据 是 字符串类型 变量 , 该…

java拷贝数组

package com.mohuanan.exercise;public class Exercise {public static void main(String[] args) {int[] arr {1, 2, 3, 4, 5, 6, 7, 8, 8}; //格式化快捷键 CTRL 加 Alt 加 L键// F1截图 F3贴图//调用 copyOfRangeint[] ints copyOfRange(arr, 3, 7);for (int i 0; i &l…

【C++】string类初步介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 为什么学习string类1.1 C语言中的字符串1.2 推荐学习网站 2. 标准库中的string类2.1 string类2.2 string类的常用接口说明2.2.1 constructor2.2.2 遍历string2.2.2.1 下标加[]遍历2.2.2.2 迭代器&#xff08;itera…

前端跨页面通信的几种方式---同源

参考链接 1、LocalStorage:当 LocalStorage 变化时&#xff0c;会触发storage事件。利用这个特性&#xff0c;我们可以在发送消息时&#xff0c;把消息写入到某个 LocalStorage 中&#xff1b;然后在各个页面内&#xff0c;通过监听storage事件即可收到通知。 2、BroadCast C…

快速排序 刷题笔记

思路 分治双指针 在每个区间选定一个基准目标 两个指针从数组的两边向中间推进 使用 while循环判断 do {i;}while(q[i]<x); do{j--;}while(q[j]>x); 每次这样做完就会找到q[i]>x,,,,q[j]小于x 此时我们交换 q[i] ,q[j]于是小于x的数分到了小于x的一侧 大…

Java中 final、finally、finalize 有什么区别?

1、典型回答 final、finally、finalize 是 Java 中三个不同的关键字&#xff0c;它们除了长得像之外&#xff0c;其他的&#xff08;作用和含义&#xff09;完全不同。 它们三个的区别就好像&#xff1a;雷、雷锋、雷峰塔之间的区别。&#xff08;是三个完全不同的东西&#…

python实现卡普均值最小回路算法

如果给定一个含有环的有向图,要在这个图中找出所有的环并计算这些环的路径长度,然后除以环的边数,所得到的结果也就是环的平均值,这里也就是如何计算这个环的最小均值问题。 首先可以确定的是,如果图中均值最小的环的值是0,那么图中就不包含负环,由于是负环,那么所有边…