Author:伟易达集团软件工程师 II 杨源鑫
Date :2016.11.11
Subject:内存为什么是线性分布的

今天有位小伙伴问了我一个问题,问题大概是这样描述的:
      师兄,我如何能够先访问一个函数,接着我访问另外一个函数,然后再访问原来的那个函数,但是不能调用原来那个函数,我该怎么实现呢?看完这问题,还真有点饶口啊,其实他想说明的关键就是:函数指针。函数指针实现是太方便了,定义一个指针指向函数,这个指针就可以获取那个函数的入口地址。
     鉴于这个概念,我写了一个例子,来验证内存为什么是线性分布的。

请看源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define abs(a,b) a < b ?(b-a):(a-b) //定义一个绝对值宏
int add(int a , int b)
{
	eturn a+=b;
}
int sub(int a , int b)
{
	return a-=b;
}
int time(int a , int b)
{
	return a*=b ;
}
int main(int argc, char *argv[])
{
	int a = 20 , b = 10 ;
	int (*addfun)(int , int) = add;
	int (*timefun)(int , int) = time ;
	int (*subfun)(int , int) = sub ;
	int offset = subfun-addfun ;
	int (*fpofset)(int , int) = subfun-offset;
	printf("%p--->%d\n",fpofset,fpofset(a,b));
	int offs = abs(addfun,timefun);
	int (*ftimefset)(int , int) = fpofset+offs;
	printf("%p---> %d\n",ftimefset,ftimefset(a,b));
	int *p = (int *)add ;
	int (*q)(int,int) = p ;
	printf("%d\n",q(a,b));
	return 0;
}

首先我们看到,我定义了三个函数 add 和 sub 和 time,功能我就不多说了,大家都懂。

int (*addfun)(int , int) = add;
int (*timefun)(int , int) = time ;
int (*subfun)(int , int) = sub ;

以上三句学过 C 语言的都知道,这叫函数指针。Add,time,sub 是函数名,函数名其实就是函数的入口地址,赋值给函数指针也就说明一个道理,addfun,timefun,subfun 可以像我们平常使用函数一样,往里传参数,然后通过 printf 输出。

今天我们的重点不在这里,大家请看,以下是我加的 printf 语句,在上面没有,主要是为了演示我的结论。

我们可以观察到 int offset = subfun-addfun;这句话的效果就是将这两个函数的入口地址相减获取到他们之间的偏移量。然后通过函数指针 int (*fpofset)(int,int) = subfun-offset;用subfun 地址减去 subfun 和 addfun 之间的偏移量可以得到 addfun 的地址。这时候,往fpofset(a,b)传参我们可以得到结果是 30,那就说明,fpofset(a,b)此时是通过偏移地址获取到addfun 的地址,然后再通过 addfun 这个函数指针间接的访问到 add 这个我们定义的函数的入口地址,从而调用了 add 这个函数实现两数相加。看到这里可能各位看官有点晕呐,没事,再看看下面的分析,这次我要实现的是,将通
过函数指针的偏移实现间接访问获取 time 函数实现相乘。
      那如果用 addfun-timefun 的偏移地址给 offs,这样可以不呢?我们把上面的代码改一下:

天啊,段错误了,程序异常退出! !为什么?想过原因吗?因为, addfun 的地址是 0x00401630,而 timefun 的地址是 0x0040164C,它们两的地址之差是 0xFFFFFFE4,这明显已经不是一个正常的地址了,是一个负数,我们都知道,负数的补码等于该数的绝对值的二进制形式按位取反再加一,所以后面将取得得这个偏移量赋值给了ftimeefset这个函数指针, 再用printf打印, 在这就出现错误了, 原来fpofset的地址是0x401630,你给这个地址加上一个 0Xffffffe4 能等于 timefun 的地址 0x0040164C 这个地址吗?这不扯蛋吗?所以,正确的做法应当是,给这相减的结果加一个绝对值,abs(a,b);先判断 a 和 b 这两个参数哪个比较大,然后再计算他们的偏移,这样就不会错误了。可以看看。

仅仅是加了一个绝对值,我们可以看到打印结果,我们得到的偏移地址是 0x0000001C,这就是 timefun 和 addfun 之间相减的偏移量。 通过地址偏移量取到了 timefun 这个函数指针的地址, 因为 timefun 这个函数指针指向了 time 函数, 进而就调用了这个函数实现两数相乘了。我们还可以用以下的内存分布图来解释为什么是这样的:

C语言诠释--为什么内存是线性分布的。的更多相关文章

  1. C语言中的内存分配与释放

    C语言中的内存分配与释放 对C语言一直都是抱着学习的态度,很多都不懂,今天突然被问道C语言的内存分配问题,说了一些自己知道的,但感觉回答的并不完善,所以才有这篇笔记,总结一下C语言中内存分配的主要内容 ...

  2. C语言中的内存压缩技术

    C语言中的内存压缩技术 前言 在整个研究生阶段我都在参与一个LTE协议栈实现的项目,在这个项目中,我们利用一个自己编写的有限状态机框架将协议栈中每一层实现为一个内核模块.我们知道,在编写内核代码时需要 ...

  3. C语言中的内存管理

    开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容.不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰. 先说一下C语言中的内存管 ...

  4. C语言中的内存对齐

    最近看了好多,也编了好多C语言的浩强哥书后的题,总觉的很不爽,真的真的好怀念linux驱动的代码,好怀念那下划线,那结构体,虽然自己还很菜. 同时看了一遍陈正冲老师的C语言深度剖析,收益很多,又把唐老 ...

  5. C语言数据在内存分配

    一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.程序结束时由编译器自动释放 ...

  6. C语言变量声明内存分配

    转载: C语言变量声明内存分配   一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结 ...

  7. 数据结构基础(1)--数组C语言实现--动态内存分配

    数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...

  8. 2-Linux C语言指针与内存-学习笔记

    Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...

  9. C语言程序的内存布局

    C语言程序的内存布局 一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段( ...

随机推荐

  1. PHPCMS v9.5.6 通杀getshell(前台)

    漏洞url:http://wooyun.jozxing.cc/static/bugs/wooyun-2014-062881.html 很好的fuzz思路. 文章提到:文件名前面的数字是被"干 ...

  2. Centos常用命令之:压缩与解压缩

    在Linux中,压缩文件的扩展名主要是:[*.tar,*.tar.gz,*.tgz,*.gz,*.Z,*.bz2],虽然,我们知道,在LInux中,文件的扩展名没有什么作用,但是由于在Linux中支持 ...

  3. NOIP 2011 观光公交

    题目描述 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第 0 分钟出现在 1号景点,随后依次前往 2 ...

  4. [HNOI2011]数学作业

    题目描述 小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题: 给定正整数 N 和 M,要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenat ...

  5. bzoj 3679: 数字之积

    Description 一个数x各个数位上的数之积记为\(f(x)\) 求[L,R)中满足\(0<f(x)<=n\)的数的个数 solution 最后\(f(x)\)可以拆分成2,3,5, ...

  6. bzoj3224Tyvj 1728 普通平衡树 treap

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 17706  Solved: 7764[Submit][St ...

  7. SqlServer 跨网段跨服务器复制

    注意:被同步的表必须有主键,否则无法同步.对数据库进行操作时需要登录服务器,在服务器本地进行操作,远程对数据库进行操作不能完成所有的步骤 准备工作: 1.将发布数据库完整备份到订阅服务器上,并在订阅服 ...

  8. 使用JAXB解析xml文件(二)

    前面一章简单演示了JAXB的用法,这个章节主要梳理一下JAXB常见的几个注解 1.@XmlRootElement 用于类级别的注解,对应xml的跟元素,常与 @XmlType 和 @XmlAccess ...

  9. TensorFlow 中文资源全集,官方网站,安装教程,入门教程,实战项目,学习路径。

    Awesome-TensorFlow-Chinese TensorFlow 中文资源全集,学习路径推荐: 官方网站,初步了解. 安装教程,安装之后跑起来. 入门教程,简单的模型学习和运行. 实战项目, ...

  10. 认识Json解析json生成json

    .markdown-body hr::after,.markdown-body::after { clear: both } .loopLine,.messageLine0 { } .markdown ...