Learn C Programming

Lesson 1 of 7 · Constants, Qualifiers, Input and Output

Module 3 · Constants, Qualifiers, Input and Output

Constants and Literals: Values With a Name

FreeReading

In this lesson

  • Write an integer, floating, character and string literal with the type you actually meant.
  • Give a value a name with #define or const, and say which one to reach for first.
  • Spot a magic number, and read 010 as the eight it really is.

Maria's shop till charges 7.5 percent VAT. The number 7.5 is typed into her program in eleven places. The government moved the rate to 15, she edited ten of the eleven, and for three weeks one kind of receipt was quietly wrong.

Nothing in that story is a hard bug. It is a filing problem, and C has two tools for it. This lesson is about the values you write straight into your code. It is also about naming the important ones, so that there is only ever one of them.

A literal is a value with no name

Every bare value you type into a program is a literal. The 42 in int marks = 42; is an integer literal. The "Progsity" in a printf is a string literal.

A literal has a type, even though you never wrote one down. The compiler picks it from how the literal is written, and that choice is not always the one you expected.

The literal families, and the type each one gets

42            int
42U           unsigned int              U or u
42L           long                      L or l
42LL          long long                 LL or ll
42ULL         unsigned long long        the two suffixes together

3.14          double                    this is the default for decimals
3.14f         float                     f or F
3.14L         long double               L or l
1.5e3         double, the value 1500.0  e means times ten to the power

'A'           int, holding 65           one character in single quotes
'\n'          int, holding 10           an escape sequence is one character
"Progsity"    an array of char          double quotes, a different thing

0x1F          int, the value 31         base 16, the 0x prefix
037           int, the value 31         base 8, a leading zero
0b11111       int, the value 31         base 2, C23 or a compiler extension
  • The suffix is part of the literal, not part of the variable it is going into.
  • 3.14 is a double. Writing it into a float variable narrows it, which is fine and costs you digits.
  • A character constant is an int in C, not a char. Module 2 lesson 4 explained why it is a number at all.

So the shortest useful rule is: whole number means int, decimal means double, and a suffix is how you say otherwise.

#include <stdio.h>

int main(void)
{
    printf("int literal    : %d\n", 42);
    printf("long long      : %lld\n", 42LL);
    printf("unsigned       : %u\n", 42U);
    printf("double literal : %.2f\n", 3.14);
    printf("float literal  : %.2f\n", 3.14f);
    printf("char literal   : %c and %d\n", 'A', 'A');
    printf("string literal : %s\n", "Progsity");
    return 0;
}
int literal    : 42
long long      : 42
unsigned       : 42
double literal : 3.14
float literal  : 3.14
char literal   : A and 65
string literal : Progsity

Seven literals, seven specifiers, one program. Notice the sixth line: one literal, printed twice, once as a shape and once as the number underneath it.

A leading zero means base eight

C lets you write the same number in four ways. Base 10 is the plain one. Base 16 starts with 0x, base 8 starts with a single 0, and base 2 starts with 0b.

Three of those are harmless. The fourth is the oldest trap in the language, because a leading zero is something people add for neatness.

#include <stdio.h>

int main(void)
{
    int plain = 31;
    int hex = 0x1F;
    int octal = 037;
    int looks_like_ten = 010;

    printf("31   is %d\n", plain);
    printf("0x1F is %d\n", hex);
    printf("037  is %d\n", octal);
    printf("010  is %d\n", looks_like_ten);
    return 0;
}
31   is 31
0x1F is 31
037  is 31
010  is 8

The first three lines are the same value written three ways. The fourth is a number nobody typed on purpose.

A reader writing a list of product codes as 001, 007, 010 gets 1, 7 and 8. The compiler says nothing, at either command line, because all three are legal octal.

Base 2 is newer. 0b11111 has been a GCC extension for years and became standard C only in C23, so this track writes hex where a bit pattern matters.

So the rule is: a leading zero is never decoration in C, and if you want alignment, use a width in your printf instead.

The four literal families, and the four ways to write an integer one Every bare value you type is one of these literal integer floating character string 3.14 3.14f 1.5e3 'A' '7' '\n' "Progsity" 31 0x1F 037 base 10 base 16 base 8 All three integers above are the same value. Only the writing changed, and only base 8 is signalled by something a person would add for tidiness.
Figure 1. The literal families. The dangerous branch is the one whose marker is a single leading zero.

#define is find and replace, before the compiler looks

A #define is a preprocessor instruction. The preprocessor runs before the compiler, reads your file as text, and swaps every occurrence of the name for the replacement.

It has no type, it takes no semicolon, and it is gone by the time the compiler starts. That is its strength and every one of its problems.

#include <stdio.h>

#define VAT_PERCENT 15
#define SHOP_NAME "Maria General Store"

int main(void)
{
    long long bill_paisa = 36000;

    printf("%s\n", SHOP_NAME);
    printf("VAT is %d percent\n", VAT_PERCENT);
    printf("VAT on 360.00 is %lld paisa\n", bill_paisa * VAT_PERCENT / 100);
    return 0;
}
Maria General Store
VAT is 15 percent
VAT on 360.00 is 5400 paisa

By the time the compiler reads that program, the word VAT_PERCENT does not exist. It sees the digits 15 in both places, exactly as if Maria had typed them.

Because the swap is textual, a macro also has no scope. A #define at the top of a file is in force to the bottom of it. It will replace the name inside a function, inside a declaration, and anywhere else the name appears.

const is a variable the compiler refuses to let you change

A const is an ordinary variable with one extra word. It has a type, it lives in memory, and it obeys the scope it was declared in. Assigning to it is an error the compiler reports.

#include <stdio.h>

int main(void)
{
    const double PI = 3.14159265358979;
    const int SEATS_PER_ROW = 8;

    double radius = 2.5;

    printf("area of the mat  : %.2f\n", PI * radius * radius);
    printf("seats in 12 rows : %d\n", SEATS_PER_ROW * 12);
    return 0;
}
area of the mat  : 19.63
seats in 12 rows : 96

Both constants are used and neither is changed. Add the line PI = 3.0; anywhere below them and the program stops building, which is the whole service const performs.

The exact message, and the four other things the compiler starts refusing, are the next lesson. Here the point is narrower: const is a variable with a promise attached, and a #define is not a variable at all.

const or #define, and which one first

Reach for const first. Reach for #define when you need something a variable cannot be.

Questionconst#define
Who handles itthe compilerthe preprocessor, before the compiler
Does it have a typeyesno, it is text
Is it type checkedyesnothing to check
Can it be scoped to a blockyesno, file wide from that line
Visible in a debuggeryes, by nameno, already replaced
Usable as an array sizenot as a true constant, see belowyes
Takes a semicolonyesno, and one will be swapped in too

The array size row is the one real gap. In C, a const int is a variable whose value you promised not to change, and it is not a constant expression.

Writing int marks[limit]; with a const int limit compiles under C17, but you have made a variable length array rather than a fixed one. Module 9 says what that means and when it matters.

In C++ the same line is a plain fixed array, which is why advice copied from a C++ page can be quietly wrong here.

enum, in one paragraph

There is a third way to name numbers, and it is the right one for a related set. An enum gives readable names to a run of whole numbers, starting at 0 and counting up unless you say otherwise.

#include <stdio.h>

int main(void)
{
    enum status { pending, running, passed, failed };
    enum status verdict = passed;

    printf("verdict = %d\n", verdict);
    return 0;
}
verdict = 2

Unlike a const int, an enum name really is a constant expression, so it can size an array. Module 12 builds on this properly alongside struct, and until then treat it as a preview.

A magic number is a number nobody can explain

A magic number is a bare literal in the middle of a calculation whose meaning lives only in the author's head. Six months later the author is a different person.

Here is Maria's till as she first wrote it, and then rewritten with names. The two programs print the same bytes.

Example 1: the till with magic numbers

Every number here is correct. Try telling somebody what the 75 and the 1000 are without reading the rest of the line.

#include <stdio.h>

int main(void)
{
    long long rice = 12000;
    long long oil = 45000;

    printf("Rice  : %lld\n", rice + rice * 75 / 1000);
    printf("Oil   : %lld\n", oil + oil * 75 / 1000);
    printf("Total : %lld\n", rice + oil + (rice + oil) * 75 / 1000);
    return 0;
}
Rice  : 12900
Oil   : 48375
Total : 61275

The rate appears three times. A change to it is three edits, and the compiler cannot tell you when you have done two.

Run in Compiler
Example 2: the same till, with the numbers named

Two declarations, and the rate now exists in exactly one place.

#include <stdio.h>

int main(void)
{
    const long long VAT_PER_MILLE = 75;
    const long long PER_MILLE = 1000;

    long long rice = 12000;
    long long oil = 45000;
    long long both = rice + oil;

    printf("Rice  : %lld\n", rice + rice * VAT_PER_MILLE / PER_MILLE);
    printf("Oil   : %lld\n", oil + oil * VAT_PER_MILLE / PER_MILLE);
    printf("Total : %lld\n", both + both * VAT_PER_MILLE / PER_MILLE);
    return 0;
}
Rice  : 12900
Oil   : 48375
Total : 61275

Byte for byte the same output. The machine gained nothing and the next reader gained everything.

Per mille rather than per cent, because 7.5 percent is not a whole number and this till keeps everything in whole units. Module 2 lesson 3 is why.

Run in Compiler
Example 3: both tools in one program, the one you would actually write

A #define for the thing that is never a number, a const for everything that is.

#include <stdio.h>

#define SHOP_NAME "Maria General Store"

int main(void)
{
    const long long VAT_PER_MILLE = 75;
    const long long PER_MILLE = 1000;
    const long long PAISA_PER_TAKA = 100;

    long long bill = 57000;
    long long vat = bill * VAT_PER_MILLE / PER_MILLE;
    long long total = bill + vat;

    printf("%s\n", SHOP_NAME);
    printf("bill  : %lld.%02lld\n", bill / PAISA_PER_TAKA, bill % PAISA_PER_TAKA);
    printf("vat   : %lld.%02lld\n", vat / PAISA_PER_TAKA, vat % PAISA_PER_TAKA);
    printf("total : %lld.%02lld\n", total / PAISA_PER_TAKA, total % PAISA_PER_TAKA);
    return 0;
}
Maria General Store
bill  : 570.00
vat   : 42.75
total : 612.75

The % in those last three lines is the remainder operator, and Module 4 introduces it properly. It is here because a receipt is the natural home for it, and Module 4 is two lessons away.

Run in Compiler

Where this is used

  • The standard library you are already using. BUFSIZ, EOF and NULL are macros in <stdio.h>. When you write EOF in Module 3 lesson 5, the preprocessor swaps in -1 before the compiler sees it.
  • File permissions on Linux. chmod 0755 is an octal literal, and the C call behind it takes the same number. The leading zero there is deliberate and load bearing, which is exactly why C reads it that way.
  • Colours in a graphics program. Every game engine and image tool writes a colour as 0xFF8800, because each pair of hex digits is one byte of red, green or blue. Decimal would hide that structure.
  • A payroll batch job. Tax bands, the round-off unit and the pay period sit in named constants at the top of one file. The year's rule change is then one edit a reviewer can see.

Common mistakes

1. Padding a number with a zero for tidiness.

int product_code = 010;
printf("%d\n", product_code);

No message at either command line, and it prints 8. Every digit is legal octal, so there is nothing for the compiler to object to. If you want a code to print as 010, that is a width and a zero flag in the printf. Lesson 3 of this module is where you get them.

2. Putting a semicolon after a #define.

#define PI 3.14159;

double area = PI * 2.0 * 2.0;

The Playground's GCC 12 says error: invalid type argument of unary '*' (have 'double'), and the line it points at is the one that uses PI, not the one that defined it. The semicolon was swapped in too, so the compiler read 3.14159; * 2.0. A macro is text, and every character of the text goes in.

3. Leaving the parentheses off a macro parameter.

#define SQUARE(x) x * x

printf("%d\n", SQUARE(2 + 3));

No message, and it prints 11. The text swap gives 2 + 3 * 2 + 3, and multiplication goes first. Wrap the whole body and every parameter: #define SQUARE(x) ((x) * (x)). A const cannot have this problem, which is one more reason to prefer it.

4. Reusing a #define name as a variable.

#define MAX_STUDENTS 60

int main(void)
{
    int MAX_STUDENTS = 30;
    return 0;
}

GCC 12 says error: expected identifier or '(' before numeric constant and adds note: in expansion of macro 'MAX_STUDENTS'. It read int 60 = 30;. A macro has no scope, so it reaches inside your function and replaces the name there too. This is why macro names are conventionally shouted in capitals.

Brain teaser

Bob is writing the lesson durations for a timetable, padded to two digits so the list looks neat.

#include <stdio.h>

int main(void)
{
    int five = 05;
    int six = 06;
    int seven = 07;

    printf("%d %d %d\n", five, six, seven);
    return 0;
}

This builds and prints 5 6 7, so Bob adds int eight = 08; and the program stops building. Say what the compiler told him, and why the three lines above it were fine.

Then the harder half. He gives up on padding and deletes every leading zero, so 05 becomes 5 and so on up to 12. Which of the twelve printed values change, and which do not?

A leading zero is not decoration, it is a base. Then ask which digits that base is even allowed to contain.

Exercise 1: named-receiptMedium

Maria's till, rewritten so that the VAT rate lives in exactly one line. Read a quantity and a unit price in paisa, and print the subtotal, the VAT and the total.

Input. One line with two integers: the quantity, then the unit price in paisa.

Output. Three lines, each a word then an amount as whole taka, a full stop, and two digits of paisa.

Constraints. 1 <= quantity <= 100000, 1 <= unit price <= 10000000, VAT is 15 percent with the fraction of a paisa dropped. No double.

Sample. Input 3 12000 gives Subtotal 360.00, VAT 54.00, Total 414.00.

#include <stdio.h>

int main(void)
{
    const long long VAT_PERCENT = 15;
    const long long PAISA_PER_TAKA = 100;
    const long long PERCENT_WHOLE = 100;

    long long quantity = 0;
    long long unit_paisa = 0;
    scanf("%lld %lld", &quantity, &unit_paisa);

    /* Three lines. %02lld is what makes five paisa print as 05. */

    return 0;
}

Graded as named-receipt. The two hundreds mean different things, so they are two constants and not one.

Run in Compiler
Exercise 2: base-decoderEasy

Amara's config file writes a permission in base 8 and a colour in base 16. Print both in decimal.

Input. One line with a hexadecimal number then an octal number, with no prefixes.

Output. One line with both in decimal, one space between them.

Constraints. Hex 0 to FFFFFF in either case, octal 0 to 7777777.

Sample. Input 1F 755 gives 31 493.

#include <stdio.h>

int main(void)
{
    unsigned int from_hex = 0;
    unsigned int from_octal = 0;
    scanf("%x %o", &from_hex, &from_octal);

    /* One printf. Both boxes already hold ordinary numbers. */

    return 0;
}

Graded as base-decoder. Lesson 4 of this module explains the scanf line; here it is written for you.

Run in Compiler
Exercise 3: the circle, twiceHard

Zara is cutting circular mats and needs the area and the edge length of each one, from one radius and one named value of pi.

Input. One line with one decimal number, the radius.

Output. Two lines: the area, then the circumference, each to exactly two decimal places.

Constraints. Use const double PI = 3.14159265358979; and write the digits once.

Sample. Input 2.5 gives 19.63 then 15.71.

#include <stdio.h>

int main(void)
{
    const double PI = 3.14159265358979;

    double radius = 0.0;
    scanf("%lf", &radius);

    /* Two printf calls, both using PI, neither repeating its digits. */

    return 0;
}

Not graded in this module. Then do it a second time with #define PI 3.14159265358979 and check that the output is identical. The difference between the two is about people, not about arithmetic.

Run in Compiler

Common doubts

  • Why are constant names written in capitals?

    Habit, and a useful one. It started with macros, where a name reaching into the wrong place is a real hazard. It spread to const because a reader then knows at a glance that the value never moves.

  • Does a const cost memory that a #define does not?

    In principle yes, in practice almost never. Every compiler at any optimisation level puts a small const straight into the instruction that uses it, exactly as a macro would.

  • Is 3.14f less accurate than 3.14?

    Yes. The f suffix makes it a float, which carries about 7 digits against a double's 15. Module 2 lesson 3 showed what that costs, and the answer there was to use double unless you have a reason.

  • Can I write const on something I do change later?

    No, and that is the point of it. The compiler refuses the assignment, which is the next lesson.

  • Are #define and const the only ways to name a value?

    No. enum is the third, and it is the best of the three for a set of related whole numbers. Module 12 gives it a lesson of its own.

Key takeaways

  • Every bare value is a literal, and the way it is written decides its type.
  • A suffix says you meant something other than int or double.
  • A leading zero means base 8, silently, and 010 is 8.
  • #define is a text swap done before the compiler, with no type and no scope.
  • const is a typed, scoped variable the compiler will not let you assign to; reach for it first.
  • A magic number is a value whose meaning nobody can recover, and naming it costs nothing at run time.

Next you find out what the compiler actually refuses once you have written const, and meet the one qualifier that asks it to stop being clever.

End of lesson 1

Mark it done, and your progress moves with you.

Next: const and volatile: Promises You Make to the Compiler