搜索
bottom↓
回复: 25

哪位高手给解释一下这个原因?

[复制链接]

出0入0汤圆

发表于 2014-7-23 13:33:05 | 显示全部楼层 |阅读模式
本帖最后由 zhiguangqi 于 2014-7-23 16:32 编辑

有俩C 的程序,一个是我写的,一个是例程,运行结果类似,当然例程写的很好,片子是PIC16F877A ,软件是HITECH C 9.83,除了程序不同,其他的条件都一样,我把程序直接贴上方便大家看。我的问题就是,这俩程序很多语句是一样的,我写的语句少,编译结果却大,而那个例程语句多,可是编译结果却小,为什么呢?百思不得其解!(我只想知道产生这个结果的原因,而不是程序好不好)
:先把我的贴上
#include <pic.h>

__CONFIG(FOSC_HS & WDTE_OFF & LVP_OFF);


const unsigned char led[] =
{
        //定义表格一定要使用const,这样会做到程序存储区中
    0B00111111,            //"0"的字形表,0x3F
    0B00000110,            //"1"的字形表,0x06
    0B01011011,            //"2"的字形表,0x5B
    0B01001111,            //"3"的字形表,0x4F
    0B01100110,            //"4"的字形表,0x66
    0B01101101,            //"5"的字形表,0x6D
    0B01111101,            //"6"的字形表,0x7D
    0B00000111,            //"7"的字形表,0x07
    0B01111111,            //"8"的字形表,0x7F
    0B01101111,            //"9"的字形表,0x6F

};



void main (void)
{
       
        unsigned int i;
        unsigned char j;

       
       
       
        TRISD = 0X0;
        TRISB = 0X0;
        PORTB = 0X0;
        PORTD = 0X0;
       
        while(1)
        {       
               
               
                PORTD = 0;
                if (++i > 9999) i = 0;
               
               
               
                       
       
                PORTD = led[i / 1000];
                PORTB = 0X0;
               
               
                PORTD = led[(i % 1000) / 100];
                PORTB = 0X01;
               
       
                PORTD = led[(i % 100) / 10];
                PORTB = 0X02;
               
               
                PORTD = led[i %10];
                PORTB = 0X03;
               
                for (j = 0; j < 200; j++);
               
               
               
               
               
               
               
       
        }





}

我的程序的编译结果:

Memory Summary:
    Program space        used    F5h (   245) of  2000h words   (  3.0%)
    Data space           used    11h (    17) of   170h bytes   (  4.6%)
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
    Configuration bits   used     1h (     1) of     1h word    (100.0%)
    ID Location space    used     0h (     0) of     4h bytes   (  0.0%)




这个是例程:

#include <pic.h>           //调用PIC16F87XA单片机的头文件

//根据选项,配置字应该如下所示:
//__CONFIG(HS&WRTEN&WDTDIS&BOREN&PWRTDIS&UNPROTECT&DUNPROT&DEBUGDIS&LVPDIS);

//实际使用中,这样太麻烦,对于默认选项,我们将其忽略掉:
__CONFIG(FOSC_HS & WDTE_OFF & LVP_OFF);

//---------------------------------------
//数码管字形表,供显示时查询
const unsigned char LED[10]=
{                          //定义表格一定要使用const,这样会做到程序存储区中
    0B00111111,            //"0"的字形表,0x3F
    0B00000110,            //"1"的字形表,0x06
    0B01011011,            //"2"的字形表,0x5B
    0B01001111,            //"3"的字形表,0x4F
    0B01100110,            //"4"的字形表,0x66
    0B01101101,            //"5"的字形表,0x6D
    0B01111101,            //"6"的字形表,0x7D
    0B00000111,            //"7"的字形表,0x07
    0B01111111,            //"8"的字形表,0x7F
    0B01101111,            //"9"的字形表,0x6F
};

//---------------------------------------
//4位数码管相关I/O设置
#define U5A  RB0           //4位数码管单元的U5(74HC138)的A脚接在RB0口上
#define U5B  RB1           //4位数码管单元的U5(74HC138)的B脚接在RB1口上
#define U5C  RB2           //4位数码管单元的U5(74HC138)的C脚接在RB2口上
//---------------------------------------

//---------------------------------------
//名称: 主函数
//公司:宁波芯动电子有限公司
//网址:www.MovingChip.com
//日期:20121009
//---------------------------------------
void main(void)            //主函数,单片机开机后就是从这个函数开始运行
{

    unsigned char c=0;     //定义一个char型变量,做延时用
    unsigned char d=0;     //定义一个char型变量,控制显示位置
    unsigned char e=0;     //定义一个char型变量,做延时用
    unsigned int  f=0;     //定义一个int型变量,显示内容用,显示内容0-9999

    TRISB=0B11111000;      //初始化RB7-RB0的输入输出方向
    TRISD=0B00000000;      //初始化RD7-RD0的输入输出方向
    PORTB=0B00000000;      //初始化RB7-RB0的数值
    PORTD=0B00000000;      //初始化RD7-RD0的数值

    while(1)               //死循环,单片机初始化后,将一直运行这个死循环
    {

        for(c=0;c<200;c++);//做一个0-250的循环,不执行其他操作,只为延时
        if(++e>10)        //做一个延时,时间到将显示内容加1
        {
            e=0;           //清零,为下一次延时做准备
            if(++f>9999) f=0;//显示内容加1,因为只有4位显示,超过9999后归零
        }
        PORTD=0;           //关一次显示,以免显示出鬼影
        if(++d>3) d=0;     //先将d加1,然后判断是否大于3,大于3归零
        if(d==0)           //如果d=0,显示千位
        {
            U5A=0;         //U5A=0,U5B=0,U5C=0,选通数码管的千位进行显示
            U5B=0;         //U5A=0,U5B=0,U5C=0,选通数码管的千位进行显示
            U5C=0;         //U5A=0,U5B=0,U5C=0,选通数码管的千位进行显示
            PORTD=LED[f/1000];       //将要显示的f的千位提取出来查表后送显示
        }
        else if(d==1)      //如果d=1,显示百位
        {
            U5A=1;         //U5A=1,U5B=0,U5C=0,选通数码管的百位进行显示
            U5B=0;         //U5A=1,U5B=0,U5C=0,选通数码管的百位进行显示
            U5C=0;         //U5A=1,U5B=0,U5C=0,选通数码管的百位进行显示
            PORTD=LED[(f%1000)/100]; //将要显示的f的百位提取出来查表后送显示
        }
        else if(d==2)      //如果d=2,显示十位
        {
            U5A=0;         //U5A=0,U5B=1,U5C=0,选通数码管的十位进行显示
            U5B=1;         //U5A=0,U5B=1,U5C=0,选通数码管的十位进行显示
            U5C=0;         //U5A=0,U5B=1,U5C=0,选通数码管的十位进行显示
            PORTD=LED[(f%100)/10];   //将要显示的f的十位提取出来查表后送显示
        }
        else if(d==3)      //如果d=3,显示个位
        {
            U5A=1;         //U5A=1,U5B=1,U5C=0,选通数码管的个位进行显示
            U5B=1;         //U5A=1,U5B=1,U5C=0,选通数码管的个位进行显示
            U5C=0;         //U5A=1,U5B=1,U5C=0,选通数码管的个位进行显示
            PORTD=LED[f%10];         //将要显示的f的个位提取出来查表后送显示
        }
    }
}


例程的编译结果:

Memory Summary:
    Program space        used    EBh (   235) of  2000h words   (  2.9%)
    Data space           used    13h (    19) of   170h bytes   (  5.2%)
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
    Configuration bits   used     1h (     1) of     1h word    (100.0%)
    ID Location space    used     0h (     0) of     4h bytes   (  0.0%)



从编译结果来看是只差了10个字节,但是我的程序比例程还要短几十个语句,里外里可能差几十个字节

出0入0汤圆

发表于 2014-7-23 13:54:48 | 显示全部楼层
求模指令是大头
对非2次方的数字求模,一个C语言指令可以产生几十条汇编指令

出0入0汤圆

 楼主| 发表于 2014-7-23 13:57:11 | 显示全部楼层
ruanxianwu 发表于 2014-7-23 13:54
求模指令是大头
对非2次方的数字求模,一个C语言指令可以产生几十条汇编指令 ...

但俩程序都有求模运算啊

出0入0汤圆

 楼主| 发表于 2014-7-23 13:58:03 | 显示全部楼层
ruanxianwu 发表于 2014-7-23 13:54
求模指令是大头
对非2次方的数字求模,一个C语言指令可以产生几十条汇编指令 ...

并且俩程序是一样的求模运算

出0入0汤圆

发表于 2014-7-23 14:29:03 | 显示全部楼层
本帖最后由 tam2907 于 2014-7-23 14:32 编辑

”一个是我写的,一个是例程,运行结果类似“  我看不然。

如果是你自建的工程,与别人建的工程比较,的确有可能。

出0入0汤圆

发表于 2014-7-23 14:35:33 | 显示全部楼层
>> 并且俩程序是一样的求模运算

不是一样。例程将要显示的f的个位提取出来查表后送显示(f%10),而你的是(i/1000),本身就是错的。

出0入0汤圆

发表于 2014-7-23 14:41:56 | 显示全部楼层
我还没看过这么长的程序                    

出0入0汤圆

 楼主| 发表于 2014-7-23 15:40:13 | 显示全部楼层
alias 发表于 2014-7-23 14:35
>> 并且俩程序是一样的求模运算

不是一样。例程将要显示的f的个位提取出来查表后送显示(f%10),而你的是(i ...

那个是错了,但是改过来一样的结果,还是我那个编译结果要大

出0入0汤圆

 楼主| 发表于 2014-7-23 15:42:03 | 显示全部楼层
tam2907 发表于 2014-7-23 14:29
”一个是我写的,一个是例程,运行结果类似“  我看不然。

如果是你自建的工程,与别人建的工程比较,的确 ...

我都是测试过的,都是在四位数码管显示累加结果的,我那个当然会有暗影,不过我想问的不是这个问题,而是最终代码的问题。

出0入0汤圆

发表于 2014-7-23 15:45:56 | 显示全部楼层
同样优化后编译 看看结果如何

出0入0汤圆

 楼主| 发表于 2014-7-23 15:54:38 | 显示全部楼层
mcucow 发表于 2014-7-23 15:45
同样优化后编译 看看结果如何

当然是一样的环境了

出0入0汤圆

发表于 2014-7-23 16:07:55 | 显示全部楼层
zhiguangqi 发表于 2014-7-23 15:40
那个是错了,但是改过来一样的结果,还是我那个编译结果要大

改了还是错。

(i/10) 和 (i%10) 是不同的。

出0入0汤圆

发表于 2014-7-23 16:27:44 | 显示全部楼层
看一下汇编不就一目了然了么

出0入0汤圆

 楼主| 发表于 2014-7-23 16:33:56 | 显示全部楼层
alias 发表于 2014-7-23 16:07
改了还是错。

(i/10) 和 (i%10) 是不同的。

你有环境的话编译一下就知道了,没必要纠结这个,我这个也是测试的时候改错的,找不到问题根源

出0入0汤圆

 楼主| 发表于 2014-7-23 16:34:40 | 显示全部楼层
lcofjp 发表于 2014-7-23 16:27
看一下汇编不就一目了然了么

汇编当然不一样了,我看了,但是不知道什么原因导致的这个

出0入0汤圆

发表于 2014-7-23 17:08:15 | 显示全部楼层
zhiguangqi 发表于 2014-7-23 16:34
汇编当然不一样了,我看了,但是不知道什么原因导致的这个

你看了汇编的话,就能看到是哪些C语句导致指令指令增加的。
我觉得,差异可能出在这个上面
类似这样的语句PORTB = 0X02;
要比下面的代码多
RB0 = 0;
RB1 = 1;
你改下试试?

出0入0汤圆

发表于 2014-7-23 19:35:36 | 显示全部楼层
zhiguangqi 发表于 2014-7-23 16:33
你有环境的话编译一下就知道了,没必要纠结这个,我这个也是测试的时候改错的,找不到问题根源 ...

没必要纠结?

把错的程式和正确的比较记忆使用量大小,你不觉得你是在浪费时间吗?

出0入0汤圆

发表于 2014-7-23 19:57:10 | 显示全部楼层
>> 类似这样的语句PORTB = 0X02;
>> 要比下面的代码多
>>  RB0 = 0;
>>  RB1 = 1;

完全不合逻辑。

PortB = 0x02 由 movlw 0x02 及 movwf portB 组成。 RB0=0, RB1=1 由 bcf portB,0 及 bsf portB,1 组成,大家都是占用2字节。

后者连续进行位元改写运作,在 PIC 的 R-M-W 限制下非常不可靠。好的编译器可能会在两个指令间插入 NOP,至编译后会多占字节。

出0入20汤圆

发表于 2014-7-23 20:21:10 | 显示全部楼层
如果是差几百个字节,哪还有讨论的余地,几个字节,太浪费表情了。

出0入0汤圆

 楼主| 发表于 2014-7-24 09:00:39 | 显示全部楼层
alias 发表于 2014-7-23 19:57
>> 类似这样的语句PORTB = 0X02;
>> 要比下面的代码多
>>  RB0 = 0;

这几个都是单字节指令好不

出0入0汤圆

 楼主| 发表于 2014-7-24 09:03:47 | 显示全部楼层
mon51 发表于 2014-7-23 20:21
如果是差几百个字节,哪还有讨论的余地,几个字节,太浪费表情了。

我想说的是,同一个函数在不同的位置为何编译出来结果差那么多
。而不是单纯讨论程序的长度

出0入31汤圆

发表于 2014-7-24 09:07:00 | 显示全部楼层
1、确保开花环境设置一样,可以这样测试,直接复制一份例程,在例程上面改成你的程序;
2、实在不行就比较一下汇编,代码不多,应该还是能看出来的。

出0入0汤圆

 楼主| 发表于 2014-7-24 09:54:58 | 显示全部楼层
zchong 发表于 2014-7-24 09:07
1、确保开花环境设置一样,可以这样测试,直接复制一份例程,在例程上面改成你的程序;
2、实在不行就比较 ...

其实关键就是这个语句PORTD=LED[f/1000]在我那里和例程里的汇编代码差别太大,为什么呢

出0入0汤圆

发表于 2014-7-24 10:49:18 | 显示全部楼层
zhiguangqi 发表于 2014-7-24 09:54
其实关键就是这个语句PORTD=LED[f/1000]在我那里和例程里的汇编代码差别太大,为什么呢 ...

因为这是C编译器,感觉还是有点傻
代码改成如下,则可以更省:
#include <pic.h>

__CONFIG(FOSC_HS & WDTE_OFF & LVP_OFF);

const unsigned char led[] =
{
                                                        //定义表格一定要使用const,这样会做到程序存储区中
    0B00111111,                                //"0"的字形表,0x3F
    0B00000110,                                //"1"的字形表,0x06
    0B01011011,                                //"2"的字形表,0x5B
    0B01001111,                                //"3"的字形表,0x4F
    0B01100110,                                //"4"的字形表,0x66
    0B01101101,                                //"5"的字形表,0x6D
    0B01111101,                                //"6"的字形表,0x7D
    0B00000111,                                //"7"的字形表,0x07
    0B01111111,                                //"8"的字形表,0x7F
    0B01101111,                                //"9"的字形表,0x6F
};

void main (void)
{
        unsigned int i = 0;
        unsigned char j = 0;
        unsigned char k = 0;

        TRISD = 0X0;
        TRISB = 0X0;
        PORTB = 0X0;
        PORTD = 0X0;
        
        while(1)
        {
                for (j = 0; j < 200; j++);

                PORTD = 0;
                if (++i > 9999) i = 0;
               
                if(++k > 3) k = 0;
                if(k==0)
                {
                        PORTB = 0X0;
                        PORTD = led[i / 1000];
                }
                else if(k==1)
                {       
                        PORTB = 0X01;       
                        PORTD = led[(i % 1000) / 100];
                }
                else if(k==2)
                {
                        PORTB = 0X02;
                        PORTD = led[(i % 100) / 10];
                }
                else if(k==3)
                {
                        PORTB = 0X03;
                        PORTD = led[i %10];
                }
        }
}

分析原因,编译器对
if()
{
}
else if()
{
}
else if()
{
}
else if()
{
}
结构内的代码做了不一样的处理,具体就是
1、PORTD = led[(i % 100) / 10];
汇编肯定包含了汇编取余、除法部分
2、PORTD = led[i / 1000];
除法部分调用了1中的处分部分
3、PORTD = led[(i % 1000) / 100];
则是直接调用了1的取余、除法部分;
这样一来,就省了不少FLASH了嘛

不采用
if()
{
}
else if()
{
}
else if()
{
}
else if()
{
}
结构你写的代码,分析编译后的汇编
PORTD = led[i / 1000];
PORTD = led[(i % 1000) / 100];
PORTD = led[(i % 100) / 10];
这三个部分中的取余、除法都是各自独立的
所以生成的代码就大了

出0入0汤圆

 楼主| 发表于 2014-7-24 13:52:03 | 显示全部楼层
yklstudent 发表于 2014-7-24 10:49
因为这是C编译器,感觉还是有点傻
代码改成如下,则可以更省:
#include

你说的最靠谱了,其实我就是想知道为什么会出这种情况。多谢

出0入0汤圆

 楼主| 发表于 2014-7-24 13:59:06 | 显示全部楼层
yklstudent 发表于 2014-7-24 10:49
因为这是C编译器,感觉还是有点傻
代码改成如下,则可以更省:
#include

的确又小了不少,这个C真是够傻的。是不是别的函数也会有这个现象发生呢?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-6 03:27

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表