C Fundamentals
Compilation Terminologies
Preprocessing
The process of giving the source file to a preprocessor which obeys commands that begin with # symbol (also called directives). Also acts like an editor which can add things to the program and make modifications.
Compiling
The process of supplying the program (which has been modified by the preprocessor) to a compiler, which translates the code into machine instructions (object code).
Linking
The process where the linker combines the object code produced by the compiler with any additional code needed to yield a complete executable program. Additional code include library functions (like printf) that are used in the program.
Directives
Commands intended for the preprocessor. For example:
#include <stdio.h>
here, the directive states that the information in <stdio.h> is to be "included" into the prograrm before it is compiled.
By default, directives are one line long; there's no semicolon or other special marker at the end of a directive.
Compilation Flags
Some compilation options used in most of my programs and their meanings are:
-Wall: Causes the compiler to produce warning messages when it detects possible errors. (-Wcan be followed by codes for specific warnings;-Wallmeans "all-Woptions.") Should be used in conjuction with-O(optimize) for maximum effects.-W: Issues additional warnings messages beyond those produced by-Wall.-pedantic: Issues all warnings required by the C standard. Causes programs that use non-standard features to be rejected.-ansi: Turns off features of GCC that aren't standard C and enables a few standard features that are normally disabled.-std=c89or-std=c99: Specifies which version of C the compiler should use to check the program.
Source File
-
By default, any rational (or irrational) literal is viewed as a
double. For instance, the identifierPIis a double literal:Double literal#define PI 3.1415Floating Point SuffixTo change this into a floating literal, we can append
forFat the end of literal as:Floating literals#define PI 3.1415f
#define E 2.7182F -
The C standards provide some functions to the programmer. Functions such as
printf,strncmp, and such are library functions. Asking a function to perform its assigned task is known as calling function. -
A series of characters enclosed in double quotation marks is called string literal.
-
The right hand side of an assignment can be a formula (or expression.)
Assignment operation...
/*
* If foo is a numeric data type,
* arithmetic conversion will take
* place unless the operands on the
* RHS is of same type.
*/
foo = bar + baz;
lorem = ipsum();
...Arithmetic ConversionsThe strategy behind the usual arithemtic conversions is to convert operands to the "narrowest" type that will safely accomodate both values. (Roughly speaking, one type is narrower than another if it requires fewer bytes to store.)
For the above example, consider that
foois of typeint,baroffloat, andbazofint. When line8is executed, the result of+operator is of typefloatand narrowed down to value forint. It acts like an implicit conversion.Chapter 7 - Basic Types goes into details of Arithmetic Conversions.
-
Variables that are declared (and not initialized) may be initialized with the value of 0. Automatic variables are not initialized and contains garbage value while static variables (and global variables) are initialized with the default value of 0.
Declaration and Initialization// Global variables when declared store initial value of 0.
int global_var; /* initialized to 0 */
int main (void) {
int local_var; /* initialized to random value */
int init_var = 10; /* initialized to value of 10 */
int height = 8, length = 10, breadth = 12; /* multiple initialization */
int x, y, z = 100; /* only z is initialized */
} -
Consider a program fragment of the kind:
Comment...
a/**/b = 0;
...One might assume that the comment will be replaced by no character, hence making
ab. But this is not the case. The C standard states that the compiler must replace each comment by a single space character, so we'll end up with the following (illegal) statement:Resulta b = 0; -
Functions are like "procedures" or "subroutines" in other programming languages - they're building blocks from which programs are constructed.
-
A statement is a command to be executed when the program runs. C requires that each statement end with a semicolon. (an exception to this is compound statment.) The semicolon shows the compiler where the statement ends; since statements can continue over serveral lines, it's not always obvious where they end.
-
A variable is a storage location used to store data temporarily during a program execution.
Variable TypeEach variable have a type, which specifies what kind of data it will hold.
-
Variables must be declared - described for the benefit of the compiler - before they can be used. To declare a variable, we first specify the type of the variable, then its name. For example, to declare a variable named
radiusof typeintand another variable namedareaof typefloat, we do:Variable declarationint radius;
float area;Multiple DeclarationIf several variables have the same type, their declarations can be combined:
Multiple variable declarationint length, breadth;
float price, sale;❗Pre-C99 DeclarationBefore C99, C programs were required to have all the variables declared before at the beginning. For instance, the code
Invalid C89 Programint main (void) {
int x = 10;
printf("The value of x is: %d\n", x);
int y;
...
}would cause an warning as the variable
yis declared after a statementprintfin this case. But in C99, declarations doesn't have to come before statements. -
A variable can be given a value by means of assignment. For example, the statements:
Assignments/*
* Before a variable can be assigned a value - or used in any other way,
* for that matter - it must first be declared.
*/
int height;
int length;
int width;
height = 10;
length = 20;
width = 30;assign values to
height,length, andwidth. The numbers10,20, and30are said to be constants.
I/O
-
C standard library provides various I/O library functions defined in
stdioheader file. -
The "f" in
printfandscanfrefers to formatted, hence, "print formatted" and "scan formatted" respectively. -
Identifiers are defined as the name given to variables, functions, macros, and other entities.
-
printfisn't limited to displaying numbers stored in variables; it can display the value of any numeric expression. For example,Printing expressions...
volume = height * length * width;
printf("%d\n", volume);
printf("%d\n", height * length * width);
...printf's ability to print expressions illustrates one of C's general principles:Whenever a value is needed, any expression of the same type will do.
-
Regarding
printf, the library function has an internal buffer which is used to store the "format string" and flush it to the standard output once the buffer is full. To observe this behavior, consider the program fragment below:printf and write#include <stdio.h> /* for printf */
#include <unistd.h> /* for write */
int main (void) {
const char helloworld[15] = "Hello, World!";
printf("printf is slow!");
write(1, helloworld, sizeof(helloworld));
}One might expect the output to be as:
Incorrect outputprintf is slow!Hello, World!But the actual output will be:
Correct outputHello, World!printf is slow!This is because the standard output stream - also known as the
stdoutstream - is line buffered. Unless the internal buffer ofprintfis full or contains the newline character (usually\n, but sometimes\r\n).System CallsDescribing what the
writesystem call does is not the scope of this section. But it should be noted that underneath theprintfcall,writeis called to write tostdout(the only streamprintfis allowed to write).ReadmoreThis stackoverflow thread discusses why
printfdoes not flush the buffer immediately. -
A C program contains a series of tokens - group of characters that can't be split up without changing their meaning. Tokens are identifiers, keywords, operators like +, -, and such, punctuation marks like comma, semi-colon and string literals.
-
Spaces in a C program does not matter as long as there is no space between tokens such that it changes the meaning of the token.
Identifiers
There is no limit to the length of name of an identifier, but, compilers are only required to remember the first 31 characters (63 characters in C99). So, if two names begin with the same 31 characters, a compiler might be unable to distinguish between them.
There are some special rules for identifiers with external linkage. Old linkers can handle only short names, so, only the first 6 characters are significant in C89 (plus it is not case sensitive). For C99, the first 31 characters are significant and the case of the letters are taken into account.
The return value in the main function should be defined even though it is not mandatory. Before the C99 compiler, the main function of the program would return an undefined value to the host environment (OS) if the return value was not defined. But in C99 compiler, even if the return value for the main function is not defined, when main reaches the end, the program returns 0 (even if not defined).
Keywords
A keyword can't be used as identifier. Keywords have special significance to C compilers. In the table below, 5 new keywords were added in C99.
auto enum restrict [+] unsigned
break extern return void
case float short volatile
char for signed while
const goto sizeof _Bool [+]
continue if static _Complex [+]
default inline [+] struct _Imaginary [+]
do int switch
double long typedef
else register union
-> [+] beside the name represents the keywords added in C99
-> Total keywords: 37
Because of C's case-sensitivity, keywords must appear in programs exactly as shown in the table above, with all letters in lower case.
Some compilers treat certain identifiers (asm, for example) as additional keywords. Identifiers that belong to the standard library are restricted as well. Accidently using one of these names can cause an error during compilation or linking. Identifiers that begin with an underscore are also restricted.
Additional Notes
-
A C program may contain comments of the form:
Valid comment/*
This is multi-
line comment
*/
// This is single-line commentSingle-line comment is a feature of C99. C89 only supports multi-line comments.
It should be noted that multi-line comments cannot be nested. For instance, the comment below is invalid:
Invalid comment/*
/* This format of commenting is not supported. */
*/
Exercises
Question 2.1
Create and run Kernighan and Ritchie's famous “hello, world" program:
#include <stdio.h>
int main (void) {
printf("hello, world\n");
}
Do you get a warning message from the compiler? If so, what's needed to make it go away?
- Answer
- Output
- Program
No, this code did not generate any warnings, mostly in C99 the main function of int return type with no return value means "the termination status returned to the host environment is undefined".
hello, world
#include <stdio.h>
int main (void) {
printf("hello, world\n");
}
Question 2.2
Consider the following program:
#include <stdio.h>
int main (void) {
printf("Parkinson's Law:\nWork expands so as to ");
printf("fill the time\n");
printf("available for its completion. \n");
return 0;
}
- Identify the directives and statements in this program.
- What output does the program produce?
- Answer
- Output
- Program
- One directive:
#include <stdio.h>, and four statements: threeprintfstatement and onereturnstatement - Output: Check Output tab.
Parkinson's Law: Work expands so as to fill the time available for its completion.
#include <stdio.h>
int main (void) {
printf("Parkinson's Law:\nWork expands so as to ");
printf("fill the time\n");
printf("available for its completion. \n");
return 0;
}
Question 2.3
Condense the dweight.c program by (1) replacing the assignments to height, length, and width with initializers and (2) removing the weight variable, instead calculating (volume + 165) / 166 within the last printf.
- Output
- Program
Dimensions (in inches): 12x10x8 Volume (in cubic inches): 960 Dimensional Weight (in pounds): 6
#include <stdio.h>
int main (void) {
int height = 8, length = 12, width = 10, volume;
volume = height * width * length;
printf("Dimensions (in inches): %dx%dx%d\n", length, width, height);
printf("Volume (in cubic inches): %d\n", volume);
printf("Dimensional Weight (in pounds): %d\n", ((volume + 165) / 166));
return 0;
Question 2.4
Write a program that declares several int and float variables — without initializing them — and then prints their values. Is there any pattern to the values? (Usually there isn't.)
- Output
- Program
The values of integers that are not initialized are: Value in var_1: 73629696 Value in var_2: 73629696 Value in var_3: 73629696 Value in var_4: 73629696 Value in var_5: 73629696 The values of floats that are not initialized are: Value in var_6: 0.000000 Value in var_7: 0.000000 Value in var_8: 0.000000 Value in var_9: 0.000000 Value in var_10: 0.000000
#include <stdio.h>
int main (void) {
int var_1, var_2, var_3, var_4, var_5;
float var_6, var_7, var_8, var_9, var_10;
printf("The values of integers that are not initialized are: \n");
printf("Value in var_1: %d\n", var_1);
printf("Value in var_2: %d\n", var_2);
Question 2.5
Which of the following are not legal C identifiers?
100_bottles_100_bottlesone_hundred_bottlesbottles_by_the_hundred_
- Answer
#include <stdio.h>
int main (void) {
// int 100_bottles; // illegal identifier
int _100_bottles; // legal identifier
int one__hundred__botles; // legal identifier
int bottles_by_the_hundred_; // legal identifier
return 0;
}
Question 2.6
Why is it not a good idea for an identifier to contain more than one adjacent underscore (as in current____balance, for example)?
- Answer
It is probably a good idea to use a sufficient amount of underscore as the compiler usually stores the first few characters of an identifier (31 characters in C89 and 63 characters in C99). Also, while taking external linkage into account, only the first six characters are significant in C89 while it does not care for the case sensitivity (31 significant characters in C99 while taking case sensitivity into account).
Question 2.7
Which of the following are keywords in C?
forIfmainprintfwhile
- Answer
If we take case sensitivity into account, If is not considered a keyword (if is). And main is not a keyword, it is the name of the function that must be present in a C program.
Question 2.8
How many tokens are there in the following statement?
answer=(3 * q - p * p) / 3;
- Answer
There are a total of 14 tokens in the given statement.
tokens: answer, =, (, 3, *, q, -, p, *, p, ), /, 3, ; identifier: answer, q, p punctuation: (, *, -, /, ) constants: 3
Question 2.9
Insert spaces between the tokens in Exercise 8 to make the statement easier to read.
- Answer
answer = (3 * q - p * p) / 3;
Question 2.10
In the dweight.c program (Section 2.4), which spaces are essential?
- Answer
In the program, the spaces are essential to separate out the directives from the main program as the directives are usually of one line.
Programming Projects
Project 2.1
Write a program that uses printf to display the following picture on the screen:
*
*
*
*
* *
* *
*
- Program
#include <stdio.h>
int main (void) {
printf(" *\n");
printf(" *\n");
printf(" *\n");
printf("* *\n");
printf(" * *\n");
printf(" *\n");
Project 2.2
Write a program that computes the volume of a sphere with a 10-meter radius, using the formula v = 4/3πr3. Write the fraction 4/3 as 4.0f/3.0f.(Try writing it as 4/3. What happens?) Hint: C doesn't have an exponentiation operator, so you'll need to multiply r by itself twice to compute r3.
- Answer
- Program
When the VOL_FACTOR is only ( 4 / 3 ), it truncates the value after the decimal leaving only 1 which results in the volume to be of value 3141.590 but when changed to ( 4.0f / 3.0f ), it results the correct value of 4188.787.
#include <stdio.h>
#define PI 3.14159f
#define VOL_FACTOR ( 4.0f / 3.0f )
int main (void) {
float radius = 10.0f;
float volume = VOL_FACTOR * PI * (radius * radius * radius);
printf("The volume of the sphere is: %.3f\n", volume);
Project 2.3
Modify the program of Programming Project 2 so that it prompts the user to enter the radius of the sphere.
- Program
#include <stdio.h>
#define PI 3.14159f
#define VOL_FACTOR ( 4.0f / 3.0f )
int main (void) {
float radius = 0.0f;
printf("Enter the radius of sphere: ");
scanf("%f", &radius);
Project 2.4
Write a program that asks the user to enter a dollars-and-cents amount, then displays the amount with 5% tax added:
Enter an amount: 100.00
With tax added: $105.00
- Program
#include <stdio.h>
int main (void) {
float value_before_tax = 0.0f;
float value_after_tax = 0.0f;
printf("Enter an amount: ");
scanf("%f", &value_before_tax);
Project 2.5
Write a program that asks the user to enter a value for x and then displays the value of the following polynomial:
Hint: C doesn't have an exponentiation operator, so you'll need to multiply x by itself repeatedly in order to compute the powers of x. (For example, is cubed.)
- Program
#include <stdio.h>
// #define SQUARE_NUM(x) (x * x)
// #define CUBE_NUM(x) (x * x * x)
// #define QUAD_NUM(x) (x * x * x * x)
// #define QUINT_NUM(x) (x * x * x * x * x)
int main (void) {
int x = 0;
int result = 0;
Project 2.6
Modify the program of Programming Project 5 so that the polynomial is evaluated using the following formula:
Note that the modified program performs fewer multiplications. This technique for evaluating polynomials is known as Horner's Rule.
- Program
#include <stdio.h>
int main (void) {
int x = 0;
int result = 0;
printf("Program that computes 3x^5 + 2x^4 - 5x^3 - x^2 + 7x - 6\n");
printf("Enter the value for x: ");
scanf("%d", &x);
Project 2.7
Write a program that asks the user to enter a U.S. dollar amount and then shows how to pay that amount using the smallest number of $20, $10, $5, and $1 bills:
Enter a dollar amount: 93
$20 bills: 4
$10 bills: 1
$5 bills: 0
$1 bills: 3
Hint: Divide the amount by 20 to determine the number of $20 bills needed, and then reduce the amount by the total value of the $20 bills. Repeat for the other bill sizes. Be sure to use integer values throughout, not floating-point numbers.
- Program
#include <stdio.h>
int main (void) {
int amount = 0;
printf("Enter a dollar amount: ");
scanf("%d", &amount);
int twenty_bill_count = amount / 20;
Project 2.8
Write a program that calculates the remaining balance on a loan after the first, second, and third monthly payments:
Enter amount of loan: 20000.00
Enter interest rate: 6.0
Enter monthly payment: 386.66
Balance remaining after first payment: $19713.34
Balance remaining after second payment: $19425.25
Balance remaining after third payment: $19135.71
Display each balance with two digits after the decimal point. Hint: Each month, the balance is decreased by the amount of the payment, but increased by the balance times the monthly interest rate. To find the monthly interest rate, convert the interest rate entered by the user to a percentage and divide it by 12.
- Program
#include <stdio.h>
int main (void) {
float loan = 0.0f, interest_rate = 0.0f, monthly_payment = 0.0f;
printf("Enter amount of loan: ");
scanf("%f", &loan);
printf("Enter interest rate: ");
scanf("%f", &interest_rate);