How to define an integer constant in C?
This is a tricky issue with a number of options available in C. They include:

    3.   Explicit use of literal
    4.   Use of preprocessor #define
    5.   Use of enum
    6.   Use of constant int without or with address references

The following table shows a comparison for the above options in terms of CPP, CC and GDB:

    Constant Option        C Preprocessor           C Compiler                     C Debugger
    Integer Literal        • Does not see any       • Sees a Literal               • No Debugger Symbol
                              Literal or Symbol     • Replaces in Expressions         Entry
    (10)                                            • No Memory Binding
    Define Constant        •   Sees a Symbol        • Sees a Literal               •   No Debugger Symbol
                           •   Replaces Textually   • Replaces in Expressions          Entry
    #define TEN 10                                  • No Memory Binding
    Enum Literal           •   Does not see any     • Sees a Symbol                •   Debugger Symbol with
                               Literal or Symbol    • Symbol is Constant               Constant Value
    enum { TEN = 10 };                              • Replaces in Expressions      •   Address Operation on
                                                    • No Memory Binding                Symbol an Error
                                                       Created; and is Not
                                                       allowed
    Constant Integer       •   Does not see any     • Sees a Symbol                •   Debugger Symbol with
                               Literal or Symbol    • Symbol is Constant               Constant Value
    const int TEN = 10;                             • Replaces in Expressions      •   Address Operation on
                                                    • No Memory Binding                Symbol an Error
                                                       Created; but is allowed
    Constant Integer       •   Does not see any     • Sees a Symbol                •   Debugger Symbol with
    (Address Used)             Literal or Symbol    • Symbol is Constant               Constant Value
                                                    • Replaces in Expressions      •   Address Operation on
    const int TEN = 10;                                for direct use; indirect        Symbol allowed
    &TEN;                                              use may not be replaced
                                                    • Memory Binding Created
                                                       in Constant Segment

Next, we illustrate the above with a code below:

#define u 1             // u: Symbol - No. Memory                     -   No
enum { v = 1 };         // v: Symbol - No. Memory                     -   No;     Disallowed
const int w = 1;        // w: Symbol - Yes. Memory                    -   No;     Allowed
const int x = 1;        // x: Symbol - Yes. Memory                    -   Yes     (Const Segment)
const int *p = &x;      // p: Symbol - Yes. Memory                    -   Yes     (Data / Static Segment)
void main()
{
      // LITERAL
      int a[1];
      a[0] = 1;
      // DEFINE CONSTANT
      // 'u' is replaced by CPP
      // Expression '&u' is illegal in CPP
      // Symbol 'u' is undefined in Debugger
      int b[u];
      b[0] = u;
      // ENUM LITERAL
      // 'v' is replaced by CPP
      // Expression '&v' is illegal in CPP
      // Symbol 'v' is defined in Debugger
      int c[v];
c[0] = v;
       // CONST INT
       // 'w' is replaced by CPP
       // Expression '&w' is legal in CPP
       // Symbol 'w' is defined in Debugger
       // Expression '&w' is undefined in Debugger
       int d[w];
       d[0] = w;
       // CONST INT - ADDRESS TAKEN
       // 'x' is replaced by CPP
       // Expression '&x' is legal in CPP
       // Symbol 'x' is defined in Debugger
       // Expression '&x' is defined in Debugger
       // Reference to 'x' may not be optimized
       int e[x];
       e[0] = *p;
       return;
}

First let us take a look into the CPP output to understand what is actually ‘seen’ by the C
Compiler. The large number of blank lines is due to omitted comment lines from the source.

#line 1 "d:personal_transparent programming_projectsdefine
constantsdefine constantsmain.cxx"
enum { v = 1 };
const int w = 1;
const int x = 1;
const int *p = &x;
void main()
{
      int a[1];
      a[0] = 1;

       int b[1];
       b[0] = 1;

       int c[v];
       c[0] = v;

       int d[w];
       d[0] = w;

       int e[x];
       e[0] = *p;
       return;
}

Note that the #define symbol ‘u’ has completely disappeared from the source.

Next let us take a look at the Debug Assembly for the code. This tells us about:
   7. Constant folding / propagation
   8. The lack of optimizations and
   9. Memory binding, wherever applicable. These have been highlighted
; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.42
      TITLE d:Personal_Transparent Programming_ProjectsDefine
ConstantsDefine ConstantsMain.cxx
      .686P
      .XMM
      include listing.inc
      .model      flat
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
PUBLIC      ?p@@3PBHB                            ; p
_DATA SEGMENT
?p@@3PBHB DD      FLAT:_x                              ; p
_DATA ENDS
CONST SEGMENT
_x    DD    01H
CONST ENDS
PUBLIC      _main
EXTRN @_RTC_CheckStackVars@8:PROC
EXTRN __RTC_Shutdown:PROC
EXTRN __RTC_InitBase:PROC
;     COMDAT rtc$TMZ
; File d:personal_transparent programming_projectsdefine constantsdefine
constantsmain.cxx
rtc$TMZ     SEGMENT
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown
rtc$TMZ     ENDS
;     COMDAT rtc$IMZ
rtc$IMZ     SEGMENT
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase
; Function compile flags: /Odtp /RTCsu /ZI
rtc$IMZ     ENDS
;     COMDAT _main
_TEXT SEGMENT
_e$ = -56                                  ; size = 4
_d$ = -44                                  ; size = 4
_c$ = -32                                  ; size = 4
_b$ = -20                                  ; size = 4
_a$ = -8                                   ; size = 4
_main PROC                                 ; COMDAT
; 13   : {
      push ebp
      mov   ebp, esp
      sub   esp, 252                       ; 000000fcH
      push ebx
      push esi
      push edi
      lea   edi, DWORD PTR [ebp-252]
      mov   ecx, 63                              ; 0000003fH
      mov   eax, -858993460                      ; ccccccccH
      rep stosd
; 14   :    // LITERAL
; 15   :    int a[1];
; 16   :    a[0] = 1;
      mov   DWORD PTR _a$[ebp], 1
; 17   :
; 18   :    // DEFINE CONSTANT
; 19   :    // 'u' is replaced by CPP
; 20   :    // Expression '&u' is illegal in CPP
; 21   :    // Symbol 'u' is undefined in Debugger
; 22   :    int b[u];
; 23   :    b[0] = u;
      mov   DWORD PTR _b$[ebp], 1
; 24   :
; 25   :    // ENUM LITERAL
; 26   :    // 'v' is replaced by CPP
; 27   :    // Expression '&v' is illegal in CPP
; 28   :    // Symbol 'v' is defined in Debugger
; 29   :    int c[v];
; 30   :    c[0] = v;
      mov   DWORD PTR _c$[ebp], 1
;   31 :
;   32 :      // CONST INT
;   33 :      // 'w' is replaced by CPP
;   34 :      // Expression '&w' is legal in CPP
;   35 :      // Symbol 'w' is defined in Debugger
;   36 :      // Expression '&w' is undefined in Debugger
;   37 :      int d[w];
;   38 :      d[0] = w;
      mov     DWORD PTR _d$[ebp], 1
; 39   :
; 40   :
; 41   :      // CONST INT - ADDRESS TAKEN
; 42   :      // 'x' is replaced by CPP
; 43   :      // Expression '&x' is legal in CPP
; 44   :      // Symbol 'x' is defined in Debugger
; 45   :      // Expression '&x' is defined in Debugger
; 46   :      // Reference to 'x' may not be optimized
; 47   :      int e[x];
; 48   :      e[0] = *p;
      mov     eax, DWORD PTR ?p@@3PBHB             ; p
      mov     ecx, DWORD PTR [eax]
      mov     DWORD PTR _e$[ebp], ecx
; 49   :
; 50   : //   Test(a[0], b[0], c[0], d[0], e[0]);
; 51   :
; 52   :      return;
; 53   : }
      xor     eax, eax
      push    edx
      mov     ecx, ebp
      push    eax
      lea     edx, DWORD PTR $LN9@main
      call    @_RTC_CheckStackVars@8
      pop     eax
      pop     edx
      pop     edi
      pop     esi
      pop     ebx
      mov     esp, ebp
      pop     ebp
      ret     0
      npad    2
$LN9@main:
      DD      5
      DD      $LN8@main
$LN8@main:
      DD      -8                            ; fffffff8H
      DD      4
      DD      $LN3@main
      DD      -20                           ; ffffffecH
      DD      4
      DD      $LN4@main
      DD      -32                           ; ffffffe0H
      DD      4
      DD      $LN5@main
      DD      -44                           ; ffffffd4H
      DD      4
      DD      $LN6@main
      DD      -56                           ; ffffffc8H
      DD      4
      DD      $LN7@main
$LN7@main:
      DB      101                           ; 00000065H
DB      0
$LN6@main:
      DB      100                                   ; 00000064H
      DB      0
$LN5@main:
      DB      99                                    ; 00000063H
      DB      0
$LN4@main:
      DB      98                                    ; 00000062H
      DB      0
$LN3@main:
      DB      97                                    ; 00000061H
      DB      0
_main ENDP
_TEXT ENDS
END

Please note that unless the address is taken the const int option has all the advantages of the
other schemes and gives rise to the same code (as in other cases). In addition, it can make the
constant symbol visible at the debugger. Interestingly, if a[0], b[0] etc are used somewhere, then
the above codes for their initialization may also get optimized out (unless they are reassigned
some other value at some other place). The same may apply to initialization via *p provided p is
not reassigned in the meanwhile.

Moral: Use const int and do not take its address.

How To Define An Integer Constant In C

  • 1.
    How to definean integer constant in C? This is a tricky issue with a number of options available in C. They include: 3. Explicit use of literal 4. Use of preprocessor #define 5. Use of enum 6. Use of constant int without or with address references The following table shows a comparison for the above options in terms of CPP, CC and GDB: Constant Option C Preprocessor C Compiler C Debugger Integer Literal • Does not see any • Sees a Literal • No Debugger Symbol Literal or Symbol • Replaces in Expressions Entry (10) • No Memory Binding Define Constant • Sees a Symbol • Sees a Literal • No Debugger Symbol • Replaces Textually • Replaces in Expressions Entry #define TEN 10 • No Memory Binding Enum Literal • Does not see any • Sees a Symbol • Debugger Symbol with Literal or Symbol • Symbol is Constant Constant Value enum { TEN = 10 }; • Replaces in Expressions • Address Operation on • No Memory Binding Symbol an Error Created; and is Not allowed Constant Integer • Does not see any • Sees a Symbol • Debugger Symbol with Literal or Symbol • Symbol is Constant Constant Value const int TEN = 10; • Replaces in Expressions • Address Operation on • No Memory Binding Symbol an Error Created; but is allowed Constant Integer • Does not see any • Sees a Symbol • Debugger Symbol with (Address Used) Literal or Symbol • Symbol is Constant Constant Value • Replaces in Expressions • Address Operation on const int TEN = 10; for direct use; indirect Symbol allowed &TEN; use may not be replaced • Memory Binding Created in Constant Segment Next, we illustrate the above with a code below: #define u 1 // u: Symbol - No. Memory - No enum { v = 1 }; // v: Symbol - No. Memory - No; Disallowed const int w = 1; // w: Symbol - Yes. Memory - No; Allowed const int x = 1; // x: Symbol - Yes. Memory - Yes (Const Segment) const int *p = &x; // p: Symbol - Yes. Memory - Yes (Data / Static Segment) void main() { // LITERAL int a[1]; a[0] = 1; // DEFINE CONSTANT // 'u' is replaced by CPP // Expression '&u' is illegal in CPP // Symbol 'u' is undefined in Debugger int b[u]; b[0] = u; // ENUM LITERAL // 'v' is replaced by CPP // Expression '&v' is illegal in CPP // Symbol 'v' is defined in Debugger int c[v];
  • 2.
    c[0] = v; // CONST INT // 'w' is replaced by CPP // Expression '&w' is legal in CPP // Symbol 'w' is defined in Debugger // Expression '&w' is undefined in Debugger int d[w]; d[0] = w; // CONST INT - ADDRESS TAKEN // 'x' is replaced by CPP // Expression '&x' is legal in CPP // Symbol 'x' is defined in Debugger // Expression '&x' is defined in Debugger // Reference to 'x' may not be optimized int e[x]; e[0] = *p; return; } First let us take a look into the CPP output to understand what is actually ‘seen’ by the C Compiler. The large number of blank lines is due to omitted comment lines from the source. #line 1 "d:personal_transparent programming_projectsdefine constantsdefine constantsmain.cxx" enum { v = 1 }; const int w = 1; const int x = 1; const int *p = &x; void main() { int a[1]; a[0] = 1; int b[1]; b[0] = 1; int c[v]; c[0] = v; int d[w]; d[0] = w; int e[x]; e[0] = *p; return; } Note that the #define symbol ‘u’ has completely disappeared from the source. Next let us take a look at the Debug Assembly for the code. This tells us about: 7. Constant folding / propagation 8. The lack of optimizations and 9. Memory binding, wherever applicable. These have been highlighted ; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.42 TITLE d:Personal_Transparent Programming_ProjectsDefine ConstantsDefine ConstantsMain.cxx .686P .XMM include listing.inc .model flat
  • 3.
    INCLUDELIB MSVCRTD INCLUDELIB OLDNAMES PUBLIC ?p@@3PBHB ; p _DATA SEGMENT ?p@@3PBHB DD FLAT:_x ; p _DATA ENDS CONST SEGMENT _x DD 01H CONST ENDS PUBLIC _main EXTRN @_RTC_CheckStackVars@8:PROC EXTRN __RTC_Shutdown:PROC EXTRN __RTC_InitBase:PROC ; COMDAT rtc$TMZ ; File d:personal_transparent programming_projectsdefine constantsdefine constantsmain.cxx rtc$TMZ SEGMENT __RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown rtc$TMZ ENDS ; COMDAT rtc$IMZ rtc$IMZ SEGMENT __RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase ; Function compile flags: /Odtp /RTCsu /ZI rtc$IMZ ENDS ; COMDAT _main _TEXT SEGMENT _e$ = -56 ; size = 4 _d$ = -44 ; size = 4 _c$ = -32 ; size = 4 _b$ = -20 ; size = 4 _a$ = -8 ; size = 4 _main PROC ; COMDAT ; 13 : { push ebp mov ebp, esp sub esp, 252 ; 000000fcH push ebx push esi push edi lea edi, DWORD PTR [ebp-252] mov ecx, 63 ; 0000003fH mov eax, -858993460 ; ccccccccH rep stosd ; 14 : // LITERAL ; 15 : int a[1]; ; 16 : a[0] = 1; mov DWORD PTR _a$[ebp], 1 ; 17 : ; 18 : // DEFINE CONSTANT ; 19 : // 'u' is replaced by CPP ; 20 : // Expression '&u' is illegal in CPP ; 21 : // Symbol 'u' is undefined in Debugger ; 22 : int b[u]; ; 23 : b[0] = u; mov DWORD PTR _b$[ebp], 1 ; 24 : ; 25 : // ENUM LITERAL ; 26 : // 'v' is replaced by CPP ; 27 : // Expression '&v' is illegal in CPP ; 28 : // Symbol 'v' is defined in Debugger ; 29 : int c[v]; ; 30 : c[0] = v; mov DWORD PTR _c$[ebp], 1
  • 4.
    ; 31 : ; 32 : // CONST INT ; 33 : // 'w' is replaced by CPP ; 34 : // Expression '&w' is legal in CPP ; 35 : // Symbol 'w' is defined in Debugger ; 36 : // Expression '&w' is undefined in Debugger ; 37 : int d[w]; ; 38 : d[0] = w; mov DWORD PTR _d$[ebp], 1 ; 39 : ; 40 : ; 41 : // CONST INT - ADDRESS TAKEN ; 42 : // 'x' is replaced by CPP ; 43 : // Expression '&x' is legal in CPP ; 44 : // Symbol 'x' is defined in Debugger ; 45 : // Expression '&x' is defined in Debugger ; 46 : // Reference to 'x' may not be optimized ; 47 : int e[x]; ; 48 : e[0] = *p; mov eax, DWORD PTR ?p@@3PBHB ; p mov ecx, DWORD PTR [eax] mov DWORD PTR _e$[ebp], ecx ; 49 : ; 50 : // Test(a[0], b[0], c[0], d[0], e[0]); ; 51 : ; 52 : return; ; 53 : } xor eax, eax push edx mov ecx, ebp push eax lea edx, DWORD PTR $LN9@main call @_RTC_CheckStackVars@8 pop eax pop edx pop edi pop esi pop ebx mov esp, ebp pop ebp ret 0 npad 2 $LN9@main: DD 5 DD $LN8@main $LN8@main: DD -8 ; fffffff8H DD 4 DD $LN3@main DD -20 ; ffffffecH DD 4 DD $LN4@main DD -32 ; ffffffe0H DD 4 DD $LN5@main DD -44 ; ffffffd4H DD 4 DD $LN6@main DD -56 ; ffffffc8H DD 4 DD $LN7@main $LN7@main: DB 101 ; 00000065H
  • 5.
    DB 0 $LN6@main: DB 100 ; 00000064H DB 0 $LN5@main: DB 99 ; 00000063H DB 0 $LN4@main: DB 98 ; 00000062H DB 0 $LN3@main: DB 97 ; 00000061H DB 0 _main ENDP _TEXT ENDS END Please note that unless the address is taken the const int option has all the advantages of the other schemes and gives rise to the same code (as in other cases). In addition, it can make the constant symbol visible at the debugger. Interestingly, if a[0], b[0] etc are used somewhere, then the above codes for their initialization may also get optimized out (unless they are reassigned some other value at some other place). The same may apply to initialization via *p provided p is not reassigned in the meanwhile. Moral: Use const int and do not take its address.