概述

本文通过c++示例代码演示指针的加减法运算及对 “指针 = 地址 + 偏移量” 的理解。

研究示例

1. 首先来检查各种变量类型所占的内存大小

#include <iostream>
using namespace std; int main(){
cout << sizeof(char) << endl; // 1 Byte
cout << sizeof(short) << endl; // 2 Byte
cout << sizeof(int) << endl; // 4 Byte
cout << sizeof(long long) << endl; // 8 Byte
return 0;
}

2. 各类型的指针进行加减运算

各类型的指针进行加减运算是以元素为单位进行加减,而非地址的最小单位(1个Byte)。以下演示各类型指针指向的地址:

#include <iostream>
using namespace std; int main(){
char *p_char = new char('a');
cout << "*p_char = " << *p_char << endl;
cout << "p_char = " << (void*)p_char << endl; // char指针在使用cout输出会直接打印变量而非地址,要加(void*)
cout << "p_char + 1 = " << (void*)(p_char + 1) << endl;
cout << "p_char + 2 = " << (void*)(p_char + 2) << endl;
cout << "p_char - 1 = " << (void*)(p_char - 1) << endl; short *p_short = new short(456);
cout << "*p_short = " << *p_short << endl;
cout << "p_short = " << p_short << endl;
cout << "p_short + 1 = " << p_short + 1 << endl;
cout << "p_short + 2 = " << p_short + 2 << endl;
cout << "p_short - 1 = " << p_short - 1 << endl; int *p_int = new int(123);
cout << "*p_int = " << *p_int << endl;
cout << "p_int = " << p_int << endl;
cout << "p_int + 1 = " << p_int + 1 << endl;
cout << "p_int + 2 = " << p_int + 2 << endl;
cout << "p_int - 1 = " << p_int - 1 << endl; long long *p_long_long = new long long(456789);
cout << "*p_long_long = " << *p_long_long << endl;
cout << "p_long_long = " << p_long_long << endl;
cout << "p_long_long + 1 = " << p_long_long + 1 << endl;
cout << "p_long_long + 2 = " << p_long_long + 2 << endl;
cout << "p_long_long - 1 = " << p_long_long - 1 << endl; return 0;
}

输出:(char, short, int, long long的指针的最小移动单位分别是1, 2, 4, 8 Byte,刚好是对应的单个元素的类型所占内存大小)

*p_char = a
p_char = 0xe41600
p_char + 1 = 0xe41601
p_char + 2 = 0xe41602
p_char - 1 = 0xe415ff
*p_short = 456
p_short = 0xe41620
p_short + 1 = 0xe41622
p_short + 2 = 0xe41624
p_short - 1 = 0xe4161e
*p_int = 123
p_int = 0xe41640
p_int + 1 = 0xe41644
p_int + 2 = 0xe41648
p_int - 1 = 0xe4163c
*p_long_long = 456789
p_long_long = 0xe41660
p_long_long + 1 = 0xe41668
p_long_long + 2 = 0xe41670
p_long_long - 1 = 0xe41658

说明:指针 = 地址 + 偏移量。即指针除了包含地址信息之外,还包含解析这个地址的方式(从该地址开始向后读取多少个Byte),因此“指针就是地址”的说法是不准确的,一个int* p1和char* p2 指向的地址可能是相同的,但是解析这个地址的方式是不同的。

3. 强制转换指针类型对地址进行解析

可以强行指定指针的类型对同一块内存的数据进行不同方式的解析。例如:

#include <iostream>
using namespace std; int main(){
char arr[10] = {0,1,2,3,4,5,6,7,8,9};
unsigned int l = sizeof(arr)/sizeof(arr[0]); char* p_char = arr;
cout << "*p_char = " << (int)*p_char << endl;
cout << "*(p_char + 1) = " << (int) *(p_char + 1) << endl;
cout << "*(p_char + 2) = " << (int)*(p_char + 2) << endl;
cout << "*(p_char + 3) = " << (int)*(p_char + 3) << endl;
cout << "*(p_char + 9) = " << (int)*(p_char + 9) << endl; short* p_short = (short*)arr;
cout << hex << "*p_short = " << *p_short << endl;
cout << hex << "*(p_short + 1) = " << *(p_short + 1) << endl;
cout << hex << "*(p_short + 2) = " << *(p_short + 2) << endl; int *p_int = (int*)arr;
cout << hex << "*p_int = " << *p_int << endl;
cout << hex << "*(p_int + 1) = " << *(p_int + 1) << endl;
cout << hex << "*(p_int + 2) = " << *(p_int + 2) << endl; return 0;
}

输出:

*p_char = 0
*(p_char + 1) = 1
*(p_char + 2) = 2
*(p_char + 3) = 3
*(p_char + 9) = 9
*p_short = 100
*(p_short + 1) = 302
*(p_short + 2) = 504
*p_int = 3020100
*(p_int + 1) = 7060504
*(p_int + 2) = fdf60908

解释如下图:arr数组在内存中占10个Byte,分别定义了三种类型的指针char*, short*, int*并且都指向首地址arr[0],则三种指针的差异体现在两个方面:1、解引用(解析地址)时的偏移量分别为1,2,4个Byte;2、加减时分别以1,2,4个Byte为单位移动地址。

注意:

1、本编译器采用小端模式,因此*p_short = 0x0100 而非 0x0001。

2、*(p_int + 2)访问到了未知的内存,因此结果中打印出来了不受控制的数据"0xfdf60908",警示了我们在使用指针时,尤其是涉及到类型转换、加减运算、赋值等操作时,一定要避免指针越界,否则将会产生不可预知的危险后果。

总结

本文通过几个简单的c++程序验证了 “指针 = 地址 + 偏移量” 这一结论,希望能对指针如何操作内存有更深入的理解。

另外,本文所讨论的指针均为变量指针,而函数指针不能进行加减运算(会报warning: pointer to a function used in arithmetic),我会另写一篇文章讨论函数指针。

C++指针等于地址加偏移量的更多相关文章

  1. 承诺c指针 (1)指针是地址

    (1)是地址 首先明白一个观点:指针就是地址.这是理解指针的起始一步. 直观感受下.变量的地址 int main() { int foo; int *foo_p; foo = 5; foo_p = & ...

  2. C语言指针、地址、赋值三者含义

    先来一个观点.大家先看看对不对 按:在CSDN论坛上,有位坛友提到这个问题: ==================================== 先看一段代码: #include<stdi ...

  3. C语言中指针变量的加减运算

    1.指针变量中存放的是地址值,也就是一个数字地址,例如某指针变量中的值是0x20000000,表示表示此指针变量存放的是内存中位于0x20000000地方的内存地址.指针变量可以加减,但是只能与整型数 ...

  4. 驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址

    驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址 最近重新看了乾龙_Heron的<ARM 上电启动及 Uboot 代码分析>(下简称<代码分析>) ...

  5. c语言 指针与地址的区别

    指针由两部分组成,指针的类型和指针的值(也就是变量的地址). 指针和地址的区别: 地址只是一堆十六进制的字符,对应着内存条的某段内存, 而指针本身有地址,指针的值也是一个地址,指针本身还有类型,这与单 ...

  6. JavaScript中指针和地址理解

    个人理解:指针只是指向内存的一个索引:而地址则是内存中确切的位置. 下面是函数中关于指针和地址一个小例子: function sum(num1,num2){ return num1+num2; } a ...

  7. c++入门之出话指针和地址。

    指针和地址是c和c++中重要的概念,在此,对指针做以下几方面的总结: new和delete: ]; point[] = ; point[] = ; point[] = ; cout << ...

  8. iOS - WKWebView加载不受信任的https (因用到IP地址加端口号去请求数据)

    1.描述:因公司域名临时出现问题,所以项目中引用到了IP地址加端口号去请求数据,因而造成在wkwebView中某些网址打不开,查看错误是因为服务器证书无效,实际就是不受信任; 2.解决办法:在plis ...

  9. 搬运1:关于对C语言中数组名取地址加减等操作的一点探究

    对于数组名取地址强制转换的操作 偶然在晚上学了C语言指针后网页闲逛找题时,被一个数组名取地址搞糊涂了,在自己试验加探索后我稍微悟了一点东西. 代码如下: #include<stdio.h> ...

  10. new 等于 malloc加构造函数

    1.new 是c++中的操作符,malloc是c 中的一个函数 2.new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员 ...

随机推荐

  1. 初看vue3源码

    因为工作的原因又回到了vue的领域,需要加深对vue和vue生态的了解 也许平时比较多人手机看别人解析怎么看vue源码的,自己动手看vue源码的还是比较少,这次我想自己动手看看 首先 吧代码获取到本地 ...

  2. QT入门学习记录01

    目录 前言 一.Qt安装 二.创建一个Qt工程 三.基类的区别和常用函数 1.QWidget 1.1 设置窗口标题 1.2 设置窗口大小和显示位置 1.3 显示窗口 1.4 隐藏窗口 1.5 改变窗口 ...

  3. 【DataBase】MySQL根据父节点查询下面的所有子节点

    表结构如下: /* Navicat Premium Data Transfer Source Server : 主机 Source Server Type : MySQL Source Server ...

  4. NVIDA GPU-SXM和NVIDA GPU-PCIe 两种类型显卡到底哪个性能更高?

    相关: 大模型时代该用什么样的显卡 -- 实验室新进两块A800显卡 浅析:NVIDA GPU卡SXM和PCIe之间的差异性 原来SXM类型的显卡比PCIex类型显卡性能要高.PCIE版本是通用接口, ...

  5. MindSpore分布式并行训练 (GPU-Docker)mindspore—1.2.1—gpu—docker版本运行报错,Failed to init nccl communicator for group,init nccl communicator for group nccl_world_group

    如题目所述: 计算框架MindSpore分布式并行训练报错,具体版本:docker-gpu-1.2.1 运行环境: 硬件:Intel CPU, 4卡泰坦 软件:Ubuntu18.04宿主机,docke ...

  6. .NET电子邮件高效处理解决方案

    前言 在日常软件开发中,电子邮件处理是一个不可或缺的功能,无论是用户注册验证.通知推送还是日常的业务沟通,都离不开电子邮件的支持.今天大姚给大家分享2款.NET开源.高效.强大的.NET电子邮件处理库 ...

  7. condition字符串匹配问题

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. fs使用dialplan配置文件执行业务流程,condition条件变量的配置是必然会使用的,这里记录一次配置过程中的错误示范. 环 ...

  8. MFC制作带界面的DLL库

    ## MFC如何创建一个带界面的DLL(动态链接库) 1.创建项目 打开VS,文件->新建->项目: 点击确定之后弹出来的界面,点击下一步->选择"使用共享MFC DLL的 ...

  9. java判断文本文件编码格式

    上篇文章需要读取当前java或者配置文件的编码格式,这里主要支持UTF-8.GBK.UTF-16.Unicode等 /** * 判断文件的编码格式 * @param fileName :file * ...

  10. 手把手教你ubuntu下移植MJPG-streamer

    一.嵌入式视频图像开源库 在嵌入式系统中,常用的视频图像处理开源系统有:luvcview.cheese.motion.mjpg-streamer或者ffmpeg,其中: • luvcview: 基于V ...