(Updated March 25, 2023)
Table of contents
-
Overview
Components of the Language
Data Types
Using
void
and bool
Arithmetic Operators and Precedence
Constants, Literals, Variables, and
const
Strings
Basic Input/Output
Escape
Increment, Decrement and Comments
The
main
FunctionQuiz
Exercises
Overview
This is an extensive chapter. Many components of the C programming language are presented throughout. Although there is a big quiz at the end to measure comprehension and retention, the best approach is to consume this chapter in small portions.
Take your time and remember that this is a 10,000-foot view of the language. Refer to this chapter often when determining the use of certain data types and operators. This chapter will always be here, so do not worry if it is not all sticking at once.
Remember that, as with anything new, we have to start somewhere. So, we begin with the fundamental components of the language.
Components of the Language
The C language is relatively small when you look at the parts that make up the core aspects. First, we will discuss the rules and the meaning of the language. Like any foreign language, some rules govern how the language may be written, and understanding or purpose is derived from how the words are arranged. For example, when we say, “The car belongs to Bill.” We know that the subject, car, and object, Bill, are arranged such that we can derive the meaning of Bill owning a vehicle. We can derive further meaning if there are surrounding sentences of a paragraph in which this original sentence also belongs. Perhaps we would learn that Bill’s car is large or small if it is gas, diesel, or even the color.
The point is every language has syntax rules that govern how the language may be written. The semantic rules help us to determine the meaning. C is broken down into three basic parts, or tokens, for writing the language. These tokens are symbols, reserved words and identifiers.
Objects are a region of memory containing a specific value viewed as having a particular type. The use of the term object is directly from the ISO/IEC 9899:2018 standard (loosely interpreted here) and is not intended to imply object-oriented programming.
Symbols are the characters we use in our writing regardless of the subject. So punctuation, arithmetic, and relational symbols are used throughout the program. Table 1 lists some categories of symbols. Note that regardless of the number of characters used to make the symbol, it is considered one.
Puctuation | . | , | ; | “ | ! |
Arithmetic | + | – | * | / | % |
Relational | <= | != | > | < | == |
Table 1: Some symbols used in C.
Keywords, also called reserved words, are a standard set of words that are set aside expressly for the C language. You may not reuse these words as identifiers. If you do, the compiler will inform you that using the keyword is inappropriate by signaling a syntax error when you compile your program. When using an IDE, you will also know that the word is reserved when the word you are using has a different color from those that are identifiers. Keywords are listed in Table 2.
auto |
break |
case |
char |
const |
continue |
default |
do |
double |
else |
enum |
extern |
float |
for |
goto |
if |
inline (since C99) |
int |
long |
register |
restrict (since C99) |
return |
short |
signed |
sizeof |
static |
struct |
switch |
typedef |
union |
unsigned |
void |
volatile |
while |
_Alignas (since C11) |
_Alignof (since C11) |
_Atomic (since C11) |
_Bool (since C99) |
_Complex (since C99) |
_Generic (since C11) |
_Imaginary (since C99) |
_Noreturn (since C11) |
_Static_assert (since C11) |
_Thread_local (since C11) |
Table 2: C keywords.
Identifiers are used to label things. These things are often variables, functions, or constants. Like the rest of the C language, identifiers are case-sensitive. The identifiers you create may contain letters, digits, and the underscore character (_).
Your identifiers must not begin with a digit. They can start with any of the other characters. It is conventional to limit the use of the underscore for special-purpose identifiers or to use the underscore where spaces would ordinarily appear in everyday language.
The C standard defines an upper bound on the length of an identifier as 63 significant initial characters. As such, you should endeavor to keep the length of your identifiers within this range.
A few acceptable identifiers are shown in Table 3.
sum | totalWords | Sum |
test1 | Word_Count | myLittleVariable |
Table 3: Examples of unique and valid identifiers.
Data Types
There are several basic data types in C. There are integer types, real (floating point), and character types. These are loose classifications. This is because several modifiers may change the default behavior of a given data type.
The rudimentary set of types are int
, float
, double
and char
.
The set of modifiers are short
, long
, signed
and unsigned
.
Given these types and modifiers, we can derive a stunning set of various sizes and styles of the same root data type. This can also lead to some confusion. If signed
is omitted and unsigned
is also omitted, and the type is not char
, then the data type is assumed to be signed
.
Further, there is also accepted shorthand. So short
, short int
, and signed short int
are all the same.
The types and properties list are displayed in Table 4.
Data Type | Range of values | Size in bytes (bits) |
---|---|---|
char |
-127 to 127 or 0 to 255 | 1 (8) |
signed char |
-127 to 127 | 1 (8) |
unsigned char |
0 to 255 | 1 (8) |
short short int signed short signed short int |
-32767 to 32767 | 2 (16) |
unsigned short unsigned short int |
0 to 65535 | 2 (16) |
int signed signed int |
-32767 to 32767 | 2 (16) |
unsigned unsigned int |
0 to 65535 | 2 (16) |
long long int signed long signed long int |
-2147483647 to 2147483647 | 4 (32) |
unsigned long unsigned long int |
0 to 4294967295 | 4 (32) |
long long long long int signed long long signed long long int |
-9223372036854775807 to 9223372036854775807 |
8 (64) |
unsigned long long unsigned long long int |
0 to 18446744073709551615 | 8 (64) |
Table 4: Values and sizes of integer data types.
The only integral data type with special written properties is char
. The values representing this type use single quotes to denote that value as a char
literal. A few examples of these literals are ‘+’, ‘A’, ‘(‘ and ‘n’. Without the single quotes, the plus symbol may be mistaken for an arithmetic operator, the open parenthesis as part of the C language, or the letters mistaken for identifiers.
Characters are assumed to be in the 127-bit set of ASCII characters. Characters are numeric quantities, and the characters we use with single quotes are human consumable forms, so we do not need to memorize the entire character set. The table of values to characters is standardized and represents a collating sequence for several character ranges.
Given the use of the ASCII collating sequence, it is important to recognize the significance of the groupings of characters therein. Three starting points need to be mentioned. The digit ‘0’ (48), capital ‘A’ (65) and lower case ‘a’ (97). Following ‘0’ is the digit ‘1’, then ‘2’ and so on. The same goes for the two starting points for the alphabet. If ‘A’ is 65, then ‘B’ is 66 and ‘C’ is 67.
The ASCII collating sequence is shown in Table 5.
Table 5: The ASCII Collating sequence.
There are a few real data types, and their properties are displayed in Table 6.
Data type | Range of values | Max Significant Digits | Size in bytes (bits) |
---|---|---|---|
float |
1.17549435E-38F to 3.40282347E+38F | 7 | 4 (32) |
double |
2.2250738585072014E-308 to 1.7976931348623157E+308 | 15 | 8 (64) |
long double |
3.362103e-4932 to 1.189731e+4932 | 19 | 10 (80) |
Table 6: Properties of real data types.
Should you be interested in the ranges of values of your given platform, you can use the code in Example 1 to display the minimums and maximums of the current data types supported in the C17 standard.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>
int main(void) {
printf("CHARs\n");
printf("CHAR_BIT : %d\n", CHAR_BIT);
printf("CHAR_MAX : %d\n", CHAR_MAX);
printf("CHAR_MIN : %d\n", CHAR_MIN);
printf("SCHAR_MAX : %d\n", SCHAR_MAX);
printf("SCHAR_MIN : %d\n", SCHAR_MIN);
printf("UCHAR_MAX : %d\n", UCHAR_MAX);
printf("\nINTs\n");
printf("SHRT_MAX : %d\n", SHRT_MAX);
printf("SHRT_MIN : %d\n", SHRT_MIN);
printf("USHRT_MAX : %d\n", (unsigned short) USHRT_MAX);
printf("INT_MAX : %d\n", INT_MAX);
printf("INT_MIN : %d\n", INT_MIN);
printf("UINT_MAX : %u\n", (unsigned int) UINT_MAX);
printf("LONG_MAX : %ld\n", (long) LONG_MAX);
printf("LONG_MIN : %ld\n", (long) LONG_MIN);
printf("ULONG_MAX : %lu\n", (unsigned long) ULONG_MAX);
printf("LLONG_MAX : %lld\n", (long long) LLONG_MAX);
printf("LLONG_MIN : %lld\n", (long long) LLONG_MIN);
printf("ULLONG_MAX : %llu\n", (unsigned long long) ULLONG_MAX);
printf("\nFLOATs\n");
printf("FLT_MAX : %e\n", FLT_MAX);
printf("FLT_MIN : %e\n", FLT_MIN);
printf("DBL_MAX : %e\n", DBL_MAX);
printf("DBL_MIN : %e\n", DBL_MIN);
printf("LDBL_MAX : %Le\n", LDBL_MAX);
printf("LDBL_MIN : %Le\n", LDBL_MIN);
return 0;
}
Example 1: Data type minimums and maximums.
Do not be alarmed that the printf
‘s look strange and that you may not know where all of those names came from. If you copy the code to your editor or IDE and compile and run it, you will see your particular platform’s values for the different data types.
The output generated by a Macbook Pro running Apple clang version 12.0.0 (clang-1200.0.32.29) is shown below:
CHARs CHAR_BIT : 8 CHAR_MAX : 127 CHAR_MIN : -128 SCHAR_MAX : 127 SCHAR_MIN : -128 UCHAR_MAX : 255 INTs SHRT_MAX : 32767 SHRT_MIN : -32768 USHRT_MAX : 65535 INT_MAX : 2147483647 INT_MIN : -2147483648 UINT_MAX : 4294967295 LONG_MAX : 9223372036854775807 LONG_MIN : -9223372036854775808 ULONG_MAX : 18446744073709551615 LLONG_MAX : 9223372036854775807 LLONG_MIN : -9223372036854775808 ULLONG_MAX : 18446744073709551615 FLOATs FLT_MAX : 3.402823e+38 FLT_MIN : 1.175494e-38 DBL_MAX : 1.797693e+308 DBL_MIN : 2.225074e-308 LDBL_MAX : 1.189731e+4932 LDBL_MIN : 3.362103e-4932
Using void
and bool
The void
data type represents an absence of any value. It indicates that no value is expected or should be provided wherever it is used. Any attempt to place a value where void
has been defined at the type will result in an error.
The official type for Boolean representation in C is _Bool
. That type and the bool
macro was introduced in C99. It is, however, a macro. To use it, you must include the header file stdbool.h.
The bool macro is converted to _Bool
, which is a reserved word. It has only two possible values. They are true and false, which are also macros that represent 1 and 0, respectively.
It is important to note that this header file defines macros that implement bool
, true
, and false
. These are not keywords and could be redefined.
It is not required to use bool
to represent a Boolean value. As we will see later, the use of conditional statements uses integer values to represent true and false.
Arithmetic Operators and Precedence
There are five arithmetic operators, as shown in Table 7. These operators are used to construct expressions. An expression is something simple like 3+2 or 8/5. So our expressions are made up of operators and operands. Operands are the values the operators work with. The previous examples are known as integer expressions. They are integer expressions because they have two integers as their operands, and the result will be an integer. Note that operands can be literals, identifiers, and other expressions.
Operator | Purpose |
---|---|
+ | addition |
– | subtraction |
* | multiplication |
/ | division |
% | modulus (remainder) |
Table 7: Arithmetic operators.
The operators in Table 7 are also known as binary operators. Not because they work with binary numbers but because they require two operands. You are probably familiar with the unary operators –
and +
. These can be seen when talking about the value ‐3. See how the minus sign has only one operand? It says that the value of 3 is negative as opposed to positive.
Why is it necessary to talk about unary and binary operators? Consider the expression
x = 4 - -3
Some would look at this and say, “Syntax error!” Quite the contrary. It says four take away a negative three or four minus negative 3, which is the equivalent of 4 plus 3. The result is 7.
When multiple operators are mixed within the same expression, operator precedence is considered. Operator precedence allows us to know the order in which the operators will be applied to the values. Of course, we can alter the default precedence rules by simply using parentheses. The higher precedence operators, the ones nearest the top, are done first. When there are many arithmetic operators of the same precedence, the operators will be evaluated based on associativity.
Table 8 shows the complete C precedence order for operators. Precedence is higher toward the top. All operators are evaluated left to right except unary, ternary, and assignments which are evaluated right to left.
Operator Precedence (highest to lowest) Associativity postfix () [] -> . ++ -- left to right unary + - ! ~ ++ -- (type)* & sizeof right to left multiplicative * / % left to right additive + - left to right shift << >> left to right relational < > <= >= left to right equality == != left to right bitwise AND & left to right bitwise XOR ^ left to right bitwise OR | left to right logical AND && left to right logical OR || left to right ternary ? : right to left assignment = += -= *= /= %= &= ^= |= <<= >>= right to left comma , left to right
Table 8: C precedence list.
Expressions With Mixed Types, Type Conversion, Widening and Narrowing of Primitive Types
When writing your programs, you will have values of varying types from varying sources. When this happens, we have to keep some simple rules in mind for how things will be evaluated and what the resultant type will be.
First, C is not a strongly typed language. To be strongly typed, each object must have a type, AND types must match on some level when applying operators. C is a statically typed language. This means that the types are known at compile-time, but the language and the compilers allow you to mix data types at will with no warnings about incompatible types or possible loss of precision. This is also known as implicit type conversion.
Implicit type conversion is handled through widening and narrowing, which is described below.
Widening
Consider the fact that values can not only be copied between variables but also between types. C provides a mechanism for automatically narrowing or widening values copied between data types or when used in mixed expressions.
The widening of data types is best described using the chart below.
char → int → long → float → double → long double
Given any starting type, follow the arrows until you reach your destination type. Your smaller type will be converted to the more significant type through sign extension or zero extension for signed and unsigned values, respectively. When moving from a fixed point (integral) to a floating point, internal conversion is made, and the fractional portion is simply zero.
Widening is the default behavior when mixing data of differing types in a given expression.
Narrowing
The narrowing of the types involves shrinking the current type to fit into the new type. You must remain keenly aware that in doing so, there is a chance that you will lose data if the receiver type is sufficiently smaller than the source value.
The narrowing of data types is shown in the chart below.
long double → double → float → long → int → char
In the narrowing process, the more significant type will be converted to the smaller type by way of preserving low-order bits (i.e., long to short), preserving bit patterns (converting between signed and unsigned), or truncating at the decimal point (converting floating-point to integral). Converting a floating-point number to a sufficiently small integral type may undergo two narrowing steps: truncating at the decimal point and preserving low-order bits.
Cast
If we need to change a type temporarily for a specific and intentional purpose, we can use a cast or explicit type conversion. The cast acts as a temporary type of conversion. First, the simplest expression to the right of the cast is evaluated then the cast is applied. The following few examples show how to perform a cast.
C Code Example | Result |
---|---|
(int)7.9 |
yields 7 since the double is converted to an int . (The value 7.9 is considered to be a double literal.) |
(float)25/6 |
yields 4.1666665 since int 25 is converted to float and divided by the int 6 (see rule 2, above). |
(float)(25/6) |
yields 4.0 since the two int s are divided, which yields no precision, and the 4 is converted to 4.0 (see rule 1, above). |
Table 9: Examples of casting.
Now that we have discussed narrowing, widening, and casting at length, it is essential to mention the default types for constants. Since there are so many forms of basic types, you will find that Table 10 identifies what sort of values are allowed in different integer data types.
Suffix | Example | C Types Allowed (first fit) |
---|---|---|
No Suffix | 35 |
int long int unsigned long int (before C99)long long int (since C99) |
U or u |
427U |
unsigned int unsigned long int unsigned long long int (since C99) |
L or l |
519L |
long int unsigned long int (before C99)long long int (since C99) |
l /L and u /U |
519ul 4057LU |
unsigned long int unsigned long long int (since C99) |
LL or ll |
397534ll 287934LL |
long long int (since C99) |
ll /LL and u /U |
397534ull 287934LLU |
unsigned long long int (since C99) |
f or F |
6.022f |
float |
No suffix | 6.022 |
double |
l or L |
6.022L |
long double |
Table 10: Examples of various basic types and suffixes.
Negative constants are treated as the unsigned value with the unary minus operator applied. This means that the compiler determines the best fit by using the unsigned representation of the constant value, then applies the sign.
Constants, Literals, Variables, and const
The term constant is being used as defined by the ISO/IEC 9899:2018 standard and means representing exact meaning whereby the intent is clear. When we say
x = 7;
We know the variable x
contains the value 7 when the statement is complete. Or
c = 'A';
There is no doubt that capital A has been assigned to the variable c
. We use the term constant for any value not expressed by acquisition from another source. Consider the following:
x = y;
In this example, the variable y
already contains some value to be assigned to x
. Some examples of constant values are shown below (see also Table 10, above):
Variables must be declared before you can use them, and they must be given a type. Here are a bunch of declared variables with a variety of constants being assigned to them:
int i = 35;
long l = 734L; // The use of L or l denotes a long type.
int o = 013; // octal for 11.
int x = 0x80; // hexadecimal for 128.
float f = 3.14f; // the f is used to denote a float type and avoid narrowing.
const double PI = 3.14; // the value is a non-modifiable double.
char c = '#'; // single quotes for chars
PI
. While the const
type qualifier can have a complex definition, we are only concerned with the fact that any type that is qualified by const
is not modifiable. You can initialize the variable, but no further modification can be made. The use of this qualifier will enlist the help of the compiler to make sure we do not accidentally change the variable in the future.When it comes to strings, however, we use double quotes to show the literal value. This is called a string literal. The example below shows how to safely copy a string literal into the variable n
.
char n[10];
snprintf(n, sizeof n, "%s", "Bill"); // double quotes for string literals
snprintf()
here because functions like strcpy()
are considered unsafe and should not be used.It is important to note that variables are not pre-initialized. The default value of a declared variable is undefined and not simply zero. Hence, we must endeavor to initialize variables that will be used in subsequent calculations (if they will not be filled by other means).
#include <stdio.h>
int main(int argc, char **argv) {
int sum, n;
sum = 0;
// Read a value from user into n...
sum = sum + n;
}
Example 2: Initializing a variable with an integer constant for later use.
In Example 2, we have a somewhat incomplete program – we are not actually reading anything from the user. Details to note:
- Line 5: The values of
sum
andn
are undefined. - Line 7: We initialize sum to zero. This variable now has a reliable value.
- Line 9: This is a comment to indicate something was read from the user, but that code is omitted for simplicity.
- Line 11: We can use the value of
sum
andn
in the expression because they both have valid values – it does not matter if the value is placed directly (Line 9) or indirectly through user input.
Failure to initialize variables before using them will result in an error from the compiler.
Consider the program in Example 3.
#include <stdio.h>
int main (int argc, char **argv) {
int test1, test2, test3, sum;
double average;
test1 = 90;
test2 = 85;
test3 = 87;
sum = test1 + test2 + test3;
average = sum / 3.0;
printf("The average is %f.\n", average);
}
Example 3: Program to calculate an average using variables, constants, and literals.
The output from this program is:
The average is 87.333333
There are several things to note about this program.
- Line 5: Several variables of type
int
are declared on the same line by using commas to separate the names. - Line 14: The value contained in sum is divided by the
double
value 3.0, which causes a widening of the result to thedouble
type which is then stored inaverage
. - Line 16: The value contained in
average
is used to replace the%f
in theprintf
statement (seeprintf
in Chapter 3) - Line 16: The
\n
is used to move the cursor to the next line since printf does not advance to the next line by default. - The variable
sum
could be eliminated. Consider for a moment how you would rewrite the average calculation if you eliminatedsum
.
We will discuss the use of printf
at length in the next chapter. However, we should mention the structure of printf
since several previous examples have used it.
The first thing to note is that printf
always has a string as the first argument. After that, there may be a comma-separated list of additional arguments of practically any type.
The printf from Example 3 is shown below:
printf("The average is %f.\n", average);
We know that the \n
provides the newline and moves the cursor to the next line. What is noteworthy is the %f
. This is called a conversion. The conversions make printf
so powerful. By using the %f
, we are telling printf
to save a spot where a floating point value will be placed later. By later, we mean at runtime – when the program gets executed by the user.
When the program executes, the value of average
at that time will be substituted for the %f
. It is that easy, and we will see more powerful tools in the next chapter.
Strings
The string type will be as valuable as the basic data types mentioned earlier. Strings represent data that a single char
is incapable of expressing, such as a person’s name or street address of the name of a country.
Strings are exceedingly easy to represent. They use double quotes to denote the string literal, much like single quotes are used to indicate a char
literal value. A few string literals are listed below.
"Bill"
"Route 2"
"United States"
The empty string or null string is represented as empty double quotes (""
) and has a length of zero.
You will often need the library of string functions to help you manage strings. To use string functions successfully, you must first import <string.h>
. String handling is quite different than in other languages and requires care.
In the following example, we will use the strlen
function while we manage some strings.
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char name[30] = "William";
printf("The size of name is %lu\n", sizeof name);
printf("The value of name is %s\n", name);
printf("The length of name is %lu.\n", strlen(name));
}
Example 4: Using a simple string.
The code in Example 4 shows a declared and initialized string called name
.
printf
is using more conversions. In this example, it is %s
(string) and %lu
for long, unsigned value.
It is essential to know that in C, we always deal with different-sized values. Simply using %d
instead of %lu
has the potential to lose precision due to misrepresentation of the data. Fortunately, the compiler will provide warnings to inform you when this might occur.
Finally, we use the strlen
function to count the number of characters in the string – which is also the index of the null character.
The output of Example 4 is shown below.
The size of name is 30 The value of name is William The length of name is 7.
Strings and how to safely use them are covered in much greater detail in Chapter 3.
Basic Input/Output
We have seen a few examples of printf
and the introduction of scanf
. The truth is, there are many ways to display output. There are also many ways to get input from the user, and only a few are secure enough to be trusted today.
It is essential to review the chronology of the program in Example 3.
- The variables are declared.
- Values are assigned for the three test grades.
- The sum is calculated.
- The average is calculated and displayed.
It would be silly to put the output statement first; we have nothing to display. Remember the steps necessary to solve a problem when working through any solution. This example is rudimentary, and you can expect your programming projects to be more complex than this. Still, the premise is the same: determine what must happen first, then what must occur after that, and so on.
Consider what you would do if you had to read values from the user instead of assigning them directly using literals. The scanf
function is a relatively simple way to read data from some input stream, including the user’s keyboard.
#include <stdio.h>
int main(int argc, char **argv) {
int test1, test2, test3;
double average;
printf("Enter test 1: ");
scanf("%d", &test1);
printf("Enter test 2: ");
scanf("%d", &test2);
printf("Enter test 3: ");
scanf("%d", &test3);
average = (test1 + test2 + test3) / 3.0;
printf("The average is %f.\n", average);
}
Example 5: Program modified to read values from user.
The program in Example 5 is fundamentally the same as Example 3 with test data read from the user by the scanf
function. A sample run with the user-provided values in bold is shown here:
Enter test 1: 90
Enter test 2: 85
Enter test 3: 87
The average is 87.333333.
There are some items to note about this program:
- The variable
sum
was eliminated. - The
scanf()
function stores the input in the form of an integer in the address of the variable passed as the second argument. - There is an alternation of prompt, read, prompt, read. This is necessary to inform the user what to do next.
A very important note about the program in Example 5
is the use of the address-of operator &
. To use the scanf()
function, we need to work with destinations that are addresses – memory locations. This is because we want scanf
to store the requested information in a particular location in memory. That memory is also a named location, specifically the variable we intend to use later to reference the stored value. So, instead of passing test1
, we have to pass the address of test1
or &test1
.
The %d
in scanf
allows us to specify the type of data we wish to parse. The printf
function does the same thing with what we wish to write.
Integers are not the only things we would want to read. What about characters, words, lines of text, and precision values? The next chapter explains the details of how that can be accomplished.
This and the next chapter are focused on providing essential tools to achieve I/O with the end-user. We acknowledge these will need review and refinement, but for the moment, you can get up and running now, and we will discuss the finer points and best practices later.
It is worth noting that the scanf
function gets a bad reputation on nearly every online forum. There are issues with it. They are not wrong in this respect. But this book will help you navigate the correct way to use the tools that are still deemed safe.
Escape
Have you considered how we store the single quote character in a char
variable if single quotes are used to define character literals? Or, how do we print a double quote character if double quotes define the boundaries of a string literal?
It is frequently necessary to print characters that are otherwise reserved for special use. This is quickly resolved with a series of escape sequences. Escape sequences are represented by a leading backslash (\) which changes the meaning of the next character.
Consider the following:
char squote;
char fiveBlankLines[] = "\n\n\n\n\n";
squote = '\'';
The character sequence \'
escapes the single quote character making it simply a single quote and not part of the delimiting quotes for a character literal. The assignment statement stores a single quote character into the variable squote
.
A similar concept is applied to the sequence \n
, which makes the n
special – this n
represents the newline character. A list of escape sequences is shown in Table 8.
Escape Sequence | Purpose |
---|---|
\n |
Newline. Moves the cursor to the beginning of the next line. |
\t |
Tab. Moves cursor to next tab stop. |
\\ |
Backslash. Represents a backslash. |
\" |
Double quote. Represents a double quote within a string or character literal. |
\' |
Single quote. Represents a single quote within a string or character literal. |
\b |
Backspace. Move the cursor back one character. |
\a |
Audible bell. |
\r |
Return. Move the cursor to the beginning of the current line. |
\0 |
The null character. Typically used to mark the end of a string. |
Table 8: Common escape sequences.
Increment, Decrement and Comments
Some additional aspects of the C language that require mention but not a great deal of detail are listed here.
The increment (++) and decrement (‐‐) operators are unary operators similar to unary plus and unary minus. One crucial difference is ++ and ‐‐ may appear on either side of the expression element. This defines pre- or post- for the operator.
Consider the following:
int x=4, y=6, z, a;
x++;
--y;
z = x++;
a = ++y;
printf("x is %d\ny is %d\nz is %d\na is %d\n", x, y, z, a);
Example code for increment and decrement operators.
printf
noted above looks incredibly complex. Look closer and practice seeing the boundaries within the format string. Note the text to be expressly printed, the placement of the conversions, and the use of newlines to produce the output below.
This is a good example of a complex-looking printf
function call which you will also be writing one day!
If this code snippet were in a complete program, the output would look like this:
x is 6 y is 6 z is 5 a is 6
The variable x
starts at 4, and y
starts at 6. A post-increment is applied to x
, making its value 5. A pre-decrement of y
makes its value 5. Since these statements only have single variable expressions, it doesn’t matter if they are pre or post – in this case.
Now look at the assignment statements for z
and a
. The variable z
will get the value of a post-increment of x
. This means z
will get the value of x
before it is incremented. Therefore z
will be 5, and x
will be 6. The variable a
will get the value of y
after it is incremented. Therefore a
will be 6, and y
will be 6.
Comments
Comments are the favorite tool of any good programmer to document their program, especially complex or non-intuitive solutions. Comments come in two forms – the //
form (since C99) and the /* */
form.
int x; //comment to end of line describing purpose of x
/*
This comment is allowed to span lines.
It is quite useful when the necessary programming note
requires much more detail than a single line will allow.
*/
x = 10;
Examples of C comments.
As you can see, the //
comment is only a comment until the end of the line. and the /*...*/
comment may span multiple lines. Note that there are no spaces between any of the comment characters. Therefore the //
, /*
and */
must not have spaces.
The main
Function
As mentioned earlier, in any programming language, there needs to be an entry point to begin the execution of statements. In C that function is called main
. The main
function has had a number of variations since its inception in the original K & R standard. At that time, the most common implementation was
void main(void) {
// ...
}
The old K&R main
function.
This is no longer a valid form. In later releases, the return type (the type specified to the left of main
) was required to be int
. Today, the most widely accepted forms are as follows.
int main(void) {
// ...
return 0;
}
A main
function with no parameters.
or
int main(int argc, char *argv[]) {
// ...
return 0;
}
A more conventional use of the main
function.
or
int main(int argc, char **argv) {
// ...
return 0;
}
The last two are the same. (We will learn later that a *
on the left can be moved to the right and represented as []
.)
The first version says the main
function will accept no arguments from the hosting system’s runtime environment. This is okay and is often the desired implementation. The later versions indicate that information can be passed to the main function as strings. This can be very useful if additional instructions are required for the program’s success. This information can be anything from file names to command-line options; anything goes as long as a string can represent it.
main
function is zero, even if there is no return
statement or the return
has no specified value. The value returned from main
is typically used to indicate success or failure to the host environment.
It is, however, considered good form to provide a return
statement.
It is worth mentioning that some implementations allow for another variation shown below.
int main(int argc, char **argv, char **envp)
This is not a part of any C standard. Using char **envp
is entirely vendor and/or platform-dependent.
Quiz
Exercises
[Note: All of this is done using the basic template for main
, above.]
- (Beginner) Declare a
string
calledname
capable of holding 15 characters. - (Beginner) Declare an
int
calledi
, adouble
calledd
andboolean
calledb
. - (Beginner) Assign appropriate values to each variable and use
printf()
to display the contents of each, separately. - (Intermediate) Intentionally create errors in some statements to see how the compiler conveys its knowledge of the issue. These should include the following:
- Missing punctuation.
- Assigning inappropriate values to variables.
- Leaving quotes or parenthesis unbalanced.
- (Advanced) Try your hand at
scanf
and use it to read in values to populate the variables noted above. [You may have to read ahead a bit!]