keil4编译c51显示超过限制 怎么突破20k限制

 这篇文章是由keil4编译c51显示超过限制 C51 嘚英文文档翻译过来的很多语句都是根据自己的理解翻译的,肯定还有许多地方需要推敲希望读者能吸取到有用的部分,不要被误解叻自己多理解。

函数指针是C语言中几个难点之一由于8051的C编译器的独特要求,函数指针和再入函数有更多的挑战需要克服主要由于函數变量的传递。

典型的(绝大部分8051芯片)函数变量通过堆栈的入栈和出栈命令来传递因为8051只有有限的堆栈空间(128字节或更少的64字节),函数变量必须通过不同的方式进行传递

8051的PL/M-51编译器,介绍在固定的存储空间存储变量的方式当使用连接器时,程序建立一个调用树计算出函数变量的互斥空间,然后覆盖它们这就是连接器的“OVERLAY”指令。

因为PL/M-51不支持函数指针所以不能实现间接函数调用。然而C语言中存在这样的问题。连接器知道哪块空间用于存储间接函数的变量怎样间接加入函数进入调用树?

本文解释在C51编程中怎样有效使用函数指针。特别地讨论如下几个话题:

分配常量地址给一个指针;

C51中函数指针问题;

使用OVERLAY指令确定调用树;

再入函数的指针; 

   你很容易的给函数指针分配一个数字地址。有许多原因需要这样做例如,你需要复位目标你可以设置函数指针为0000H去实现。

   你可以使用标准C语言的类型映射特点映射0X0000指针指向地址0的函数。例如当你编译如下C代码….

把一个数字常量映射成一个函数指针是一件很复杂的事情。下面关于仩面的函数调用的各部分的描述将帮助你怎样更好的使用它们。

  在上面的函数调用中(void ( *) (void))是数据类型:一个不带参数且返回void的函数指針。

  0x0000是一个映射地址经过类型映射,函数指针指向地址0x0000注意我们把一个圆括号放在数据类型和0x0000后面。如果我们仅仅想映射0x0000成为函数指針这是不必要的。然而因为我们将引用这个函数,这些圆括号是必要的

   映射一个数值常量成为指针和通过指针调用函数是不同的。為了实现这个我们必须指定一个变量表。这就是为什么在此行的后面有一个()

   注意上面表达式中的所有圆括号都是必须的。分组和優先级是很重要的

   上面不带参数的函数指针和带参数的函数指针的唯一不同是数据类型和变量列表。例如下面的函数调用…..

声明一个函数,地址在0x8000接收3个int型参数,返回long型结果

   指向函数的函数指针是可变的。函数的地址是一个可变的数值例如,下面的函数指针的声奣….

因为函数没有参数传送所以参数列表时空的。

   注意必须分配一个地址给函数指针。如果没有分配函数指针将有一个0值(如果你運气好),或者有一些你完全不知道的数值依赖于你的数据存储区的使用情况。当你间接的调用一个函数通过函数指针如果函数指针沒有初始化,你的程序将是混乱的

   为了声明一个带返回值的函数指针,在声明过程中你必须指定返回值的数据类型例如,下面的声明妀变了上面的函数指针的声明返回一个float 数据。

  带参数的函数指针与不带参数的函数指针是相似的例如:

注意,函数指针仅仅可以指向尛于等于3个参数的函数这是因为,间接调用函数时参数必须保存在寄存器中。关于超过3个参数的函数指针的信息在再入函数中介绍。

使用函数指针的附加说明

   如果你在C51中使用函数指针编程有几个附加的说明你必须注意。

   通过函数指针传递参数给函数必须把所有的参數存入寄存器在大部分情况下,3个参数能够自动通过寄存器传递在C51的用户手册中能找到传递参数进入寄存器的运算法则。但是并不保證任何的3个数据类型可以传递。

  因为C51在寄存器中传递3个参数用于传递参数的存储空间是不被分配的,除非函数指向一个要求更多参数嘚函数如果在那样的情况下,可以把参数混入一个结构体中然后通过一个结构体指针传递参数。如果这样不可接受你可以使用再入函数(看下面)。

   C51不把函数参数压栈(除非使用再入函数)函数参数和全局变量被存入寄存器或固定的存储空间。这样阻止函数的再入例如,一个函数调用它自己它将覆盖它自己的参数或存储空间。函数的再入问题通过关键字“reentrant”来解决函数指针的非再入函数的副莋用,在执行中出现问题

   为了保护尽量多的数据空间,连接器执行调用树的性能分析决定一些存储空间被安全的覆盖。例如如果你嘚应用中包含main 函数,函数a函数b,函数c并且main函数调用a,b,c,但是a,b,c之间没有互相调用在你应用中的调用树见出现如下:

这样A,B,C的存储空间可鉯被安全的覆盖

当调用树不能正确的建立,函数指针将带来问题因为连接器不能决定函数之间的引用。在这个问题上没有自动的解決方法。

下面两个源文件将解答这个问题使问题容易明白。第一个源文件FPCALLER.C包括一个函数,它通过一个函数指针(fptr)调用另一个函数

仩面的两个的源文件编译和链接都没有错误。通过连接器调用树的映射文件如下:

在这个简单的例子中,许多信息可以从调用树里挖掘絀来?C_C51STARTUP段调用main函数的?PR?MAIN?FPMAIN,段名各部分解析:PR是代码存储区MAIN是函数名,FPMAIN是定义函数所在的源文件名

  FUNC_CALLER函数使用的存储区从0008H开始,FUNC函数使用的存储区也是从0008H开始因为FUNC_CALLER函数调用FUNC函数,又因为两个函数使用相同的存储区这样就产生了问题。当FUNC函数被FUNC_CALLER函数调用时存储区将被FUNC_CALLER破坏。这个问题是怎样产生的是由keil4编译c51显示超过限制 51编译器产生还是由连接器产生?

  这个问题的原因是函数指针当你使用函数指针时,你將总是遇到这样的问题幸运的是,他们是容易被修改的“OVERLAY”指令让你指定在调用树中,函数与其他函数是怎样连接的

  为了修正上面顯示的调用树,FUNC函数必须从MAIN函数中删除同时FUNC函数必须插入到FUNC_CALLER函数中。下面用“OVERLAY”指令修改后如下:

为了删除或插入相关的进入调用树指定第一调用和第二调用。“~”符号用于删除相关的函数“!”用于插入一个外部函数。例如?PR?MAIN?FPMAIN ~ ?PR?_FUNC?FPMAIN意义是从MAIN函数中删除FUNC函数的调用。

  经过調整连接命令包括用“OVERLAY”指令修正调用树,调整后的映射文件如下:

修正后的调用树中FUNC_CALLER函数和FUNC函数使用独立存储空间。

  下面是一个典型的函数指针列表的定义:

如果你的MAIN函数中通过fp_tab调用歌函数连接映射文件出现如下:

三个函数通过列表被调用,FUNC1FUNC2 和FUNC3被C_INITSEG调用。但是这是錯误的C_INITSEG按照常规的方式在程序中初始化。这些函数被引入初始化代码中因为函数指针列表被初始化成这些函数的地址值。

   C51编译器和BL51连接器联合工作当使用函数指针列表时,使得函数变量空间覆盖很容易但是,你必须合理的声明指针列表如果你这样做了,就可以避免使用“OVERLAY”指令下面的函数指针列表的定义,C51和BL51可以自动处理:

注意唯一不同的是存储列表在CODE空间现在,连接映射文件如下:

  现在初始化代码中没有引入FUNC1,FUNC2 和FUNC3但是,MAIN函数中引入一个常数段FP_TAB这是一个函数指针列表。因为函数指针列表引入了FUNC1FUNC2 和FUNC3,所以调用树是正确嘚

  只要把函数指针列表放在一个独立的源文件中,在调用树中C51和BL51就能正确的连接。 

  有些函数指针的应用技巧

  把函数指针从一个普通嘚指针变成一个指定空间的指针。用一个字节保存指针因为函数属于CODE存储区(在8051里),一个字节可以用来保存声明的函数指针作为CODE指针例如:

如果你选择在你的函数指针声明中包含code关键字,就可以在任何地方使用它如果你声明一个函数,它接收一个3字节的普通指针通过指定空间传递,2字节函数指针坏事将要产生。

  keil4编译c51显示超过限制 C51 为函数的再入提供关键字“reentrant”再入函数的参数通过模拟栈来传递。模拟栈对于small存储模式位于IDATA对于compact存储模式位于PDATA,对于large存储模式位于XDATA如果你使用再入函数,在STARTUP.A51中你必须初始化再入栈的指针参考下面嘚启动代码:

  你必须设置你使用的存储模式的堆栈和设置栈顶。当有入栈时再入函数的栈指针减少(向下移动)。为了保护内部的数据區有一个技巧就是把所有的再入函数放在一个独立的存储模式,像large或compact

声明一个再入函数的函数指针,必须使用reentrant关键字

再入函数的函數指针和非再入函数的函数指针没有许多不同。当使用再入函数指针时会生成更多的代码,因为参数被压入模拟栈然而,没有特殊的連接要求和不需要打乱“OVERLAY”指令

  如果通过间接调用传递超过3个参数给函数,需要再入函数指针

  函数指针是非常有用的,并不是很困难嘚如果你注意连接调用树,保证用“OVERLAY”指令修正一些冲突

以下为原文,有问题可以参考原文

}

我要回帖

更多关于 keil4编译c51显示超过限制 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信