51单片机入门 - SDCC / Keil_C51 会让没有调用的函数参与编译吗?

Posted 风好衣轻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51单片机入门 - SDCC / Keil_C51 会让没有调用的函数参与编译吗?相关的知识,希望对你有一定的参考价值。

Small Device C Compiler(SDCC)是一款免费 C 编译器,适用于 8 位微控制器。

不想看测试过程的话可以直接划到最下面看结论:)

关于软硬件环境的信息:

  • Windows 10
  • STC89C52RC
  • SDCC (构建HEX文件)
  • stcgal 1.6 (向STC单片机烧录)

修改代码中的数值会改变编译后的结果

显然,在我们修改一些 C 语言代码的时候,编译后的二进制文件也可能发生改变,尤其是一些关键的数值,在 51 单片机开发中,C 代码会编译成 .HEX 文件,然后烧录到单片机。

下面用 SDCC 编译这个示例程序来演示:

  1. 这是个选择一位数码管并使其亮起的程序:
    #include <8051.h>
    
    #define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
    #define decoder_in_2 P2_3
    #define decoder_in_3 P2_4
    #define NUMBER P0
    
    unsigned int LED_MAP[11] = 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f;
    
    void send_to_decoder(unsigned int position)   // position: 1 ~ 8 
        position--;                          // position: 0 ~ 7 (000 ~ 111)
        decoder_in_1 = position & 1;         // low    bit of position (position & 001)
        decoder_in_2 = position & 2;         // middle bit of position (position & 010)
        decoder_in_3 = position & 4;         // high   bit of position (position & 100)
    
    
    void main() 
        send_to_decoder(1);  // 位选:选择第 1 位数码管亮起
        NUMBER = LED_MAP[6]; // 段选:数码管显示数字 6
    
    
  2. 改变了上面代码的位选,选择第 2 位数码管亮起:
    #include <8051.h>
    
    #define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
    #define decoder_in_2 P2_3
    #define decoder_in_3 P2_4
    #define NUMBER P0
    
    unsigned int LED_MAP[11] = 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f;
    
    void send_to_decoder(unsigned int position)   // position: 1 ~ 8 
        position--;                          // position: 0 ~ 7 (000 ~ 111)
        decoder_in_1 = position & 1;         // low    bit of position (position & 001)
        decoder_in_2 = position & 2;         // middle bit of position (position & 010)
        decoder_in_3 = position & 4;         // high   bit of position (position & 100)
    
    
    void main() 
        send_to_decoder(2);  // 位选:选择第 2 位数码管亮起
        NUMBER = LED_MAP[6]; // 段选:数码管显示数字 6
    
    

对比它们生成的 HEX 文件的区别:

slightwind@ubuntu:/xxx/diff$ diff 1.hex 2.hex
9c9
< :0C00BE00A42290000112009E85148022F4
---
> :0C00BE00A42290000212009E85148022F3

可以看到第 9 行出现了区别。

头文件中没有被调用的函数

把数组 LED_MAP 放到头文件中,再从主函数中调用,头文件还包含了程序没有调用过的 sleep()
swutil.h

#ifndef __SWUTIL_H__
#define __SWUTIL_H__

unsigned int LED_MAP[11] = 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f;

void sleep(unsigned int n); // sleep n (ms)

#endif

swutil.c

void sleep(unsigned int n)
    unsigned char i, j;
    while (n--)
        i = 2;
        j = 239;
        do
            while(--j);
        while(--i);
    

main.c

#include <8051.h>
#include "swutil.h"

#define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
#define decoder_in_2 P2_3
#define decoder_in_3 P2_4
#define NUMBER P0


void send_to_decoder(unsigned int position)   // position: 1 ~ 8 
    position--;                          // position: 0 ~ 7 (000 ~ 111)
    decoder_in_1 = position & 1;         // low    bit of position (position & 001)
    decoder_in_2 = position & 2;         // middle bit of position (position & 010)
    decoder_in_3 = position & 4;         // high   bit of position (position & 100)


void main() 
    send_to_decoder(1);  // 位选:选择第 1 位数码管亮起
    NUMBER = LED_MAP[6]; // 段选:数码管显示数字 6

这时使用 SDCC 编译后生成的 HEX 文件和前面第一个实例代码生成的 HEX 文件完全一致,说明没有额外的函数参与了编译,也就是说尽管导入了比较复杂的头文件,编译器也只会让调用过的函数参与编译。

主文件中没有调用的函数

下面这个程序在前面第一个实例代码的基础上额外定义了 sleep() 函数,但是没有调用:

#include <8051.h>

#define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
#define decoder_in_2 P2_3
#define decoder_in_3 P2_4
#define NUMBER P0

unsigned int LED_MAP[11] = 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f;

void send_to_decoder(unsigned int position)   // position: 1 ~ 8 
    position--;                          // position: 0 ~ 7 (000 ~ 111)
    decoder_in_1 = position & 1;         // low    bit of position (position & 001)
    decoder_in_2 = position & 2;         // middle bit of position (position & 010)
    decoder_in_3 = position & 4;         // high   bit of position (position & 100)


void sleep(unsigned int n)
    unsigned char i, j;
    while (n--)
        i = 2;
        j = 239;
        do
            while(--j);
        while(--i);
    


void main() 
    send_to_decoder(1);  // 位选:选择第 1 位数码管亮起
    NUMBER = LED_MAP[6]; // 段选:数码管显示数字 6


同样是用 SDCC 编译,diff 一下和第一个程序生成的 HEX 的区别,可以发现文件变大了,也有很多不一样的地方。

slightwind@ubuntu:/xxx/diff$ diff 1.hex 4.hex
6c6
< :030003000200C038
---
> :030003000200DC1C
9c9,11
< :0C00BE00A42290000112009E85148022F4
---
> :1000BE00A422AE82AF838E048F051EBEFF011FECFD
> :1000CE004D600A7DEF7C02DDFEDCFC80E9229000B3
> :0800DE000112009E851480222E
11c13
< :100013007900E94400601B7A009000CE780175A056
---
> :100013007900E94400601B7A009000EA780175A03A
17,18c19,20
< :0D00060075811D1200CAE58260030200032F
< :0400CA007582002219
---
> :0D00060075811D1200E6E582600302000313
> :0400E60075820022FD

所以在 main.c 中定义的函数,即使没有调用,也会参与 HEX 文件的构建。

在Keil_C51编译器上的测试

另外,我还将编译器换成了 Keil_C51 进行了一遍上述的测试,发现不管是包含的头文件中的函数,还是 main.c 中定义的函数,只要没有调用,都不会参与到编译中1,这样有利于生成更小的 HEX 文件。

结论

在 SDCC 中,使用的头文件中声明的函数,没有调用的话是不会参与编译的,但是在 main.c 里面直接定义的函数,尽管没有调用也是会参与编译的。
以这个简单的例子看来,在编译优化方面还是 Keil 做的更专业一些,不过 SDCC 胜在免费,是不想使用付费/破解软件时的一个不错的替代品。


  1. 这只是从上面这个简单的示例程序中表现出来的现象得到的结论,编译器的实现和优化工作非常复杂,在其他情况下可能表现出不同的结果。 ↩︎

以上是关于51单片机入门 - SDCC / Keil_C51 会让没有调用的函数参与编译吗?的主要内容,如果未能解决你的问题,请参考以下文章

基于免费的SDCC开发51单片机

基于免费的SDCC开发51单片机

Proteus仿真51单片机基于SDCC编译器对程序编译和仿真全过程讲解

单片机成长之路(51基础篇) - 009 关于sdcc的多文件编译范例

单片机成长之路(51基础篇) - 015 关于sdcc的多文件编译范例二

我的STC单片机型号是STC90C516RD+。在keil_c中64Kflash程序存储器怎么全把它用上啊?