What are Integer Constants in C-Best Embedded C Programming Course

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Elements of C · Lecture 1

Integer Constants in C

Decimal, octal and hexadecimal literals, the U/L/LL suffixes, and the type the compiler really picks. Part of our free embedded systems course.

4
Runnable programs
8
Quiz questions
6
Interview questions
Free
Forever

Every number you type into a C program is a literal, and the whole-number ones are called integer constants in C. They look trivial, yet they hide the details that decide whether a register mask works, whether a file gets the right permissions, and whether a loop silently overflows. This lecture is the first checkpoint of our free embedded systems course and the foundation for the free Linux kernel development course and free Linux device drivers course that follow, because kernel and driver code is full of hex masks, octal modes and suffixed literals.

We will not just list rules. You will compile small programs, read the real compiler messages, predict outputs before revealing them, and finish with interview-grade questions. If you are following our free embedded Linux course in parallel, everything here applies unchanged to user-space and kernel C.

What You Will Learn

By the end of this lecture you can:

  • Classify any literal as a decimal, octal or hexadecimal integer constant at a glance
  • Apply the exact validity rules for integer constants in C and read the compiler’s complaint when they are broken
  • Use the U, L and LL suffixes and predict the type the compiler assigns on 32-bit and 64-bit systems
  • Explain why sizeof(0x80000000) and sizeof(2147483648) differ on a 64-bit Linux machine
  • Write embedded-style bit masks in hexadecimal and permission values in octal
  • Answer the classic interview traps about leading zeros, signed/unsigned comparison and overflow

Prerequisites

You should know what a C program looks like (a main function and printf) and have gcc installed. On Ubuntu or Debian run sudo apt install build-essential. A basic idea of binary numbers helps, but we rebuild it here from scratch.

Constants in C: The Big Picture

A constant is a value written directly in the source whose meaning is fixed before the program runs. The C standard recognises four families of such tokens: integer constants, floating constants, character constants and enumeration constants. String literals sit beside them as a fifth kind of fixed value. In this chapter we cover them one by one, and the diagram shows where integer constants fit.

Family tree of constants in C
Constants
▼
Numeric constantsCharacter constantsString constants
▼
Integer constantsReal (floating) constants
▼
Decimal (base 10)Octal (base 8)Hexadecimal (base 16)

Numeric constants split into integer and real; integer constants come in three bases. Character, string and real constants get their own lectures.

Rules for Writing Integer Constants in C

An integer constant is a whole number with no decimal point and no exponent. The compiler’s scanner (the lexer) accepts it only if all of these hold:

  1. It has at least one digit. A bare 0x with nothing after it is an error.
  2. It contains no spaces, commas or underscores. 98 5 is two tokens and 8,354 is the number 8, the comma operator, then 354.
  3. It uses only digits that belong to its base (table below).
  4. It has no sign of its own. In -25 the minus is the unary negation operator applied to the constant 25.
BaseHow it startsAllowed digitsExampleValue
Decimal1-9 (or the single digit 0)0 1 2 3 4 5 6 7 8 937053705
OctalLeading 00 1 2 3 4 5 6 70732474
Hexadecimal0x or 0X0-9, a-f, A-F0x1FA506

Decimal Integer Constants

These are the everyday numbers: 0, 123, 23759. A decimal constant never starts with 0 unless it is the number zero itself, because a leading zero is the signal that you want octal. That single rule is the source of the most famous beginner bug in C, which we will trigger on purpose in Example 3.

Octal Integer Constants

An octal constant starts with 0 and uses only the digits 0 to 7, so 077 is 7×8 + 7 = 63. Octal survives in modern code for one very practical reason: Unix permissions. Each permission group (owner, group, others) is three bits, and three bits map exactly to one octal digit. That is why you write chmod 0644 or pass 0644 as the mode argument of open() on Linux.

Writing a digit 8 or 9 after a leading zero is rejected outright. Compile int a = 09; and GCC says:

error: invalid digit "9" in octal constant

Hexadecimal Integer Constants

A hexadecimal constant starts with 0x or 0X followed by digits 0-9 and letters a-f in either case, so 0xFFF, 0xac and 0XA15B are all fine. Hardware people love hex because one hex digit is exactly four bits, so a 32-bit register is eight digits and you can read individual nibbles by eye. Datasheets, linker scripts and kernel headers are written in hex for this reason.

One value, three spellings: the number one hundred
Decimal100
Binary groups of 401100100
Hex digit per group64written 0x64
Binary groups of 3001100100
Octal digit per group144written 0144

Hex reads the bits four at a time and octal reads them three at a time, which is why both are shorthand for binary.

Written asValid?Why
3#5No# is not part of a number
98 5NoA space splits it into two tokens
0925NoLeading zero makes it octal, and 9 is not an octal digit
8,354NoCommas are not digit separators in C
2.5Not an integer constantIt is a valid floating constant instead
0xG1NoG is not a hexadecimal digit
0xfaUYesHex with an unsigned suffix

Newer C

The C23 standard added binary constants such as 0b1010 and the digit separator ' (as in 1'000'000). GCC has long accepted 0b as an extension, but if your code must build on older toolchains, stick to hex.

Integer Constant Types and Suffixes

Writing 7 does not just give you the value seven. The compiler also attaches a type. Without a suffix, it walks down a list of candidate types and picks the first one that can hold the value. Suffixes let you steer that choice yourself.

SuffixMeaningDecimal constant triesOctal / hex constant tries
noneplainint, long, long longint, unsigned int, long, unsigned long, long long, unsigned long long
Uunsignedunsigned int, unsigned long, unsigned long longsame as decimal
Llonglong, long longlong, unsigned long, long long, unsigned long long
ULunsigned longunsigned long, unsigned long longsame as decimal
LLlong longlong longlong long, unsigned long long
ULLunsigned long longunsigned long longsame as decimal

Notice the asymmetry. A decimal constant never silently becomes unsigned, but an octal or hex constant can. That is the trick behind the interview favourite in Example 2. Suffix letters are case-insensitive and the order UL or LU does not matter, but write the L in upper case: 10l looks far too much like 101.

How the compiler types an integer constant
Read the prefix: 0x = hex, 0 = octal, else decimal
▼
Read the suffix: U, L, LL or none
▼
Look up the candidate type list from the table
▼
Choose the first type whose range holds the value
▼
If none can hold it, the program is ill-formed (compiler warns or errors)
Platformintlonglong longTypical where
ILP324 bytes4 bytes8 bytes32-bit Linux, most Cortex-M microcontrollers
LP644 bytes8 bytes8 bytes64-bit Linux and macOS
LLP644 bytes4 bytes8 bytes64-bit Windows
16-bit MCU2 bytes4 bytes8 bytesAVR, MSP430, some 8051 compilers

The consequence is portability: the literal 40000 is an int on a 32-bit chip but a long on a 16-bit one. All examples below were run on 64-bit Linux (LP64) with GCC 13.

Hands-On: Four Programs on Integer Constants in C

Example 1: Same value, three bases

Goal

Prove that 100, 0144 and 0x64 are the same number and print it in all three bases.

Save this as int_bases.c:

#include <stdio.h>

int main(void)
{
    int dec = 100;
    int oct = 0144;
    int hex = 0x64;

    printf("dec=%d oct=%d hex=%d\n", dec, oct, hex);
    printf("100 printed in octal : %o\n", dec);
    printf("100 printed in hex   : %x\n", dec);
    printf("all three equal? %s\n", (dec == oct && oct == hex) ? "yes" : "no");
    return 0;
}

Build and run:

$ gcc -Wall -Wextra -std=c11 -o int_bases int_bases.c
$ ./int_bases
Interactive: Predict the output first, then click to check yourself.

Expected output:

dec=100 oct=100 hex=100
100 printed in octal : 144
100 printed in hex   : 64
all three equal? yes
  • %d prints decimal, %o prints octal (without the leading zero) and %x prints lower-case hex (without 0x).
  • The comparison is true because the base only changes how you write the number, never the stored bits.
  • Use %#o and %#x if you want the prefixes printed too.

Example 2: Which type did I get?

Goal

Print sizeof of several literals to see suffixes and the decimal-versus-hex difference in action.

Save this as int_types.c:

#include <stdio.h>

int main(void)
{
    printf("sizeof(7)          = %zu\n", sizeof(7));
    printf("sizeof(7U)         = %zu\n", sizeof(7U));
    printf("sizeof(7L)         = %zu\n", sizeof(7L));
    printf("sizeof(7UL)        = %zu\n", sizeof(7UL));
    printf("sizeof(7LL)        = %zu\n", sizeof(7LL));
    printf("sizeof(2147483648) = %zu\n", sizeof(2147483648));
    printf("sizeof(0x80000000) = %zu\n", sizeof(0x80000000));
    return 0;
}

Build and run:

$ gcc -Wall -Wextra -std=c11 -o int_types int_types.c
$ ./int_types
Interactive: Predict the output first, then click to check yourself.

Expected output:

sizeof(7)          = 4
sizeof(7U)         = 4
sizeof(7L)         = 8
sizeof(7UL)        = 8
sizeof(7LL)        = 8
sizeof(2147483648) = 8
sizeof(0x80000000) = 4

Read the last two lines slowly. 2147483648 is one more than the largest 32-bit int. As a decimal constant it may not become unsigned, so it skips to long (8 bytes). 0x80000000 is the same value, but as a hex constant it is allowed to try unsigned int first, and it fits there, so you get 4 bytes. Same number, two different types. This is exactly why mixed signed/unsigned expressions with hex literals behave differently from decimal ones.

Example 3: The leading-zero trap

Goal

See how a harmless-looking leading zero changes the value of a number.

Save this as octal_trap.c:

#include <stdio.h>

int main(void)
{
    int slot_a = 007;
    int slot_b = 010;
    int slot_c = 012;

    printf("007 -> %d\n", slot_a);
    printf("010 -> %d\n", slot_b);
    printf("012 -> %d\n", slot_c);
    printf("sum of the three = %d\n", slot_a + slot_b + slot_c);
    return 0;
}

Build and run:

$ gcc -Wall -Wextra -std=c11 -o octal_trap octal_trap.c
$ ./octal_trap
Interactive: Predict the output first, then click to check yourself.

Expected output:

007 -> 7
010 -> 8
012 -> 10
sum of the three = 25

Anyone padding numbers for neat alignment (007, 010, 012) accidentally writes octal. 010 is 8 and 012 is 10, so the sum is 7 + 8 + 10 = 25 and not what a column of “neat” decimals would suggest. Worse, 09 or 08 will not even compile, so the bug only hides when every digit happens to be below 8. Never pad decimal numbers with leading zeros in C source.

Example 4: Hex flags for LEDs

Goal

Use hex constants as bit masks, the way real firmware controls pins and status registers.

Save this as led_flags.c:

#include <stdio.h>

int main(void)
{
    unsigned int leds = 0x00;

    leds |= 0x01;               /* red on   */
    leds |= 0x04;               /* blue on  */
    printf("after set       : 0x%02X\n", leds);

    leds &= ~0x01u;             /* red off  */
    printf("after clear red : 0x%02X\n", leds);

    printf("green on? %s\n", (leds & 0x02) ? "yes" : "no");
    printf("blue  on? %s\n", (leds & 0x04) ? "yes" : "no");
    return 0;
}

Build and run:

$ gcc -Wall -Wextra -std=c11 -o led_flags led_flags.c
$ ./led_flags
Interactive: Predict the output first, then click to check yourself.

Expected output:

after set       : 0x05
after clear red : 0x04
green on? no
blue  on? yes
  • |= sets a bit, &= ~mask clears it, and & tests it.
  • Each constant has exactly one bit set (0x01, 0x02, 0x04), so flags never interfere with each other.
  • The u in ~0x01u keeps the whole expression unsigned, avoiding sign surprises when masks get wider.
  • On a real board the variable would be a memory-mapped register, and the same operations would switch real LEDs.

Common Mistakes and Troubleshooting

MistakeWhat happensFix
Zero-padding decimals (007, 010)Silently becomes octal, or fails on 8 and 9Never lead with 0 unless you want octal
1 << 31 on a 32-bit intSigned overflow, undefined behaviourWrite 1u << 31 or 1UL << 31
1000 * 60 * 60 * 24 * 365Overflows int before assignment, even into a long variableMake one operand long: 1000LL * 60 * 60 * 24 * 365 gives 31536000000
Comparing -1 < 1UEvaluates to 0 (false) because -1 converts to a huge unsigned valueKeep both sides the same signedness
Assuming long is 4 bytesBreaks on LP64 systemsUse <stdint.h> types and UINT32_C() style macros
Typing int x = 4294967296;GCC warns that the value changes from 4294967296 to 0Use a wider type or fix the number

Best Practices, Performance and Security

  • Hex for bit patterns, decimal for counts, octal only for Unix modes. Matching the base to the meaning makes code self-explanatory.
  • Name your constants. 0x40021000 in ten places is a bug waiting to happen; Lecture 4 shows how symbolic constants fix that.
  • Suffix on purpose. Use U on masks and UL or ULL when the value must be wide.
  • Performance: expressions made only of constants, like 60 * 60 * 24, are folded by the compiler at build time and cost nothing at run time, so favour readable arithmetic over pre-computed magic numbers.
  • Security: signed integer overflow is undefined behaviour, and size calculations such as count * 4 that wrap around are a classic source of buffer overflows. Do size maths in size_t with an unsigned or wide constant.

Quiz: Integer Constants in C

Try each question on paper first. Click a question to reveal the answer.

Q1. Which of these are valid integer constants: 250, 0372, 0xFA, 0xfaU, 1,000, 08?

Answer: Four are valid: 250, 0372, 0xFA and 0xfaU. All four have the value 250 (0372 is 3×64 + 7×8 + 2). 1,000 is not one constant (it is 1, a comma, then 000), and 08 has an invalid octal digit.

Q2. What is the value of 011 + 0x11 + 11?

Answer: 37. That is 9 (octal 011) + 17 (hex 0x11) + 11 (decimal).

Q3. What does printf("%d", 010 + 10); print?

Answer: 18, because 010 is octal and equals 8.

Q4. What is the type of 5000000000 on 64-bit Linux, and on a 32-bit system?

Answer: long (8 bytes) on LP64. On a 32-bit system where long is 4 bytes it becomes long long. Both can hold the value.

Q5. Does -1 < 1U evaluate to true or false?

Answer: False (0). The usual arithmetic conversions turn -1 into an unsigned int, which is 4294967295, and that is not less than 1.

Q6. Write the largest 32-bit unsigned value as a hex constant.

Answer: 0xFFFFFFFF, which is 4294967295. On a platform with 32-bit int it has type unsigned int.

Q7. Which line fails to compile: int a = 0xAG;, int b = 0x;, int c = 00;, int d = 07;?

Answer: The first two. G is not a hex digit, and 0x with no digits is an invalid suffix error. 00 and 07 are valid octal.

Q8. Why do sizeof(0x80000000) and sizeof(2147483648) differ on LP64?

Answer: Hex and octal constants may be typed as unsigned int, so 0x80000000 fits into 4 bytes. A decimal constant never becomes unsigned and must jump to long, giving 8 bytes.

Interview Questions: Integer Constants in C

These come up in embedded and product-company interviews. Answer out loud, then click.

Q1. Is 0 an octal or a decimal constant?

Answer: Per the C grammar, a lone 0 is formally an octal constant. The value is the same either way, which makes this a trivia question that tests whether you know leading zero means octal.

Q2. Why add UL to a literal in embedded code?

Answer: To control width and signedness. On 16-bit targets 40000 will not fit int, and 1 << 31 is undefined for a 32-bit signed int. 1UL << 31 is well defined on every platform.

Q3. Is there any difference between writing 255 and 0xFF?

Answer: None at compile time. Both are decimal-or-hex spellings of the same value and both have type int. Only readability differs, and hex wins for masks.

Q4. What happens with int x = 4294967296; on a 64-bit machine?

Answer: The constant is a long. Converting it to int does not fit, so the value is implementation-defined; GCC warns that it changes to 0.

Q5. How do you write a binary constant portably?

Answer: In C23 use 0b101. Before that, 0b is only a compiler extension, so the portable choice is hex, where each digit is four bits.

Q6. A colleague writes int mode = 0644; for open(). Is it a bug?

Answer: No, it is correct and intentional: the leading zero makes it octal, and 0644 means owner read/write, group and others read-only. Replacing it with decimal 644 would produce the wrong permission bits.

Summary and Key Takeaways

  • An integer constant is a whole number with at least one digit, no spaces or commas, and no sign of its own.
  • Decimal never starts with 0, octal starts with 0 and uses 0-7, hexadecimal starts with 0x and uses 0-9 and a-f.
  • Without a suffix the compiler picks the first fitting type from a list, and octal or hex lists include unsigned types.
  • Use U, L, UL, LL and ULL deliberately; write the L in upper case.
  • Hex is for bit masks, octal is for Unix permissions, decimal is for everything else.
  • Sizes of int and long depend on the platform, so never assume them in portable code.

Conclusion

Integer constants in C are small, but they sit at the exact point where text becomes machine values, so every misunderstanding here turns into a hard-to-find bug later: a wrong permission, a masked-off flag, an overflowing timer. You now know how to read any literal, which base it uses, and which type the compiler gives it, and you have verified the surprising cases yourself by running code.

Next we move from whole numbers to real numbers, where exponent notation and precision introduce a different set of traps. Keep practising by changing the examples: add a 0b constant, try %lu with 7UL, or write your own mask table for a GPIO port.

Frequently Asked Questions

1. Is this free embedded systems course really free?

Answer: Yes. Every lecture, example and quiz on EmbeddedPathashala is free, with no signup wall. The C lectures prepare you for the Linux kernel and device driver lectures.

2. Do I need prior programming experience for this free embedded Linux course material?

Answer: Basic familiarity with any programming language is enough. We explain each term as it appears.

3. What is the difference between a constant and a variable in C?

Answer: A constant is a fixed value written in the source, like 42. A variable is a named storage location whose value can change. Lecture 5 covers variables.

4. Why does 010 print 8?

Answer: A leading zero marks an octal constant, and octal 10 equals 8 in decimal.

5. Which suffix should I use for 64-bit values?

Answer: Use ULL or LL for guaranteed 64-bit constants, or the UINT64_C() macro from stdint.h.

6. Can an integer constant be negative?

Answer: Technically no. The minus sign is the unary negation operator applied to a positive constant, which matters in corner cases like the most negative int.

7. Will this help with the Linux kernel development course?

Answer: Yes. Kernel headers define registers with hex constants, use octal modes, and rely on suffixes such as UL for 64-bit-safe masks.

Keep Building Your C Foundation

Finish the other lectures in this free embedded systems course, then continue to the free Linux device drivers course. Re-run each example and change one thing at a time; that is how these rules stick.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *