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. 机器学习基石:08 Noise and Error

    噪声:误标.对同一数据点的标注不一致.数据点信息不准确...... 噪声是针对整个输入空间的. 存在噪声的情况下,VC bound依旧有用: 存在噪声,就是f------>p(y|x),f是p的 ...

  2. [HAOI 2006]旅行comf

    Description 题库链接 给你一个 \(n\) 个点, \(m\) 条边的无向图.并给出一个点对 \((s,t)\) ,求 \(s,t\) 间的一条路径,使得路径上最大边和最小边的比值最小. ...

  3. HDU 5909 Tree Cutting

    传送门 题意: 有一棵n个点的无根树,节点依次编号为1到n,其中节点i的权值为vi, 定义一棵树的价值为它所有点的权值的异或和. 现在对于每个[0,m)的整数k,请统计有多少T的非空连通子树的价值等于 ...

  4. PKUWC 2018 滚粗记

    day0 上午居然考了一场考试,大爆炸,攒了一波RP,下午也没有心思去落实题目,而是一心去搞颓废,到了晚上看时间还早,于是就看了一波上午考试的Solution,懵逼.jpg day1 上午考数学,前一 ...

  5. [BZOJ]4908: [BeiJing2017]开车

    Time Limit: 30 Sec  Memory Limit: 256 MB Description 你有n辆车,分别a1, a2, ..., an位置和n个加油站,分别在b1, b2, ... ...

  6. glusterfs 4.0.1 event模块 分析笔记1

    1. 前言 在C语言i中,存储变量的结构体加上一组函数指针,大概就可以算是一个对象模型了:如果将一组函数指针捆绑为结构体, 后期根据配置或者环境需要绑定到不同实现模块中的一组函数,可以认为是C语言面对 ...

  7. JS多个对象添加到一个对象中

    var obj1 = {"qq":10}; var obj2={"mm":2,"nn":3}; var obj3={"xx&quo ...

  8. AngularJs开发——指令与控制器间的通信

    (原文:http://www.html5jscss.com/controller-between-directive.html) 指令与控制器之间通信,跟控制器间的通信.指令间通信也类似,也是下几种方 ...

  9. SSH构造struts2项目

    第一在pom.xml导入相应的包 (网上有很多导入多个包的教程,我缩减到一个了) <project xmlns="http://maven.apache.org/POM/4.0.0&q ...

  10. URLconnection

    URLconnection 开发中最常用的两种网络请求: 1.标准Java接口(java.NET) --HttpURLConnection,可以实现简单的基于URL请求.响应功能:  2.Apache ...