Numbers That Never Change Still Deserve Names

Every program is full of fixed values: the 100.0 in the interest formula, the mathematical pi, the size of something. C gives you three distinct tools here: literal constants for writing values, const for variables that must not change, and #define for naming a value before compilation even starts. Exams love to ask the difference; so does real code review.

Integer Constants, in Three Bases

An integer constant is normally decimal: 42, -7, 100000. Two prefixes change the base, and both are exam regulars:

  • A leading 0 means octal (base 8): 010 is eight, not ten.
  • A leading 0x or 0X means hexadecimal (base 16): 0x1F is thirty-one.

The octal rule is a genuine trap. Write int code = 052; intending fifty-two, and you have written forty-two. Never left-pad integer constants with zeros.

Suffixes select a type when it matters: 100L is a long constant, 100U unsigned, 100UL both. Without a suffix, a small integer constant is an int.

All three lines print 42, one value written in three notations. printf can also output in the other bases: %o prints octal, %x hexadecimal.

Floating and Character Constants

A floating constant contains a decimal point (3.14159, 0.5), and may use exponent notation: 2.5e3 is 2500.0, 1e-2 is 0.01. Its type is double unless a suffix says otherwise: f makes it a float (3.14f) and l a long double (3.14l). Write those suffixes as capitals, 3.14F and 3.14L, for the same reason the integer suffix is written 100L: a lowercase l and the digit 1 are indistinguishable in too many fonts.

A character constant is one character in single quotes: 'A', '7', '%'. Escape sequences count as one character: '\n', '\t', and the one that will matter enormously in the strings chapter, '\0', the character whose code is zero. Remember from last lesson that these are small integers: 'A' is 65, '0' is 48, and '0' and 0 are different values. Double quotes make a string literal instead; 'A' and "A" are different things entirely, which the strings chapter unpacks.

The Escape Sequences in Full

Some characters cannot be written literally between quotes. A newline would end the line, a double quote would end the string, and several have no printable shape at all. C spells them with a backslash, and each of these two-character sequences is one character in the compiled program:

Sequence Character
\n newline
\t horizontal tab
\v vertical tab
\b backspace
\r carriage return
\f form feed
\a alert (the terminal bell)
\\ backslash
\' single quote
\" double quote
\? question mark
\0 the character whose code is zero

The whole table is standard exam material, but the working set is smaller than it looks. You will use \n constantly, \t for columns, and \0 throughout the strings chapter. \\ and \" exist because those two characters would otherwise be read as syntax rather than data. \' is only needed inside a character constant, where '\'' is the apostrophe, and \" only inside a string literal; each is harmless but redundant in the other. \? was invented to stop a ?? pair being mistaken for one of ANSI C's trigraph spellings, a problem no modern keyboard has. And \a, \b, \r, and \f are terminal control rather than text: what they do to your screen depends on the terminal, so no course example depends on them.

\0 is not a special rule but the shortest case of a general one: \ooo is a character given in octal and \xhh one given in hexadecimal, so '\101' and '\x41' are both 'A'.

tab:	after the tab
quote: " backslash: \ apostrophe: '
a literal percent sign: 100%

The last line is a different mechanism wearing similar clothes. \\ is resolved by the compiler, which puts one backslash character into the string; %% is resolved by printf at run time, which reads the format string looking for % and needs a way to be told "print one of these". A lone % in a format string is a bug rather than an escape, and the printf chapter is where that distinction earns its keep.

const: A Variable That Refuses Assignment

Qualifying a declaration with const makes assignment to it illegal after initialization:

const double rate = 7.5;
rate = 9.0;    /* compile error: assignment of read-only variable */

const documents intent and recruits the compiler to enforce it. Everything else about the variable is unchanged: it has an address, a size, a type, and it obeys the declarations-first rule.

volatile: A Value That Changes Behind Your Back

const is one of ANSI C's two type qualifiers. The other is volatile, and it makes almost the opposite promise:

volatile int sensorReading;

This tells the compiler that the object may be changed by something outside the flow of this program: a hardware register wired to a sensor, a location another device writes, a variable a signal handler touches. The consequence is a restriction on optimization. Ordinarily a compiler may keep a value in a register and reuse it, or delete a read whose result cannot have changed since the last one; for a volatile object it must perform every read the source asks for, in the order written.

What volatile does not do deserves saying plainly, because it attracts more folklore than any other qualifier. It is not atomicity and it is not a synchronization mechanism: it says nothing about whether a read and a write can overlap, only that reads and writes are really performed. Code that needs safety between concurrent tasks needs tools ANSI C does not contain.

Course programs need none of this, because nothing here talks to hardware. The reasons to meet it now are that you will see it in the keyword list and should not have to wonder, and that exam papers like to slip it into a list of storage classes to see whether you know it is a qualifier instead. Both qualifiers can apply at once:

const volatile int deviceStatus;

The program may not write it, and must re-read it every time. For a read-only hardware register that is exactly the right pair of statements.

#define: Naming Text Before Compilation

The preprocessor offers a different mechanism:

#define PI 3.14159

This is not a declaration; it is an instruction to the preprocessor: before compiling, replace every later token PI with the text 3.14159. No memory, no type, no semicolon. The convention, near-universal since the language began, is UPPER_CASE names for defined constants, which is why they are often called symbolic constants.

By the time the compiler runs, the printf line reads 3.14159 * radius * radius. The name PI no longer exists.

const or #define?

For a beginner's rule of thumb: prefer const for typed program values, and use #define where a name must exist before compilation or must be a true compile-time constant. In ANSI C that second category is real: a const variable is still a variable, so contexts demanding a constant expression, array sizes chief among them, when you reach them, accept #define PI but reject const double pi. The two tools also fail differently: a mistyped const gives a clear error at its declaration, while a mistyped #define produces errors at every use site, reported in code that looks innocent. Exam answers should mention the deepest difference: #define is textual substitution by the preprocessor; const is a typed variable the compiler protects.

Key Takeaways

  • Integer constants: decimal by default, 0 prefix means octal (a classic trap), 0x means hexadecimal; suffixes U and L select type.
  • Floating constants are double unless suffixed: F for float, L for long double; exponent form 2.5e3 is available.
  • Character constants are single-quoted single characters; 'A' is the integer 65; '0' is not 0; '\0' is the zero-code character.
  • Escape sequences are one character each: \n \t \v \b \r \f \a \\ \' \" \? \0, with \ooo and \xhh for any code you like. \\ is the compiler's doing; %% is printf's.
  • const declares a real, typed variable that rejects assignment after initialization.
  • volatile is the other type qualifier, not a storage class: it forbids the compiler from caching or eliminating reads, and promises nothing about atomicity.
  • #define NAME value is preprocessor text substitution: no type, no memory, UPPER_CASE by convention.
  • Prefer const for typed values; #define where a compile-time constant or pre-compilation name is required.