【C/C++】深入理解指针(六)

文章目录

  • 深入理解指针(六)
    • 1.sizeof和strlen的对比
      • 1.1 sizeof
      • 1.2 strlen
      • 1.3 sizeof和strlen的对⽐
    • 2.数组和指针笔试题解析
      • 2.1 ⼀维数组
      • 2.2 字符数组
        • 代码1:
        • 代码2:
        • 代码3:
        • 代码4:
        • 代码5:
        • 代码6:
      • 2.3 ⼆维数组
    • 3.指针运算笔试题解析
        • 3.1 题⽬1
        • 3.2 题⽬2
        • 3.3 题⽬3
        • 3.4 题目4
        • 3.5 题目5
        • 3.6 题⽬6
        • 3.7 题⽬7

深入理解指针(六)

1.sizeof和strlen的对比

1.1 sizeof

在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存空间⼤⼩的,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。

sizeof是单目操作符,不是函数!

sizeof 关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。

比如:

#include <stdio.h>
int main()
{int a = 10;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof a);//两者输出结果一致,证明了sizeof不是函数,函数不能去掉()printf("%zd\n", sizeof(int));return 0;
}

1.2 strlen

strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:

size_t strlen ( const char * str );//返回%zd
char str[]="abcdef";
const char* str="abcdef";//两者都可以
printf("%zd\n",strlen(str));

统计的是从 strlen 函数的参数 str 中这个地址开始向后\0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。

size_t len=strlen("abc\0def");
//返回3
#include<string.h>
#include <stdio.h>
int main()
{char arr1[3] = {'a', 'b', 'c'};//不知道0在哪char arr2[] = "abc";//abc\0printf("%d\n", strlen(arr1));//返回随机值printf("%d\n", strlen(arr2));//返回3printf("%d\n", sizeof(arr1));//返回3printf("%d\n", sizeof(arr2));//返回4(a b c \0)共四个return 0;
}

1.3 sizeof和strlen的对⽐

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

对于sizeof

sizeof括号中有表达式时,()里是不计算的

例如:

int a=8;
short s=4;
printf("%d\n",sizeof(s=a+2));//2
printf("%d\n",s);//4

在这里插入图片描述

a跟2都是int类型,把他们赋给short类型,所以sizeof认为它是short类型,所以返回2,但表达式是不进行真实计算的.

原因如下:

c语言是编译型语言

需要经过编译链接生成可执行程序,sizeof在编译过程完成,s=a+2在运行过程完成,所以还未到运行过程,程序就已经结束,不会运行到s=a+2这一步.

在这里插入图片描述

2.数组和指针笔试题解析

数组名的意义:

1.sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。

2.&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。

3.除此之外所有的数组名都表⽰⾸元素的地址。

2.1 ⼀维数组

int a[] = {1,2,3,4};
printf("%zd\n",sizeof(a));//zizeof(数组名)传递的是整个数组,所以结果为16(4*4)--四个整型
printf("%zd\n",sizeof(a+0));//a是首元素地址,没有单独存放进sizeof,类型是int *,a+0还是首元素地址(不会真的计算),是地址就是4(32位)或者8(64位)个字节
printf("%zd\n",sizeof(*a));//a是首元素地址,解引用就是第一个元素,所以是4个字节
printf("%zd\n",sizeof(a+1));//a是首元素地址,类型是int*,+1跳过一个整型-->第二个元素地址,是地址就是4或8个字节
printf("%zd\n",sizeof(a[1]));//a[1]是第二个元素,为4字节
printf("%zd\n",sizeof(&a));//存放的是数组地址,还是4或8
printf("%zd\n",sizeof(*&a));//两种理解方式(结果为16)//1.&a是取出整个数组的地址,对数组指针解引用访问数组//2.*和&相互抵消 d等价于(sizeof)
printf("%zd\n",sizeof(&a+1));//跳过整个数组后的位置的地址  依然是地址 4或者8
printf("%zd\n",sizeof(&a[0]));//地址 4或8
printf("%zd\n",sizeof(&a[0]+1));//第二个元素的地址 4或8

对于数组a

a——数组名

a——首元素地址——int*

&a——数组的地址——int(*)[4]

2.2 字符数组

代码1:
#include <stdio.h>
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));//数组名单独放sizeof,计算的是数组大小 6字节(一个char类型一个字节)printf("%d\n", sizeof(arr+0));//地址 4或8printf("%d\n", sizeof(*arr));//arr是首元素地址,*arr为第一个元素,1个字节printf("%d\n", sizeof(arr[1]));//第二个元素 1字节printf("%d\n", sizeof(&arr));//&arr是数组地址,4或8字节  arr——char(*)[6]printf("%d\n", sizeof(&arr+1));//跳过整个数组,指向数组后边的空间  4或者8printf("%d\n", sizeof(&arr[0]+1));//第二个元素地址  4或8return 0;
}

在这里插入图片描述

&arr+1为野指针,但是不使用

代码2:
#include <stdio.h>
#include <string.h>
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));//arr是首元素地址,数组中没有\0,会导致越界访问,所以结果是随机值printf("%d\n", strlen(arr+0));//同上printf("%d\n", strlen(*arr));//arr是首元素地址,*arr是首元素,就是'a',对应的ASII码值为97,就相当于把97作为地址传递给了strlen,strlen得到的就是野指针,所以该程序错误printf("%d\n", strlen(arr[1]));//arr[1]--'b'--98,同上错误printf("%d\n", strlen(&arr));//&arr是数组地址,起始位置是第一个元素位置-------随机值printf("%d\n", strlen(&arr+1));//随机值printf("%d\n", strlen(&arr[0]+1));//从第二个元素开始向后统计,也是随机值return 0;//假设第10行随机值为x,则第11行为x+6,第12行为x-1
}
代码3:
#include <stdio.h>
int main()
{char arr[] = "abcdef";//a b c d e f \0printf("%d\n", sizeof(arr));//数组总大小7printf("%d\n", sizeof(arr+0));//首元素地址 4或8printf("%d\n", sizeof(*arr));//首元素 1字节printf("%d\n", sizeof(arr[1]));//第二个元素 1字节printf("%d\n", sizeof(&arr));//数组地址 4或8printf("%d\n", sizeof(&arr+1));//4或8printf("%d\n", sizeof(&arr[0]+1));//4或8return 0;
}
代码4:
#include <stdio.h>
#include <string.h>
int main()
{char arr[] = "abcdef";//a b c d e f \0printf("%d\n", strlen(arr));//6printf("%d\n", strlen(arr+0));//首地址开始向后 \0之前  6printf("%d\n", strlen(*arr));//'a'--97出错printf("%d\n", strlen(arr[1]));'b'--98出错printf("%d\n", strlen(&arr));//整个数组地址,也是从第一个元素开始   6printf("%d\n", strlen(&arr+1));//随机值printf("%d\n", strlen(&arr[0]+1));//第二个元素开始 5return 0;
}
代码5:
#include <stdio.h>
int main()
{const char *p = "abcdef";//字符常量不能修改printf("%d\n", sizeof(p));//p是指针变量 4或8printf("%d\n", sizeof(p+1));//p是char*类型 +1跳一个字节 是b的地址 4或8printf("%d\n", sizeof(*p));//p的类型是char*  *p的类型就是char 1字节printf("%d\n", sizeof(p[0]));//p[0]-->*(p+0)-->*p-->'a'  1字节 把常量字符串想象成数组,p可以理解为数组名,p[0]就是首元素printf("%d\n", sizeof(&p));//4或8 取出的是p的地址printf("%d\n", sizeof(&p+1));//跳过p后的地址 4或8printf("%d\n", sizeof(&p[0]+1));//地址 4或8return 0;
}
代码6:
#include <stdio.h>
#include <string.h>
int main()
{char *p = "abcdef";// a b c d e f \0printf("%d\n", strlen(p));//6 往后数printf("%d\n", strlen(p+1));//5printf("%d\n", strlen(*p));//*p就是'a'--97  错误printf("%d\n", strlen(p[0]));//p[0]---*(p+0)---*p  错误printf("%d\n", strlen(&p));//&p取出的是指针变量p的地址,和字符串"abcdef"关系不大,从p这个指针变量起始位置开始向后数------随机值printf("%d\n", strlen(&p+1));//随机值 但不一定为上一行的随机值+1 因为&p和&p+1之间可能有\0printf("%d\n", strlen(&p[0]+1));//5return 0;
}

2.3 ⼆维数组

include <stdio.h>
int main()
{int a[3][4] = {0};printf("%d\n",sizeof(a));//a是数组名,单独放在sizeof内部,计算的是数组大小 3*4*sizeof(int)=48printf("%d\n",sizeof(a[0][0]));//第一个元素 4字节printf("%d\n",sizeof(a[0]));//是第一行数组,数组名单独放在sizeof中,是第一行数组总大小16printf("%d\n",sizeof(a[0]+1));//arr[0]是第一行数组,但没单独放在sizeof,所以是arr[0][0]的地址,+1后是arr[0][1]的地址  是地址就为4或8个字节printf("%d\n",sizeof(*(a[0]+1)));//a[0][1]元素 4个字节printf("%d\n",sizeof(a+1));//a表示首元素地址,a为二维数组,它的首元素为第一行的的地址,a+1则为第二行地址,是地址就是4或者8printf("%d\n",sizeof(*(a+1)));//第二行元素 16printf("%d\n",sizeof(&a[0]+1));//第二行地址 4或8printf("%d\n",sizeof(*(&a[0]+1)));//第二行所有元素 16printf("%d\n",sizeof(*a));//*a为第一行元素 16printf("%d\n",sizeof(a[3]));//无需真实存在,仅通过类型推断就能算出长度,是第四行元素 16return 0;
}

3.指针运算笔试题解析

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

在这里插入图片描述

输出结果为2,5

3.2 题⽬2
//在X86环境下 //假设结构体的⼤⼩是20个字节 
//程序输出的结果是啥? 
struct Test
{int Num;char *pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
//指针+-整数
int main()
{                          //0x1就是1  0x1是十六进制printf("%p\n", p + 0x1);//结构体指针+1,跳过一个结构体 0x100000+20-->0x100014-->00100014  32位补0printf("%p\n", (unsigned long)p + 0x1);//转换成整型 整型+1就是+1 0x100000+1-->0x100001printf("%p\n", (unsigned int*)p + 0x1);//0x100000+4-->0x100004-->00100004return 0;
}
3.3 题⽬3
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };//注意题目中()不是{}int *p;p = a[0];// 首元素地址,即第一行地址 其实就是a[0][0]的地址printf( "%d", p[0]);//p[0]-->*(p+0)-->*p 所以答案为1return 0;
}

在这里插入图片描述

3.4 题目4
//假设环境是x86环境,程序输出的结果是啥? 
#include <stdio.h>
int main()
{int a[5][5];int(*p)[4];//p是一个数组指针,p指向的是4个整型元素p = a;printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//指针-指针的绝对值是指针之间的元素个数return 0;//p[4][2]等价于*(*(p+4)+2)
}

在这里插入图片描述

a的类型是int * [5],p的类型是int * [4]

在这里插入图片描述

所以结果为%d打印的值为**-4**,%p打印的值为(补码)十六进制 把-4转换成十六进制的补码 结果为FFFFFFFC

3.5 题目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);//&aa是整个数组的地址 +1跳过整个数组 然后强制类型转换为int*int *ptr2 = (int *)(*(aa + 1));//得到一维数组名aa[1]=&aa[1][0] 第二行数组名表示第二行首元素地址printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;
}

在这里插入图片描述

所以答案为10 5

3.6 题⽬6
#include <stdio.h>
int main()
{char *a[] = {"work","at","alibaba"};//a是指针数组char**pa = a;//pa是二级指针,指向的是a的首元素地址pa++;printf("%s\n", *pa);//%s打印的是字符串,给一个地址,从这个地址向后打印字符串,直到遇到\0return 0;
}

在这里插入图片描述

所以本题答案为at

3.7 题⽬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;
}

根据题图可画出下图

在这里插入图片描述

对于 printf("%s\n", **++cpp);来说,++cpp拿到c+2的值----也就是c+2的地址 再解引用——————PONIT

在这里插入图片描述

然后进行printf("%s\n", *--*++cpp+3);//再次++cpp指向c+1 然后解引用得到c+1 然后进行--操作变为c 再解引用 再加3 指向E 打印ER

在这里插入图片描述

  然后运行这一行 printf("%s\n", *cpp[-2]+3);//*cpp[-2]等价于**(cpp-2)  先进行cpp-2 重新指向c+3 解引用得到c+3 再解引用 再+3 指向S 最终输出ST

在这里插入图片描述

最后 printf("%s\n", cpp[-1][-1]+1);//由于上一行代码没有改变cpp的值 所有cpp初始还是指向c+1 cpp[-1][-1]等价于*(*(cpp-1)-1)    cpp-1指向c+2 解引用得到c+2 再—1 得到c+1 再解引用 再+1指向E 最终输出EW

在这里插入图片描述


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

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

相关文章

探索大语言模型(LLM):语言模型从海量文本中无师自通

文章目录 引言&#xff1a;当语言模型学会“自己教自己”一、自监督学习&#xff1a;从“无标签”中挖掘“有监督”信号二、语言模型的自监督训练范式&#xff1a;两大经典路径1. 掩码语言模型&#xff08;Masked Language Modeling, MLM&#xff09;——以BERT为例2. 自回归语…

2025.5.4机器学习笔记:PINN文献阅读

2025.5.4周报 文献阅读题目信息摘要创新点网络架构实验结论不足以及展望 文献阅读 题目信息 题目&#xff1a; Physics-Informed Neural Network Approach for Solving the One-Dimensional Unsteady Shallow-Water Equations in Riverine Systems期刊&#xff1a; Journal o…

Unity Post Processing 小记 【使用泛光实现灯光亮度效果】

一、前言 本篇适用于Unity 2018 - 2019及以上版本&#xff0c;以默认渲染管线为例。文章内容源于个人研究尝试与网络资料收集&#xff0c;可能存在不准确之处。初衷是因新版本制作时老的Bloom插件失效&#xff0c;经研究后分享开启Bloom效果的方法。若在项目中使用Post Proces…

牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记3-地图基本操作与实战案例

目录 一、开发环境与框架搭建 二、地图数据加载与文档管理 1. 加载地图文档&#xff08;MXD&#xff09; 2. 动态添加数据源 三、地图浏览与交互操作 1. 基础导航功能 2. 书签管理 3. 量测功能 四、要素选择与属性查询 1. 属性查询 2. 空间查询 五、视图同步与鹰眼…

Qt指ModbusTcp协议的使用

Modbus 是一套通信“语言”&#xff08;协议&#xff09;&#xff0c;而 RS485 / RS232 / TCP 是通信“管道”&#xff08;物理接口&#xff09;。 编写modubusTcp程序&#xff0c;避免不了调试&#xff0c;首先用到的两个工具助手 poll是主机,slave是从机。主机也就是发送数据…

探索大语言模型(LLM):自监督学习——从数据内在规律中解锁AI的“自学”密码

文章目录 自监督学习&#xff1a;从数据内在规律中解锁AI的“自学”密码一、自监督学习的技术内核&#xff1a;用数据“自问自答”1. 语言建模&#xff1a;预测下一个单词2. 掩码语言模型&#xff08;MLM&#xff09;&#xff1a;填补文本空缺3. 句子顺序预测&#xff08;SOP&a…

CentOS7.9安装Python 3.10.11并包含OpenSSL1.1.1t

1. 安装编译 Python 所需的依赖包 yum -y install gcc make zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel wget2. 安装 OpenSSL 1.1.1 或更新版本 &#xff08;自定义路径安装的 OpenSSL 1.1.1 不会影响系统原有的…

qt事件过滤与传递机制

当点击 QLabel 时&#xff0c;正常情况下并不会直接触发 MyWidget 的 mousePressEvent 函数&#xff0c;原因在于事件的传递机制和事件过滤器的存在。下面详细分析这个过程&#xff1a; 事件传递机制 在 Qt 里&#xff0c;事件的传递是从子控件往父控件冒泡的。不过&#xff…

ubuntu 安装ollama后,如何让外网访问?

官网下载linux版本&#xff1a;https://ollama.com/download/linux 1、一键安装和运行 curl -fsSL https://ollama.com/install.sh | sh 2、下载和启动deepseek-r1大模型 ollama run deepseek-r1 这种方式的ollama是systemd形式的服务&#xff0c;会随即启动。默认开启了 …

kotlin与MVVM结合使用总结(三)

1. MVVM 架构详细介绍及源码层面理解 整体架构 MVVM&#xff08;Model - View - ViewModel&#xff09;架构是为了解决视图和数据模型之间的耦合问题而设计的。它通过引入 ViewModel 作为中间层&#xff0c;实现了视图和数据的分离&#xff0c;提高了代码的可维护性和可测试性…

A系统使用iframe嵌套B系统时登录跨域问题!

我这边两个项目都是独立的&#xff0c;问题是做了跨域配置之后点击登录接口调用成功但是页面没有跳转进去 显示以下报错 这个错误明确指出了问题的核心原因&#xff1a;由于跨站点Cookie设置未正确声明SameSiteNone&#xff0c;导致浏览器拦截了Cookie。这是现代浏览器&#x…

消息唯一ID算法参考

VUE // src/utils/idGenerator.js/*** 雪花算法风格的 ID 生成器**//*** 前缀 w代表web端,m代表手机端**/ const DEFAULT_PREFIX = w; const DEFAULT_TOTAL_LENGTH = 16; const CHARS

《WebGIS之Vue零基础教程》(5)计算属性与侦听器

1 计算属性 1) 什么是计算属性 :::info 计算属性就是基于现有属性计算后的属性 ::: 2) 计算属性的作用 计算属性用于对原始数据的再次加工 3) 案例 :::warning **需求** 实现如下效果 ::: 使用表达式实现 html Document 请输入一个字符串: 反转后的字符串: {{msg.split(…

洞悉 NGINX ngx_http_access_module基于 IP 的访问控制实战指南

一、模块概述 ngx_http_access_module 是 NGINX 核心模块之一&#xff0c;用于基于客户端 IP 地址或 UNIX 域套接字限制访问。它通过简单的 allow/deny 规则&#xff0c;对请求进行最先匹配原则的过滤。与基于密码&#xff08;auth_basic&#xff09;、子请求&#xff08;auth…

数据中台-数据质量管理系统:从架构到实战

一、数据质量管理系统核心优势解析​ ​ (一)可视化驱动的敏捷数据治理​ 在数据治理的复杂流程中,Kettle 的 Spoon 图形化界面堪称一把利器,为数据工程师们带来了前所未有的便捷体验。想象一下,你不再需要花费大量时间和精力去编写冗长且复杂的 SQL 脚本,只需通过简单…

数据分析之 商品价格分层之添加价格带

在分析货品数据的时候&#xff0c;我们会对商品的价格进行分层汇总&#xff0c;也叫价格带&#xff0c;​​ 一、价格带的定义​​ ​​价格带&#xff08;Price Band&#xff09;​​&#xff1a;将商品按价格区间划分&#xff08;如0-50元、50-100元、100-200元等&#xff…

Maven 依赖范围(Scope)详解

Maven 依赖范围&#xff08;Scope&#xff09;详解 Maven 是一个强大的项目管理工具&#xff0c;广泛用于 Java 开发中构建、管理和部署应用程序。在使用 Maven 构建项目时&#xff0c;我们经常需要引入各种第三方库或框架作为项目的依赖项。通过在 pom.xml 文件中的 <depe…

vue3实现v-directive;vue3实现v-指令;v-directive不触发

文章目录 场景&#xff1a;问题&#xff1a;原因&#xff1a;‌ 场景&#xff1a; 列表的操作列有按钮&#xff0c;通过v-directive指令控制按钮显隐&#xff1b;首次触发了v-directive指令&#xff0c;控制按钮显隐正常&#xff1b;但是再次点击条件查询后&#xff0c;列表数…

数据结构【树和二叉树】

树和二叉树 前言1.树1.1树的概念和结构1.2树的相关术语1.3树的表示方法1.4 树形结构实际运用场景 2.二叉树2.1二叉树的概念和结构2.2二叉树具备以下特点&#xff1a;2.3二叉树分类 3.满二叉树4.完全二叉树5.二叉树性质6.附&#xff1a;树和二叉树图示 前言 欢迎莅临姜行运主页…

css面板视觉高度

css面板视觉高度 touch拖拽 在手机端有时候会存在实现touch上拉或者下拉的样式操作 此功能实现可以参考&#xff1a; https://blog.csdn.net/u012953777/article/details/147465162?spm1011.2415.3001.5331 面板视觉高度 前提需求&#xff1a; 1、展示端分为两部分&…