LABORATORY 02

Constants. Variables.

Duration: 2 hours Language: C / C++ Previous: Laboratory 1 PDF handout RO versiunea română

A constant is a fixed value written directly in the code; a variable is a name associated with a memory location whose content can change. The type chosen for each determines how much space it occupies, what values it can hold, and what operations can be applied to it.

1Lab objectives

  • Writing integer constants in bases 10, 8, and 16, and using the U and L suffixes
  • Using real, character, and string constants, including escape sequences
  • Declaring variables with the type suited to the range of values
  • Experimentally determining sizes with the sizeof operator
  • Telling scope apart from a variable's lifetime

2Constants

Literal constants are fixed values that cannot be changed during execution. Their type is determined automatically by the compiler, based on the value and the syntax used.

Integer constants

BaseWriting conventionExamples
Decimal (10)the first digit is not 023, -555L, 3276L
Octal (8)the first digit is 0; the digits 8 and 9 are illegal067, -067, 067u
Hexadecimal (16)prefix 0x or 0X0x1F, 0xFFu, 0xAB3L
The leading-zero trap int x = 010; does not mean ten, it means eight - the 0 prefix triggers interpretation in base 8. This is a frequent mistake when writing calendar dates or codes with insignificant leading zeros.

Default type determination

SituationAssigned typeExample
the value fits in the int rangeint15, 23
exceeds int, fits in longlong99999
exceeds longdouble98765432100

The default type can be forced with suffixes: U/u for unsigned, L/l for long, combined as UL.

Character constants and escape sequences

SequenceMeaningASCII code
'\n'newline10
'\t'horizontal tab9
'\r'carriage return13
'\0'null character - marks the end of a string0
'\\'backslash92
'\''apostrophe39
Character vs. string'A' is a character constant and occupies 1 byte. "A" is a string and occupies 2 bytes: the letter plus the '\0' terminator. Confusing the two produces errors that are hard to track down.

3Fundamental data types

unsigned char: 8 bits, range 0...255254255012+1+1after 255 comes 0, not 256the overflow produces no error at compile time or at run timethe value wraps back to the other end of the range
Fig. - Range overflow on an unsigned integer type. Arithmetic is done modulo 2^n, so incrementing the maximum value silently brings the variable back to zero.
TypeTypical sizeRange of valuesprintf specifier
char1 byte−128 ... 127%c / %d
unsigned char1 byte0 ... 255%u
short2 bytes−32,768 ... 32,767%hd
int4 bytes−2,147,483,648 ... 2,147,483,647%d
unsigned int4 bytes0 ... 4,294,967,295%u
long long8 bytes±9.2 · 10¹⁸%lld
float4 bytes±3.4 · 10³⁸, ~7 significant digits%f
double8 bytes±1.7 · 10³⁰⁸, ~15 significant digits%lf
bool (C++)1 bytetrue / false-
Sizes are not guaranteedThe standard only requires ordering relations (char ≤ short ≤ int ≤ long). On an 8-bit microcontroller, int is 2 bytes; on an ordinary PC, 4. That is why you always check with sizeof.

4Variables

Declaring a variable reserves space in memory and associates it with a name and a type:

syntax
type name;                 // simple declaration, undefined value
type name = value;         // declaration with initialization
type n1, n2, n3;           // several variables of the same type
const type name = value;   // a value that can no longer be changed
Uninitialized variablesA local variable declared without initialization contains "garbage" - whatever was previously at that memory address. Reading it before assignment produces unpredictable results, which differ from run to run.

5Scope and lifetime

ClassWhere it is visibleHow long it livesDefault initial value
auto (local)in the block where it is declareduntil the block exitsundefined
local staticin the block where it is declaredthe entire program execution0
globalthroughout the file, after the declarationthe entire program execution0
externin other source filesthe entire program execution0
registerin the block where it is declareduntil the block exitsundefined
The key differenceA local static variable is only visible in its own function, but is not destroyed on exit: on the next call it finds its value again. This is the standard mechanism for counters that must remember their state between calls.

6Execution simulator

Watch step by step how an ordinary local variable behaves compared to one declared static, over three successive calls of the same function.

Local variable vs. static variable

7Source code

sizes.c - checking the types
#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("Type           Bytes   Min                Max\n");
    printf("-------------------------------------------------------\n");
    printf("char           %2zu     %-18d %d\n", sizeof(char),  CHAR_MIN,  CHAR_MAX);
    printf("short          %2zu     %-18d %d\n", sizeof(short), SHRT_MIN,  SHRT_MAX);
    printf("int            %2zu     %-18d %d\n", sizeof(int),   INT_MIN,   INT_MAX);
    printf("long           %2zu     %-18ld %ld\n", sizeof(long), LONG_MIN, LONG_MAX);
    printf("float          %2zu\n", sizeof(float));
    printf("double         %2zu\n", sizeof(double));

    return 0;
}
constants.c - number bases
#include <stdio.h>

int main(void)
{
    int decimal     = 100;      // base 10
    int octal       = 0144;     // base 8  -> also 100
    int hexadecimal = 0x64;     // base 16 -> also 100

    printf("%d %d %d\n", decimal, octal, hexadecimal);   // 100 100 100

    // the same value shown in different bases
    printf("decimal: %d   octal: %o   hex: %X\n", 255, 255, 255);

    // the leading-zero trap
    int wrong = 010;
    printf("010 means %d, not 10!\n", wrong);            // 8

    // character vs string
    char c = 'A';
    char s[] = "A";
    printf("sizeof('A') in C = %zu, sizeof(\"A\") = %zu\n", sizeof(c), sizeof(s));

    return 0;
}
overflow.c - what happens on overflow
#include <stdio.h>

int main(void)
{
    short small = 32767;        // the maximum value for short
    printf("before: %d\n", small);
    small = small + 1;          // overflow!
    printf("after +1: %d\n", small);   // -32768, drops to the minimum value

    unsigned char u = 255;
    u = u + 1;
    printf("unsigned char 255+1 = %u\n", u);   // 0, wraps back to the start

    return 0;
}
Why this happensOverflow produces no error: the value "wraps around" to the other end of the range, because the available bits run out. It is the programmer's responsibility to choose a type large enough.

8Code workshop

The sizes of types and their overflow behavior are not learned from tables, but by seeing them. Run the programs below and change the values to see exactly where each type breaks.

How much space each type takes and what values fit in it
#include <stdio.h>

int main(void)
{
    printf("char        %d byte(s)\n", (int)sizeof(char));
    printf("short       %d byte(s)\n", (int)sizeof(short));
    printf("int         %d byte(s)\n", (int)sizeof(int));
    printf("long long   %d byte(s)\n", (int)sizeof(long long));
    printf("float       %d byte(s)\n", (int)sizeof(float));
    printf("double      %d byte(s)\n", (int)sizeof(double));

    printf("\nConstants written in different bases:\n");
    printf("  67 decimal = %d\n", 67);
    printf("  0103 octal = %d\n", 0103);
    printf("  0x43 hex   = %d\n", 0x43);
    printf("  'C' is     = %d\n", 'C');
    return 0;
}
Try thisAll four constants at the end are the same value, written four different ways. The compiler does not remember the base - it only remembers the number.
Range overflow - watch the value "wrap around"
#include <stdio.h>

int main(void)
{
    short s = 32767;          /* the maximum value for short */
    printf("s        = %d\n", s);
    s = s + 1;
    printf("s + 1    = %d   <- became negative!\n", s);

    unsigned char u = 255;
    printf("\nu        = %d\n", u);
    u = u + 1;
    printf("u + 1    = %d   <- wrapped back to zero\n", u);

    char c = 200;             /* 200 does not fit in a signed char */
    printf("\nchar c   = %d\n", c);
    return 0;
}
Try thisOverflow produces no error at run time: the value wraps back to the other end of the range. That is why choosing the right type is a decision, not a formality.

9Work tasks

  • Run the size-checking program and note down the values obtained on the lab computer.
  • Write the value 200 in bases 10, 8, and 16 and check that the printed result is identical.
  • Declare a short variable with the maximum value and add 1; explain the result.
  • Compare sizeof('A') in C with sizeof('A') in C++ - the results differ, explain why.
  • Write a function with a static counter and call it five times; print the value on each call.
  • Declare an uninitialized local variable, print it, and run the program three times - see whether the value changes.
  • Use all the escape sequences from the table in a single printf and observe the effect.

10Extended application

ExtensionWrite a program that determines experimentally, without using <limits.h>, the maximum value of the int type on your computer: start from 1 and double the value until it becomes negative, then adjust. Compare the result with INT_MAX. Repeat for unsigned int.

11Review questions

12Resources