C语言诠释--为什么内存是线性分布的。
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语言诠释--为什么内存是线性分布的。的更多相关文章
- C语言中的内存分配与释放
C语言中的内存分配与释放 对C语言一直都是抱着学习的态度,很多都不懂,今天突然被问道C语言的内存分配问题,说了一些自己知道的,但感觉回答的并不完善,所以才有这篇笔记,总结一下C语言中内存分配的主要内容 ...
- C语言中的内存压缩技术
C语言中的内存压缩技术 前言 在整个研究生阶段我都在参与一个LTE协议栈实现的项目,在这个项目中,我们利用一个自己编写的有限状态机框架将协议栈中每一层实现为一个内核模块.我们知道,在编写内核代码时需要 ...
- C语言中的内存管理
开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容.不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰. 先说一下C语言中的内存管 ...
- C语言中的内存对齐
最近看了好多,也编了好多C语言的浩强哥书后的题,总觉的很不爽,真的真的好怀念linux驱动的代码,好怀念那下划线,那结构体,虽然自己还很菜. 同时看了一遍陈正冲老师的C语言深度剖析,收益很多,又把唐老 ...
- C语言数据在内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.程序结束时由编译器自动释放 ...
- C语言变量声明内存分配
转载: C语言变量声明内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结 ...
- 数据结构基础(1)--数组C语言实现--动态内存分配
数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...
- 2-Linux C语言指针与内存-学习笔记
Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...
- C语言程序的内存布局
C语言程序的内存布局 一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段( ...
随机推荐
- 第三届“百越杯”福建省高校网络空间安全大赛_Do you know upload?
题目在i春秋的ctf训练营 既然是上传,那就直接抓包 二话不说上来先给个00截断传个一句话助助兴 直接就成功了.... 赶紧操起菜刀去连接 进去之后发现ctf.sql是个空文件,那么flag应该在数据 ...
- 系统引导修复 ---- Windows 和 Ubuntu
Windows: 1.准备windows相应镜像盘,u盘启动该系统 (以下均为windows10安装界面) 2.进入安装界面<语言,时间,键盘格式>,点击"下一步" 3 ...
- [NOI 2014]动物园
Description 题库链接 \(t\) 组询问.每组询问给出一个字符串 \(S\) .要求求出一个 \(num\) 数组一一对于字符串 \(S\) 的前 \(i\) 个字符构成的子串,既是它的后 ...
- [SDOI 2015]约数个数和
Description 设d(x)为x的约数个数,给定N.M,求 $\sum^N_{i=1}\sum^M_{j=1}d(ij)$ Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试 ...
- UVA 5009 Error Curves
Problem Description Josephina is a clever girl and addicted to Machine Learning recently. She pays m ...
- 计蒜客NOIP模拟赛4 D2T2 跑步爱天天
YOUSIKI 在 noip2016 的一道<天天爱跑步>的题爆零后,潜心研究树上问题,成为了一代大师,于是皮皮妖为了测验他,出了一道题,名曰<跑步爱天天>. 有一个以 1 为 ...
- 2015 多校联赛 ——HDU5350(huffman)
Problem Description MZL is a mysterious mathematician, and he proposed a mysterious function at his ...
- [Noi2015]小园丁和老司机
来自FallDream的博客,未经允许,请勿转载,谢谢. 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有n棵许愿树,编号1,2,3,…,n,每棵树可以看作平面上的一个点,其中 ...
- [USACO08JAN]跑步Running
题目描述 The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ...
- 如何理解Spring IOC
Spring IOC 思维导图 要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Pr ...