Notes on the printf specifiers in C

code type format
d int decimal (base ten) number
o int octal number (no leading ‘0’ supplied in printf)
x or X int hexadecimal number (no leading ‘0x’ supplied in printf; accepted if present in scanf) (for printf, ‘X’ makes it use upper case for the digits ABCDEF)
ld long decimal number (‘l’ can also be applied to any of the above to change the type from ‘int’ to ‘long’)
u unsigned decimal number
lu unsigned long decimal number
c char [footnote] single character
s char pointer string
f float [footnote] number with six digits of precision
g float [footnote] number with up to six digits of precision
e float [footnote] number with up to six digits of precision, scientific notation
lf double [footnote] number with six digits of precision
lg double [footnote] number with up to six digits of precision
le double [footnote] number with up to six digits of precision, scientific notation

Footnote: In printf(), the rvalue type promotions are expected. Thus %c
actually corresponds to a parameter of type int and %f and %g actually
correspond to parameters of type double. Thus in printf() there is no
difference between %f and %lf, or between %g and %lg.
However, in scanf() what is passed is a pointer to the variable so no
rvalue type promotions occur or are expected.
Thus %f and %lf are quite different in scanf, but the same in printf.

From http://www.cdf.toronto.edu/~ajr/209/notes/printf.html

Notes on character strings in C

Synopsis

To initilise a string of characters, use double quotation marks.

char myString[] = "Hello!";

This declaration is the equivalent of the statement

char myString[] = {'H', 'e', 'l', 'l', 'o', '!', '\0'};

\0 is the NULL character in C, and it is used to denote the termination of an array of chars.

To display a character string inside the printf function use the %s wildcard

printf("Value of myString is: %s \n", myString);

Why double quotation marks only?

In C (and in C++) single quotes identify a single character (char), while double quotes create a string literal. ‘a’ is a single a character literal (char), while “a” is a string literal containing an ‘a’ and a null terminator (effectively a 2 char array).

An example of string concatenation in C:

#include <stdio.h>

void concat(char result[],
            char string1[],
            char string2[])
{
    // Add string 1.
    char string1Char = string1[0];
    int i = 0;
    
    while (string1Char != '\0') {
        
        result[i] = string1[i];
        
        i++;
        string1Char = string1[i];
    }
    
    // Add string 2.
    char string2Char = string2[0];
    int u = 0;
    int j = i;
    
    while (string2Char != '\0') {
        
        result[j] = string2[u];
        
        u++;
        j++;
        string2Char = string2[u];
    }
    
    // Add NULL char.
    result[j] = '\0';
}

int main(int argc, const char * argv[])
{
    char firstName[] = "Jon";
    char lastName[] = "Matthews";
    char result[12];
    
    concat(result, firstName, lastName);
    
    printf("%s\n", result);
    
    return 0;
}

Notes on the const qualifier in C

Synopsis

const <variable declaration>

Variables that are defined as constant are expected to not have their rvalue modified. If you try and modify a constant’s rvalue during program execution the compiler might issue a warning (although it is not required to do so). One of the motivations for defining a variable as constant (in addition to added readability) is that it allows the compiler to place the constant variables into read-only memory.

Examples:

const int theNumberFive = 5;
const char myName[] = "Jon";
const double theNumberSix = 6.;

int main(int argc, const char * argv[])
{
    printf("A number is: %i\n", theNumberFive);
    printf("My name is: %s\n", myName);
    printf("Another number is: %f\n", theNumberSix);
    return 0;
}

Notes on macros in C

Synopsis

#define NAME expression

It’s standard convention in C that #define statements are defined with UPPERCASE names, although this is not required.

The preprocessor essentially does a “find and replace” with all #define statements substituting their key with it’s expression.

Therefore

#define MY_NAME "Jon"
printf(MY_NAME);

Is seen by the compiler as

#define MY_NAME "Jon"
printf("Jon");

This is why #define statements do not have terminating semicolons. See an example of why below:

#define MY_NAME "Jon";
printf(MY_NAME);

Would be interpreted by the compiler as

#define MY_NAME "Jon";
printf("Jon";); 

It’s common for #define statements to be defined at the start of a program, like so:

#include <stdio.h>

#define MY_NAME "Jon"
#define NUMBER_THREE 3

int main(int argc, const char * argv[])
{
    //...
}

However, this is not required – they can be defined anywhere within a program, so long as they’re defined before they’re referenced.

Scope

#define statements are always global, regardless of whether they have been declared inside or outside of a function.

Notes on the array data type in C

Synopsis

[data type] arrayName[array size] = {default value}

Initialisation

To create an array that can hold up to 10 (integer value) key value pairs (with the keys 0 through 9):

int myNumbers[10];

Assigning values:

int myNumbers[0] = 5;
myNumbers[1] = -3;
...
myNumbers[9] = 7;

Or you can use the following braces syntax for the declaration and value assignment on a single line.

int myNumbers[10] = {5, -3, 2, 7, -6, 1, 6, -3, 2, 7};

If you wish to set the value of specific keys only using braces, you can do so like this:

int myNumbers[10] = { [3] = 7, [8] = 2, [9] = 7 };

This sets the values of entries 3, 8 and 9.

In C unspecified values are usually set to zero (but aren’t always if there happens to be junk in that memory location). For example:

int otherNumbers[10];

otherNumbers[0] = 1;
otherNumbers[1] = 2;

might not necessarily mean that keys 2 through 9 have a value of zero (0). So it’s good practice when creating an array to explicitly set the values you want. With C++ there is a helper method to set the default value of an entire array. However, there isn’t such a luxury in C.

In C you can omit the size of the array (also known as the array dimension). E.g:

int numbers[] = {1, 2, 3, 4, 5};

This approach is fine so long as you initialise every element in the array at the point that the array is defined. If this is not the case, you must explicitly define the dimension like so:

int numbers[5];

Below are examples of a floating point arrays:

float realNums[3];
realNums[0] = 0.2;
realNums[1] = -4.;
realNums[2] = 6.7219;

double realNums2[3];
realNums2[0] = 0.2;
realNums2[1] = -4.;
realNums2[2] = 6.7219;

Below is an example of a char array. Single char arrays must use single quotes within the value assignments:

char myName[12] = {'J', 'o', 'n', ' ', 'M', 'a', 't', 't', 'h', 'e', 'w', 's'};
    
for (int i = 0; i < 12; i++) {
    printf("%c", myName[i]);
}

Enumeration

short unsigned int i;
for (i=0; i<10; i++) {
    printf("%i = %i\n", i, myNumbers[i]);
}

Arrays in functions

When you pass an *entire* array to a function, any modifications to that array modify the original array. As in, the parameter's array declaration isn't a copy of the original, but a reference to it. See example below:

void doubleScores(float scoreArray[], unsigned short int arraySize)
{
    unsigned short int i;
    for (i = 0; i < arraySize; i++) {
        scoreArray[i] = (scoreArray[i] * 2);
    }
}

int main(int argc, const char * argv[])
{
    
    float scores[3] = {0.f};
    
    scores[0] = 7.f;
    scores[1] = 6.33;
    scores[2] = 1.27;
    
    unsigned short int i;
    
    for (i=0; i<3; i++) {
        printf("score %i is %f\n", i, scores[i]);
    }
    
    printf("----------------\n");
    doubleScores(scores, 3);
    
    for (i=0; i<3; i++) {
        printf("Doubled score %i is %f\n", i, scores[i]);
    }
    
    return 0;
}

Output:

score 0 is 7.000000
score 1 is 6.330000
score 2 is 1.270000
----------------
Doubled score 0 is 14.000000
Doubled score 1 is 12.660000
Doubled score 2 is 2.540000

However, parameterised individual array elements arr[x] are passed as copies, not references (just like normal C primitives int, float, char etc).

void doubleScore(float singleScore)
{
    singleScore = (singleScore * 2);
}

int main(int argc, const char * argv[])
{
    
    float scores[3] = {0.f};
    
    scores[0] = 7.f;
    scores[1] = 6.33;
    scores[2] = 1.27;
    
    printf("Score 2 is is %f\n", scores[2]);
    
    doubleScore(scores[2]);
    
    printf("Score 2 is is %f\n", scores[2]);
    
    return 0;
}

Output:

Score 2 is 1.270000
Score 2 is 1.270000

Multidimensional Arrays

int array2D[number_of_rows][number_of_columns];

Here is the initialisation of a multidimensional array with 2 rows and 3 columns:

int array2D[2][3] =
{
{ 2, 7, 1 }, // row 1 (3 columns in each)
{ 9, 4, 2 }  // row 2 (3 columns in each)
};

This array can be depicted like so:

- Column 1 Column 2 Column 3
Row 1 2 7 1
Row 2 9 4 2

Items are accessed like so:

- Column 1 Column 2 Column 3
Row 1 array2D[0][0] array2D[0][1] array2D[0][2]
Row 2 array2D[1][0] array2D[1][1] array2D[1][2]

Example of a three dimensional array:

int array3D[2][3][2] =
    {
        { { 2, 6 }, { 7, 3 }, { 1, 5 } },
        { { 9, 8 }, { 4, 4 }, { 2, 1 } }
    };

Notes on the char data type in C

Synopsis

Single character only, like ‘a’ or ‘0’ with the exception of escape characters such as ‘\n’, ‘\r’ or ‘\t’. Single character constants (chars) must use single quotation marks for their declaration.

char

char myChar     = 'a';
char myChar2    = ';';
char myChar3    = '0';
char myChar4    = '\n';
printf("Value of myChar is: %c \n", myChar);
printf("Value of myChar2 is: %c \n", myChar2);
printf("Value of myChar3 is: %c \n", myChar3);
printf("Value of myChar4 is: %c \n", myChar4);

Technically chars are integers. Just with a narrower range.

Usually the range will be 1 byte (8 bits). This is because 1 byte allows for 2^8 unique combinations (256). And there are 256 ASCII characters (http://www.ascii-code.com). Therefore each character can fit into a single byte. This means that there isn’t a need for an extra byte.

You can test the range like so:

printf("On this machine, char is stored in %lu bytes. (%lu bits wide).\n",
           sizeof(char),
           ((sizeof(char)) * 8));

You can also declare chars using their decimal, hexidecimal, octal or binary* equivalents.

The code example below represents the tilde constant (see the ASCII table @  http://www.ascii-code.com)

char charSymbol = '~';
printf("Value of charSymbol is: %c \n", charSymbol); // outputs ~

char charDec = 126;
printf("Value of charDec is: %c \n", charDec); // outputs ~

char charHex = 0x7E;
printf("Value of charHex is: %c \n", charHex); // outputs ~

char charOct = 0176;
printf("Value of charOct is: %c \n", charOct); // outputs ~

char charBin =  0b01111110;
printf("Value of charBin is: %c \n", charBin); // outputs ~

All of which are identical.

*Standard C does not support binary constants like 0b01111110; that’s a gcc extension.

You can also test a variable’s rvalue against the decimal, hex, oct or binary values.

char c;
    
printf("Enter a letter:\n");
scanf("%c", &c);
    
char tilde = 0x7E;
    
if (c == tilde){
    printf("you entered a tilde!\n");
}

Why single quotation marks only?

In C (and in C++) single quotes identify a single character (char), while double quotes create a [string literal](http://joncarlmatthews.com/c/notes/data%20type/2014/08/08/c-notes-data-types-string.html). ‘a’ is a single a character literal (char), while “a” is a string literal containing an ‘a’ and a null terminator (effectively a 2 char array).

Notes on the float and double data type in C

Synopsis

Values must contain a floating-point. You can omit digits before the decimal point or digits after the decimal point, but not both. Floats cannot be signed or unsigned. Meaning they are essentially signed by default.

[float|double] = <value>

float

float myFloat = 125.8f;
float myFloat2 = 3.0f;
float myFloat3 = -.001f;
printf("Value of myFloat is: %g \n", myFloat);
printf("Value of myFloat2 is: %g \n", myFloat2);
printf("Value of myFloat3 is: %g \n", myFloat3);

The reason you need to add the character f (or F) to the declaration is to explicitly tell the compiler to treat the variable as a float. As by default floating-point constants are taken as a double by the C compiler.

double

double myDouble = 125.8;
double myDouble2 = 3.;
double myDouble3 = -.001;
printf("Value of myDouble is: %g \n", myDouble);
printf("Value of myDouble2 is: %g \n", myDouble2);
printf("Value of myDouble3 is: %g \n", myDouble3);

Ranges

float and double cannot have range specifiers like long and short.

float is generally 4 bytes in size (32 bits wide).
double is generally 8 bytes in size (64 bits wide).

Notes on the int data type in C

Synopsis

Integers (signed and unsigned) only. No decimal places. No commas. If you want to gaurantee signed or unsigned you must explicitly define it.

[signed|unsigned] [range] = <value>

Signed integers

signed int myInt   = 12000;
signed int myInt2  = -999;
printf("Value of myInt is: %i", myInt);
printf("Value of myInt2 is: %i", myInt2);

For shorthand signed integers you can simply use int keyword.

int myInt   = 12000;
int myInt2  = -999;

Unsigned integers

unsigned int myInt3  = 12000;
printf("Value of myInt3 is: %u", myInt3);

For shorthand unsigned integers you can simply use unsigned keyword.

unsigned myInt3  = 12000;

Ranges

int is gauranteed to be at least 1 byte in size (8 bits wide). But is usually either 2 bytes (16 bits wide) or (as per most modern machines) 4 bytes (32 bits wide) in size

Test it:

printf("On this machine, int is stored in %lu bytes. (%lu bits wide).\n",
                sizeof(int),
                ((sizeof(int)) * 8));

short int is an integer value of reduced precision. For example, if the machine stores an int 32 bits wide, then short int will be 16 bits wide. A short int is guaranteed to never be wider than an int (although it may be the same).

Test it:

printf("On this machine, short is stored in %lu bytes. (%lu bits wide).\n",
                sizeof(short),
                ((sizeof(short)) * 8));

long int is an integer value of extended precision. Gauranteed to be at least 4 bytes in size (contain at least 32 bits of precision), and may or may not be larger than a standard int.

Test it:

printf("On this machine, long is stored in %lu bytes. (%lu bits wide).\n",
                sizeof(long),
                ((sizeof(long)) * 8));

long long int is an integer value of extra extended precision. Gauranteed to be at least 8 bytes in size (contain at least 64 bits of precision).

Test it:

printf("On this machine, long long is stored in %lu bytes. (%lu bits wide).\n",
                sizeof(long long),
                ((sizeof(long long)) * 8));

Ranges in real terms

8-bits wide (1 byte)

– Maximum signed range is from −128 to 127.
– Maximum unsigned range is from 0 to 255.

16-bits wide (2 bytes)

– Maximum signed range is from −32,768 to 32,767.
– Maximum unsigned range is from 0 to 65,535.

32-bits wide (4 bytes)

– Maximum signed range is from −2,147,483,648 to 2,147,483,647.
– Maximum unsigned range is from 0 to 4,294,967,295.

64-bits wide (8 bytes)

– Maximum signed range is from −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
– Maximum unsigned range is from 0 to 18,446,744,073,709,551,615

There is more information on integer ranges here: http://en.wikipedia.org/wiki/Integer_(computer_science)

Good to know

Without explicit specification, the type of an integer constant defaults to the smallest possible type that can hold the constant’s value, unless the value is suffixed with an L , l , U , or u . The following list describes the type assignment of integer constants:

* If the constant has no suffix, and is given in decimal radix, it will have the first type from this list capable of storing the value: int , long int , unsigned long int .
* If the constant has no suffix, and is given in octal or hexadecimal radix, it will have the first type from this list capable of storing the value: int , unsigned int , long int , unsigned long int .
* If the constant has the U or u suffix, it will have the first type from this list capable of storing the value: unsigned int , unsigned long int .
* If the constant has the L or l suffix, it will have the first type from this list capable of storing the value: long int , unsigned long int .
* If the constant has both U and L suffixes (or the lowercase combination), it will have type unsigned long int .
* For example, the constant 59 is assigned the int data type by default, but the constant 59L is assigned the long data type. 59UL is typed as unsigned long int .

* from cs.auckland.ac.nz

Octal notation. (Base 8)

Integers starting with a 0 are treated as octal (base 8).

int myDecimal   = 16;
int myOctal     = 020; // 16 in decimal.
printf("Value of myOctal is: %o", myOctal); // Value of myOctal is: 50
printf("Value of myOctal is: %#o", myOctal); // Value of myOctal is: 050

Hexidecimal notation. (Base 16)

Integers starting with a 0x are treated as hexidecimal (base 16). In hexidecimal, symbols 0–9 represent values zero to nine, and A, B, C, D, E, F (or alternatively a–f) represent values ten to fifteen.

int myDecimal   = 15;
int myHex       = 0xF; // 15 in decimal.
printf("Value of myHex is: %x", myHex); // Value of myHex is: f
printf("Value of myHex is: %#x", myHex); // Value of myHex is: 0xf
printf("Value of myHex is: %X", myHex); // Value of myHex is: F
printf("Value of myHex is: %#X", myHex); // Value of myHex is: 0xF
printf("Value of myHex is: %i", myHex); // Value of myHex is: 15

The primary use of hexadecimal notation is a human-friendly representation of binary-coded values. Hexadecimal is also commonly used to represent computer memory addresses.

On some machines each hexidecimal code (e.g. 5 or D) takes up 4 bits (a nibble) as opposed to a decimal which takes up (at least) 8 bits.

Binary notation. (Base 2)

C (and C++) do not have native support for binary notation. You can only work with decimal (base 10), octal (base 8) or hexadecimal (base 16) integers as standard.

int myBinary = 0xF; // binary: 00001111

Some compliers (such as GCC) support binary notation using an 0b prefix.

int x = 0b00001111;

But as support isn’t widespread it’s safer to stick to base 10, 8 or 16.

More Information

http://en.wikipedia.org/wiki/C_data_types