CSCI-UA.
0201
Computer Systems Organization
C Programming
Mohamed Zahran (aka Z)
mzahran@[Link]
[Link]
Many slides of this lecture are adapted from Lewis Girod, CENS Systems Lab
[Link]
and Clark Barrett
Brian Kernighan Dennis Ritchie
In 1972 Dennis Ritchie at Bell Labs writes C and in 1978 the
publication of The C Programming Language by Kernighan &
Ritchie caused a revolution in the computing world
Why C?
• Mainly because it produces code that runs
nearly as fast as code written in assembly
language. Some examples of the use of C might
be:
– Operating Systems
– Language Compilers
– Assemblers
– Text Editors
– Print Spoolers
– Network Drivers
– Language Interpreters
– Utilities
Your first goal: Learn C!
• Resources
– KR book: “The C Programming Language”
– These lectures
– Additional online resources ( some links on
the website)
• Learning a Programming Language
– The best way to learn is to write programs
Writing and Running Programs
#include <stdio.h>
/* The simplest C Program */ 1. Write text of program (source code) using an editor such
int main(int argc, char **argv)
as emacs, save as file e.g. my_program.c
{
printf(“Hello World\n”);
return 0; 2. Run the compiler to convert program from source to an
}
“executable” or “binary”:
$ gcc –Wall –g –o my_program my_program.c
3-Compiler gives errors and warnings; edit source file, fix it,
and re-compile
Run it and see if it works
$ ./my_program
Hello World
$▌
$ gcc –Wall –g –o my_program my_program.c
name the generated
generate all
executable
warnings
(default: [Link])
keep debugging
one or more
information
C files
About C
• Procedural language
– Functions calling each other, starting with main().
• Case-sensitive
• Four stages
– Editing: Writing the source code by using some
IDE or editor
– Preprocessing
– compiling: translates or converts source to object
code for a specific platform source code ->
object code
– linking: resolves external references and produces
the executable module
C Syntax and Hello World
#include inserts another file. “.h” files are called “header”
files. They contain stuff needed to interface to libraries and
code in other “.c” files.
This is a comment. The compiler ignores this.
#include <stdio.h> The main() function is always
/* The simplest C Program */ where your program starts
int main(int argc, char **argv) running.
{
Blocks of code are marked by
printf(“Hello World\n”); {…}
return 0;
}
Return ‘0’ from this function Print out a message. ‘\n’ means “new line”.
Preprocessing
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{
Preprocess
printf(“Hello World\n”);
return 0;
}
__extension__ typedef unsigned long long int __dev_t;
__extension__ typedef unsigned int __uid_t;
__extension__ typedef unsigned int __gid_t;
__extension__ typedef unsigned long int __ino_t;
__extension__ typedef unsigned long long int __ino64_t;
__extension__ typedef unsigned int __nlink_t;
__extension__ typedef long int __off_t;
__extension__ typedef long long int __off64_t;
extern void flockfile (FILE *__stream) ;
extern int ftrylockfile (FILE *__stream) ;
extern void funlockfile (FILE *__stream) ;
int main(int argc, char **argv)
{
printf(“Hello World\n”);
return 0;
}
my_program
Compile
Preprocessing
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{
Preprocess
printf(“Hello World\n”);
return 0;
}
__extension__ typedef unsigned long long int __dev_t;
In Preprocessing, source code is “expanded” into a
__extension__ typedef
__extension__ typedef
unsigned int
unsigned int
__uid_t;
__gid_t;
larger form that is simpler for the compiler to
__extension__ typedef
__extension__ typedef
unsigned long int
unsigned long long int
__ino_t;
__ino64_t;
understand. Any line that starts with ‘#’ is a line that is
__extension__ typedef
__extension__ typedef
unsigned int
long int
__nlink_t;
__off_t;
interpreted by the Preprocessor.
__extension__ typedef long long int __off64_t;
extern void flockfile (FILE *__stream) ;
extern int ftrylockfile (FILE *__stream)
extern void funlockfile (FILE *__stream)
;
;
• Include files are “pasted in” (#include)
int main(int argc, char **argv)
{
• Macros are “expanded” (#define)
printf(“Hello World\n”);
return 0;
• Comments are stripped out ( /* */ , // )
}
• Continued lines (i.e. very long lines ) are joined ( \ )
my_program
Compile
Compiling
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{
Preprocess
printf(“Hello World\n”);
return 0;
}
__extension__ typedef unsigned long long int __dev_t;
__extension__ typedef unsigned int __uid_t; • The compiler then converts the resulting text into
__extension__ typedef unsigned int __gid_t;
__extension__ typedef unsigned long int __ino_t; binary code the CPU can run directly.
__extension__ typedef unsigned long long int __ino64_t;
__extension__ typedef unsigned int __nlink_t; • The compilation process involves really several
__extension__ typedef long int __off_t;
__extension__ typedef long long int __off64_t; steps:
extern void flockfile (FILE *__stream) ;
extern int ftrylockfile (FILE *__stream) ; • Compiler: high level language assembly
extern void funlockfile (FILE *__stream) ;
int main(int argc, char **argv) • Assembler: assembly machine code
{
printf(“Hello World\n”); • Linker: links all machine code files and
return 0;
} needed libraries into one executable file.
• When you type gcc you really invoke the compiler,
assembler, and linker.
my_program
Compile
What is “Memory”?
• Is like a big table of numbered slots. Addr Value
• Each slot stores a byte. 0
1
• The number of a slot is its Address. 2
• One byte Value can be stored in each slot. 3
4 ‘H’ (72)
Some “logical” data values span more than one
5 ‘e’ (101)
slot, like the character string “Hello\n”
6 ‘l’ (108)
A Type names a logical meaning to a span of 7 ‘l’ (108)
memory. Some simple types are: 8 ‘o’ (111)
a single character (1 slot) 9 ‘\n’ (10)
char
char [10] an array of 10 characters 10 ‘\0’ (0)
int signed 4 byte integer
11
float 4 byte floating point
12
What is a Variable?
A Variable names a place in memory where you Symbol Addr Value
store a Value of a certain Type. 0
1
You first Define a variable by giving it a name 2
and specifying the type, and optionally an 3
initial value
x 4 ?
y 5 ‘e’ (101)
char x; Initial value of x is undefined
char y=‘e’; 6
7
Initial value The compiler puts them 8
somewhere in memory. 9
Name
10
Type is single character (char) 11
12
Multi-byte Variables
Different types consume different amounts of Symbol Addr Value
memory. Most architectures store data on 0
“word boundaries”, or even multiples of the 1
size of a primitive data type (int, char)
2
3
char x;
char y=‘e’; x 4 ?
int z = 0x01020304; y 5 ‘e’ (101)
6
0x means the constant is padding
written in hex 7
z 8 4
9 3
An int consumes 4 bytes
10 2
11 1
12
Scope
void p(char x)
Every Variable is Defined within some scope. A {
Variable cannot be referenced from outside of that char y;
scope.
char z;
}
Scopes are defined with curly braces { }.
char z;
The scope of Function Arguments is the
void q(char a)
complete body of the function. {
char b;
The scope of Variables defined inside a
{
function starts at the definition and ends at the char c;
closing brace of the containing block }
char d;
The scope of Variables defined outside a
}
function starts at the definition and ends at the
end of the file. Called Global Vars.
Now that we know about
variables, let’s combine them to
form expressions!
Expressions and Evaluation
Expressions combine Values using Operators, according to precedence.
1 + 2 * 2 1 + 4 5
(1 + 2) * 2 3 * 2 6
Comparison operators are used to compare values.
In C, 0 means “false”, and any other value means “true”.
int x=4;
(x < 5) (4 < 5) <true>
(x < 4) (4 < 4) 0
((x < 5) || (x < 4)) (<true> || (x < 4)) <true>
Not evaluated because
first clause was true
Precedence
• Highest to lowest
• ()
• *, /, %
• +, -
Comparison and Mathematical Operators
== equal to Beware in division:
< less than
<= less than or equal
• If second argument is integer, the
> greater than result will be integer (rounded):
>=
!=
greater than or equal
not equal
5 / 10 0 whereas 5 / 10.0 0.5
&& logical and
|| logical or
! logical not
+ plus & bitwise and Don’t confuse & and &&..
- minus | bitwise or
* mult ^ bitwise xor 1 & 2 0 whereas 1 && 2 <true>
/ divide ~ bitwise not
% modulo << shift left
>> shift right
Assignment Operators
x = y assign y to x x += y assign (x+y) to x
x++ post-increment x x -= y assign (x-y) to x
++x pre-increment x x *= y assign (x*y) to x
x-- post-decrement x x /= y assign (x/y) to x
--x pre-decrement x x %= y assign (x%y) to x
Note the difference between ++x and x++:
int x=5; int x=5;
int y; int y;
y = ++x; y = x++;
/* x == 6, y == 6 */ /* x == 6, y == 5 */
Don’t confuse = and ==
int x=5; int x=5;
if (x==6) /* false */ if (x=6) /* always true */
{ {
/* ... */ /* x is now 6 */
} }
/* x is still 5 */ /* ... */
Functions
What is a Function?
A Function is a series of instructions to run.
You pass Arguments to a function and it returns a Value.
“main()” is a Function. It’s only special because it always
gets called first when you run your program.
Return type, or void
Function Arguments
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{ “printf()” is just another function, like main().
printf(“Hello World\n”); It’s defined for you in a “library”, a collection
of functions you can call from your program.
return 0;
}
Returning a value
A More Complex Program: pow
#include <stdio.h>
“if” statement #include <inttypes.h>
float pow(float x, uint32_t exp)
/* if evaluated expression is not 0 */ {
if (expression) { /* base case */
/* then execute this block */ if (exp == 0) {
} return 1.0;
else { }
/* otherwise execute this block */
} /* “recursive” case */
return x*pow(x, exp – 1);
}
int main(int argc, char **argv)
{
Tracing “pow()”: float p;
• What does pow(5,0) do? p = pow(10.0, 5);
printf(“p = %f\n”, p);
• What about pow(5,1)? return 0;
}
The “Stack”
#include <stdio.h>
Recall scoping. If a variable is valid “within the #include <inttypes.h>
scope of a function”, what happens when you float pow(float x, uint32_t exp)
call that function recursively? Is there more than {
/* base case */
one “exp”? if (exp == 0) {
return 1.0;
}
Yes. Each function call allocates a “stack frame”
/* “recursive” case */
where Variables within that function’s scope will return x*pow(x, exp – 1);
reside. }
int main(int argc, char **argv)
{
float x 5.0 float p;
uint32_t exp 0 Return 1.0 p = pow(5.0, 1);
printf(“p = %f\n”, p);
float x 5.0 return 0;
}
uint32_t exp 1 Return 5.0
int argc 1
char **argv 0x2342
float p undefined
5.0
Grows
25
The “for” loop
The “for” loop is just shorthand for this “while” loop structure.
float pow(float x, uint exp) float pow(float x, uint exp)
{ {
float result=1.0; float result=1.0;
int i; int i;
i=0; for (i=0; (i < exp); i++) {
while (i < exp) { result = result * x;
result = result * x; }
i++; return result;
} }
return result;
} int main(int argc, char **argv)
{
int main(int argc, char **argv) float p;
{ p = pow(10.0, 5);
float p; printf(“p = %f\n”, p);
p = pow(10.0, 5); return 0;
printf(“p = %f\n”, p); }
return 0;
}
When to Use
Different Loop-constructs
• while
• do-while
• for
When to Use
Conditions
• if-else
• switch-case
Very strong but dangerous
concept!
Can a function modify its
arguments?
What if we wanted to implement a function pow_assign() that
modified its argument, so that these are equivalent:
float p = 2.0; float p = 2.0;
/* p is 2.0 here */ /* p is 2.0 here */
p = pow(p, 5); pow_assign(p, 5);
/* p is 32.0 here */ /* p is 32.0 here */
Would this work?
void pow_assign(float x, uint exp)
{
float result=1.0;
int i;
for (i=0; (i < exp); i++) {
result = result * x;
}
x = result;
}
NO!
Remember the stack!
void pow_assign(float x, uint exp)
{
float result=1.0; In C, all arguments are passed
int i;
for (i=0; (i < exp); i++) {
as values
result = result * x;
}
x = result;
} But, what if the argument is
main()
the address of a variable?
{
float p=2.0;
pow_assign(p, 5);
}
float x 2.0
32.0
uint32_t exp 5
float result 1.0
32.0
float p 2.0 Grows
Passing Addresses
Symbol Addr Value
0
1
2 address of x 4
3 memory content at address 4 72
char x 4 ‘H’ (72)
char y 5 ‘e’ (101)
6
7
8
9
10
11
12
“Pointers”
This is exactly how “pointers” work.
A “pointer type”: pointer to char
void f(char * p)
{
• address of x: &x *p = *p - 32;
• if y is an address, the }
content of the memory at
that address *y char y = 101; /* y is 101 */
f(&y); /* i.e. f(5) */
/* y is now 101-32 = 69 */
Pointers are used in C for many other purposes:
• Passing large objects without copying them
• Accessing dynamically allocated memory
• Referring to functions
Pointer Validity
A Valid pointer is one that points to memory that your program controls.
Using invalid pointers will cause non-deterministic behavior, and will often
cause Linux to kill your process (SEGV or Segmentation Fault).
There are two general causes for these errors:
• Program errors that set the pointer value to a strange number
• Use of a pointer that was at one time valid, but later became invalid
Will ptr be valid or invalid?
char * get_pointer()
{
char x=0;
return &x;
}
main()
{
char * ptr = get_pointer();
*ptr = 12; /* valid? */
}
Answer: Invalid!
A pointer to a variable allocated on the stack becomes invalid when that
variable goes out of scope and the stack frame is “popped”. The pointer will
point to an area of the memory that may later get reused and rewritten.
char * get_pointer()
{
char x=0;
return &x;
} But now, ptr points to a
main()
{
location that’s no longer in use,
char * ptr = get_pointer(); and will be reused the next time
*ptr = 12; /* valid? */
other_function(); a function is called!
}
101 charaverage
int x Return
0 101
12
456603
100 char * ptr 101
? Grows
Now that we know pointers (I hope!),
let’s go back to types.
More on Types
We’ve seen a few types at this point: char, int, float, char *
Types are important because:
• They allow your program to impose logical structure on memory
• They help the compiler tell when you’re making a mistake
In the next slides we will discuss:
• How to create logical layouts of different types (structs)
• How to use arrays
• How to parse C type names (there is a logic to it!)
• How to create new types using typedef
Structures
• a collection of related data items
• possibly of different types
• defined using the keyword struct
• The members of a struct type variable
are accessed with the dot (.) operator:
– <struct-variable>.<member_name>;
struct basics
• Definition of a structure:
struct <struct-type>{
Each identifier
<type> <identifier_list>;
<type> <identifier_list>;
defines a member
... of the structure.
} ;
struct basics
• Example: main()
{
struct Address { Example struct Address adrs;
int zip; …
char street[50]; [Link] = 10012;
char city[20]; }
} ;
Example of
initializing a
structure
struct Address adrs = {10012, “Mercer”, “New York”};
Arrays
Arrays in C are composed of a particular type, laid out in memory in a
repeating pattern. Array elements are accessed by stepping forward in
memory from the base of the array by a multiple of the element size.
/* define an array of 10 chars */ Brackets specify the count of elements. Initial
char x[5] = {‘t’,’e’,’s’,’t’,’\0’};
values optionally set in braces.
/* accessing element 0 */
x[0] = ‘T’;
Arrays in C are 0-indexed (here, 0..9)
/* pointer arithmetic to get elt 3 */
char elt3 = *(x+3); /* x[3] */ x[3] == *(x+3) == ‘t’ (NOT ‘s’!)
/* x[0] evaluates to the first element;
* x evaluates to the address of the
Symbol Addr Value
* first element, or &(x[0]) */
char x [0] 100 ‘t’
/* 0-indexed for loop idiom */
#define COUNT 10 char x [1] 101 ‘e’
char y[COUNT]; For loop that iterates from char x [2] 102 ‘s’
int i;
for (i=0; i<COUNT; i++) { 0 to COUNT-1. char x [3] 103 ‘t’
/* process y[i] */ Memorize it!
printf(“%c\n”, y[i]); char x [4] 104 ‘\0’
}
Pointers and Arrays in C
• An array name by itself is an address, or
pointer in C.
• When an array is declared, the compiler
allocates sufficient space beginning with
some base address to accommodate
every element in the array.
• The base address of the array is the
address of the first element in the
array (index position 0).
– Example: int num[10];
&num[0] is the same as num
Pointers and Arrays in C
• Suppose we define the following array and
pointer:
int a[100]; int *ptr;
Assume that the system allocates memory bytes
400, 404, 408, ..., 796 to the array. Recall that
integers are allocated 32 bits = 4 bytes.
– The two statements: ptr = a; and ptr = &a[0]; are
equivalent and would assign the value of 400 to ptr.
• Pointer arithmetic provides an alternative to
array indexing in C.
– The two statements: ptr = a + 1; and ptr = &a[1];
are equivalent and would assign the value of 404 to
ptr.
Pointers and Arrays in C
• Assuming the elements of the array
have been assigned values, the following
code would sum the elements of the
array:
sum = 0;
for (ptr = a; ptr < &a[100]; ++ptr)
sum += *ptr;
• Here is another way to sum the array:
sum = 0;
for (i = 0; i < 100; ++i) a[b] in C is just syntactic sugar
sum += *(a + i); for
*(a + b)
Strings
• Series of characters treated as a single
unit
• Can include letters, digits, and certain
special characters (*, /, $)
• String literal (string constant) - written in
double quotes
– "Hello"
• Strings are arrays of characters
• Example:
– char name[] = “test”;
– address of the above string can be expressed
in two ways:
• &name[0]
• name
Strings
• String declarations
– Declare as a character array or a variable of type char *
char color[] = "blue";
char *colorPtr = "blue";
– Remember that strings represented as character arrays end with
'\0'
• color has 5 elements
• Inputting strings
– Use scanf
scanf("%s", word);
• Copies input into word[], which does not need & (because a string
is a pointer)
– Remember to leave space for '\0'
Character Handling Library
• In <ctype.h>
Prototype Description
int isdigit( int c ) Returns true if c is a digit and false otherwise.
int isalpha( int c ) Returns true if c is a letter and false otherwise.
int isalnum( int c ) Returns true if c is a digit or a letter and false otherwise.
int isxdigit( int c ) Returns true if c is a hexadecimal digit character and false otherwise.
int islower( int c ) Returns true if c is a lowercase letter and false otherwise.
int isupper( int c ) Returns true if c is an uppercase letter; false otherwise.
int tolower( int c ) If c is an uppercase letter, tolower returns c as a lowercase letter. Otherwise, tolower
returns the argument unchanged.
int toupper( int c ) If c is a lowercase letter, toupper returns c as an uppercase letter. Otherwise, toupper
returns the argument unchanged.
int isspace( int c ) Returns true if c is a white-space character—newline ('\n'), space (' '), form feed
('\f'), carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v')—and
false otherwise
int iscntrl( int c ) Returns true if c is a control character and false otherwise.
int ispunct( int c ) Returns true if c is a printing character other than a space, a digit, or a letter and false
otherwise.
int isprint( int c ) Returns true value if c is a printing character including space (' ') and false
otherwise.
int isgraph( int c ) Returns true if c is a printing character other than space (' ') and false otherwise.
Each function receives a character (an int) or EOF as an argument
String Conversion Functions
• in <string.h>
• Conversion functions
– In <stdlib.h> (general utilities library)
– Convert strings of digits to integer and floating-
point values
Prototype Description
double atof( const char *nPtr ) Converts the string nPtr to double.
int atoi( const char *nPtr ) Converts the string nPtr to int.
long atol( const char *nPtr ) Converts the string nPtr to long int.
double strtod( const char *nPtr, char Converts the string nPtr to double.
**endPtr )
long strtol( const char *nPtr, char Converts the string nPtr to long.
**endPtr, int base )
unsigned long strtoul( const char *nPtr, Converts the string nPtr to unsigned
char **endPtr, int base ) long.
String Manipulation Functions
• String handling library has functions to
– Manipulate string data
– Search strings
– Determine string length
Func tio n p ro to typ e Func tio n d esc rip tio n
char *strcpy( char *s1, Copies string s2 into array s1. The value of s1 is
const char *s2 ) returned.
char *strncpy( char *s1, Copies at most n characters of string s2 into array
const char *s2, size_t n ) s1. The value of s1 is returned.
char *strcat( char *s1, Appends string s2 to array s1. The first character of
const char *s2 ) s2 overwrites the terminating null character of s1.
The value of s1 is returned.
char *strncat( char *s1, Appends at most n characters of string s2 to array
const char *s2, size_t n ) s1. The first character of s2 overwrites the
terminating null character of s1. The value of s1 is
returned.
String Manipulation Functions
int strcmp ( const char * str1,
const char * str2 )
return value indicates
the first character that does
<0 not match has a lower value in
ptr1 than in ptr2
the contents of both strings
0 are equal
the first character that does
>0 not match has a greater value
in ptr1 than in ptr2
How to Parse C Types
C type names are parsed by starting at the name and working outwards
according to the rules of precedence:
x is
an array of
pointers to
int
int *x[10];
int (*x)[10]; x is
a pointer to
an array of
int
Using typedef
At this point we have seen a few basic types, arrays, pointer types, and
structures. So far we’ve glossed over how types are named.
int x; /* int; */ typedef int T;
int *y; /* pointer to int; */ typedef int *U;
int z[10]; /* array of ints; */ typedef int V[10];
typedef defines a
int *k[10]; /* array of pointers to int; */ typedef int *W[10]; new type
int (*m)[10]; /* pointer to array of ints; */ typedef int (*N)[10];
Now:
T x; is the same as int x;
U y; is the same as int * y;
and so on …
What if you want to allocate an
array of N elements, and you
don’t know N beforehand?
Dynamic Memory Allocation
So far all of our examples have allocated variables statically by defining them
in our program. This allocates them in the stack.
But, what if we want to allocate variables based on user input or other
dynamic inputs, at run-time? This requires dynamic allocation.
sizeof() reports the size of a type in bytes
int * alloc_ints(size_t requested_count)
{ calloc() allocates memory for
int * big_array; N elements of size k
big_array = (int *)calloc(requested_count, sizeof(int));
if (big_array == NULL) {
printf(“can’t allocate %d ints: %m\n”, requested_count); Returns NULL if can’t alloc
return NULL;
}
/* now big_array[0] .. big_array[requested_count-1] are It’s OK to return this pointer. It
* valid and zeroed. */
return big_array; will remain valid until it is
} freed with free()
Dynamic Memory Allocation
• void *malloc (size_t size);
• void* calloc (size_t num, size_t size);
• void free (void* ptr);
• Unary operator sizeof is used to
determine the size in bytes of any data
type. Examples:
– sizeof(double)
– sizeof(int)
Caveats with Dynamic Memory
Dynamic memory is useful. But it has several caveats:
Whereas the stack is automatically reclaimed, dynamic allocations must be
tracked and free()’d when they are no longer needed. With every allocation, be
sure to plan how that memory will get freed. Losing track of memory is called a
“memory leak”.
Whereas the compiler enforces that reclaimed stack space can no longer be
reached, it is easy to accidentally keep a pointer to dynamic memory that has
been freed. Whenever you free memory you must be certain that you will not try
to use it again. It is safest to erase any pointers to it.
Because dynamic memory always uses pointers, there is generally no way for the
compiler to statically verify usage of dynamic memory. This means that errors
that are detectable with static allocation are not with dynamic
I/O in C
I/O
• reading from:
– standard input (usually the keyboard)
– file
• writing to:
– standard output (usually the screen)
– file
• A library of functions is supplied to
perform these operations.
• The I/O library functions are listed the
header file <stdio.h>.
Writing to stdout
printf ( ) ;
• This function provides for formatted output to
the screen. The syntax is:
printf ( “format”, var1, var2, … ) ;
• The “format” includes a listing of the data
types of the variables to be output and,
optionally, some text and control character(s).
• Example:
float a ; int b ;
scanf ( “%f%d”, &a, &b ) ;
printf ( “You entered %f and %d \n”, a, b ) ;
Formatted Output with printf
Format Conversion Specifiers (This list is
not exhaustive):
d -- displays a decimal (base 10) integer
l -- used with other specifiers to indicate a long
f -- displays a floating point value
x -- displays a number hexadecimal format
c -- displays a single character
s -- displays a string of characters
Reading from stdin
scanf ( ) ;
• This function provides for formatted input
from the keyboard. The syntax is:
scanf ( “format” , &var1, &var2, …) ;
• The “format” is a listing of the data types of
the variables to be input and the & in front
of each variable name tells the system
WHERE to store the value that is input. It
provides the address for the variable.
• Example:
float a; int b;
scanf (“%f%d”, &a, &b);
Files
• In C, each file is simply a sequential
stream of bytes.
• C imposes no structure on a file.
• Steps to deal with files
– open a file
– check that the open was successful
– read/write to a file
– close a file
First step
• Declaration:
FILE *fptr1, *fptr2 ;
Opening Files
• The statement:
fptr1 = fopen ( “filename", "r" ) ;
would open the file filename for input
(reading).
– r: read
– w: write
– a: append
– … there are some more
Testing for Successful Open
• If the file was not able to be opened, then
the value returned by the fopen routine is
NULL.
• For example, let's assume that the file
mydata does not exist. Then:
FILE *fptr1 ;
fptr1 = fopen ( "myfile", "r") ;
if (fptr1 == NULL)
{
printf ("File 'mydata' did not open.\n") ;
}
Reading From Files
• In the following segment of C language
code:
int a, b ;
FILE *fptr1, *fptr2 ;
fptr1 = fopen ( "mydata", "r" ) ;
fscanf ( fptr1, "%d%d", &a, &b) ;
the fscanf function would read values from
the file "pointed" to by fptr1 and assign
those values to a and b.
End of File
• The end-of-file indicator informs the program when
there are no more data (no more bytes) to be
processed.
• There are a number of ways to test for the end-of-file
condition. One is to use the feof function which
returns a true or false condition:
fscanf (fptr1, "%d", &var) ;
if ( feof (fptr1) )
{
printf ("End-of-file encountered.\n”);
}
• Another (better) way of testing EOF:
while(fscanf(fp,"%d ", ¤t) == 1)
{
}
Writing To Files
int a = 5, b = 30;
FILE *fptr2 ;
fptr2 = fopen ( “filename", "w" ) ;
fprintf ( fptr2, "%d %d\n", a, b ) ;
the fprintf functions would write the
values stored in a and b to the file
"pointed" to by fptr2.
Closing Files
fclose ( fptr1 ) ;
Once the files are open, they stay open
until you close them or end the program
(which will close all files.)
One last concept …
Macros
Macros can be a useful way to customize your interface to C and make your
code easier to read and less redundant. However, when possible, use a static
inline function instead.
/* Macros are used to define constants */
#define FUDGE_FACTOR 45.6 Float constants must have a decimal
#define MSEC_PER_SEC 1000 point, else they are type int
#define INPUT_FILENAME “my_input_file”
/* Macros are used to do constant arithmetic */
#define TIMER_VAL (2*MSEC_PER_SEC) Put expressions in parens.
/* Macros are used to capture information from the compiler */
#define DBG(args...) \
do { \ Multi-line macros need \
fprintf(stderr, “%s:%s:%d: “, \
__FUNCTION__, __FILE__, __LINENO__); \ args… grabs rest of args
fprintf(stderr, args...); \
} while (0)
Enclose multi-statement macros in do{}while(0)
/* ex. DBG(“error: %d”, errno); */
Conclusions
• We took a quick look at the different
features of C
• To get deeper look: check online
tutorials. You will find some links at the
course webpage
• To become an expert: write code …
write code … write code