We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
In the book "TCP/IP Lean" [1], the author states: "I have used #define in preference to typedef because compilers use better optimisation strategies for their native data types." Is this true of the Keil C51 compiler? ie, will C51 generate better-optimised code from source using
#define U8 unsigned char
typedef unsigned char U8
Andrew, I'm quite keen to know the answer to this also; not only for C51, but for other compilers in general, since I use many. This would be a good question to post on USENET's comp.lang.c.moderated also. Folks that actually implement compilers often lurk there. Maybe comp.arch.embedded too. In fact Mr. Bentham is involved in a dialog there now regarding TCP/IP for 8-bitters. Not wanting to steal your question, I wonder, would you consider posting to one or both of the above newsgroups? --Dan Henry
There are two traditional places to optimize the code generated by a C program. In the front-end. This involves optimizing the program from the aspect of the C source code. In the back-end. This involves optimizing the program from the aspect of the intermediate or assembly code generated. Since most of the serious optimizations are done in the back-end of the compiler, whether you use typedef or #define is immaterial. To test this, I created a simple program:
#include <stdio.h> typedef unsigned int WORD1; #define WORD2 unsigned int void tst_typedef (void) { WORD1 i; for (i = 0; i < 1000; i++) { printf ("i = %u\n", i); printf ("2i = %u\n", 2 * i); } } void tst_define (void) { WORD2 i; for (i = 0; i < 1000; i++) { printf ("i = %u\n", i); printf ("2i = %u\n", 2 * i); } } void main (void) { tst_typedef (); tst_define (); while (1); }
ASSEMBLY LISTING OF GENERATED OBJECT CODE ; FUNCTION tst_typedef (BEGIN) ; SOURCE LINE # 6 ; SOURCE LINE # 7 ; SOURCE LINE # 10 0000 E4 CLR A 0001 F500 R MOV i,A 0003 F500 R MOV i+01H,A 0005 ?C0001: ; SOURCE LINE # 11 ; SOURCE LINE # 12 0005 7BFF MOV R3,#0FFH 0007 7A00 R MOV R2,#HIGH ?SC_0 0009 7900 R MOV R1,#LOW ?SC_0 000B 850000 E MOV ?_printf?BYTE+03H,i 000E 850000 E MOV ?_printf?BYTE+04H,i+01H 0011 120000 E LCALL _printf ; SOURCE LINE # 13 0014 7BFF MOV R3,#0FFH 0016 7A00 R MOV R2,#HIGH ?SC_8 0018 7900 R MOV R1,#LOW ?SC_8 001A E500 R MOV A,i+01H 001C 25E0 ADD A,ACC 001E F500 E MOV ?_printf?BYTE+04H,A 0020 E500 R MOV A,i 0022 33 RLC A 0023 F500 E MOV ?_printf?BYTE+03H,A 0025 120000 E LCALL _printf ; SOURCE LINE # 14 0028 0500 R INC i+01H 002A E500 R MOV A,i+01H 002C 7002 JNZ ?C0012 002E 0500 R INC i 0030 ?C0012: 0030 C3 CLR C 0031 94E8 SUBB A,#0E8H 0033 E500 R MOV A,i 0035 9403 SUBB A,#03H 0037 40CC JC ?C0001 ; SOURCE LINE # 15 0039 ?C0004: 0039 22 RET ; FUNCTION tst_typedef (END) ; FUNCTION tst_define (BEGIN) ; SOURCE LINE # 17 ; SOURCE LINE # 18 ; SOURCE LINE # 21 0000 E4 CLR A 0001 F500 R MOV i,A 0003 F500 R MOV i+01H,A 0005 ?C0005: ; SOURCE LINE # 22 ; SOURCE LINE # 23 0005 7BFF MOV R3,#0FFH 0007 7A00 R MOV R2,#HIGH ?SC_0 0009 7900 R MOV R1,#LOW ?SC_0 000B 850000 E MOV ?_printf?BYTE+03H,i 000E 850000 E MOV ?_printf?BYTE+04H,i+01H 0011 120000 E LCALL _printf ; SOURCE LINE # 24 0014 7BFF MOV R3,#0FFH 0016 7A00 R MOV R2,#HIGH ?SC_8 0018 7900 R MOV R1,#LOW ?SC_8 001A E500 R MOV A,i+01H 001C 25E0 ADD A,ACC 001E F500 E MOV ?_printf?BYTE+04H,A 0020 E500 R MOV A,i 0022 33 RLC A 0023 F500 E MOV ?_printf?BYTE+03H,A 0025 120000 E LCALL _printf ; SOURCE LINE # 25 0028 0500 R INC i+01H 002A E500 R MOV A,i+01H 002C 7002 JNZ ?C0013 002E 0500 R INC i 0030 ?C0013: 0030 C3 CLR C 0031 94E8 SUBB A,#0E8H 0033 E500 R MOV A,i 0035 9403 SUBB A,#03H 0037 40CC JC ?C0005 ; SOURCE LINE # 26 0039 ?C0008: 0039 22 RET ; FUNCTION tst_define (END) ; FUNCTION main (BEGIN) ; SOURCE LINE # 29 ; SOURCE LINE # 30 ; SOURCE LINE # 31 0000 120000 R LCALL tst_typedef ; SOURCE LINE # 32 0003 120000 R LCALL tst_define 0006 ?C0009: ; SOURCE LINE # 34 0006 80FE SJMP ?C0009 ; FUNCTION main (END)
Thanks, Jon. This is consistent with my limited knowledge of compiler design. The compiler knows how to deal with objects and expressions having particular characteristics, with size and "type" being among those characteristics. It should not make any difference whether a compiler learns about an object having one of the "native" types directly through a macro expansion or by "looking back" to resolve a typedef. Two objects having the same "root type" should be treated the same. Of course, I could be wrong entirely. ;-) --Dan Henry