CSE342: Fundamentals of Internetworking
C Programming Review
Prof. Brian D. Davison
Computer Science & Engineering, Lehigh University
Accessing the Suns
- Use your ssh client to connect to a CSE/ECE
Sun
- Lehigh students can get a number of ssh-compatible implementations
here for
various operating systems.
- If you have forgotten your CSE/ECE Sun account password, or
don't yet have an account, send
mail to help (at) cse.lehigh.edu
Accessing the Suns
- On our Suns (and Linux) your default shell is bash
(Bourne-Again Shell). Most shells, including bash, offer:
- tab completion -- just type the first few letters and press tab to complete the command name
- command history -- use the up and down arrows to select from your history of past commands
- emacs-like keybindings -- C-a, C-e, C-t, C-b, C-f, C-p, C-n all same
- When finished, type exit to close the shell.
Demonstrate use of ssh.
Hello World
- What does a C version of hello world look like?
- Use an editor to create hello.c
- Compile using gcc hello.c
- Assuming no errors, run using ./a.out
- If no errors, the program will output its greeting and finish.
- How about a version that also says "This is day X of the semester."
where X is a variable set earlier?
GNU Emacs
- One of, if not the, most popular and full-featured editors
- Available for most every platform/OS
- More than just an editor; it can also read email, browse web, etc.
- Start emacs by typing emacs (and return) at a shell prompt
- If you are running within X-windows, a new window will open
- Otherwise, emacs will run full-screen (within the starting terminal)
- Menus are available (use F10 to operate if not running within X-Windows)
Operating Emacs
- Emacs provides many, many keyboard shortcuts (as well as longer commands)
- Some combinations of keystrokes have special meanings
- C-x (spoken as "control x") means to press and hold the control key while you press the x key once
- Example command sequence: C-x C-f will prompt you for the name of a file to open
Essential Commands
- Quitting Emacs
- C-x C-c -- Exit emacs permanently
- C-z -- Suspend emacs (iconify under X)
- File Commands
- C-x C-f -- Open a file for editing
- C-x C-s -- Save current file to disk
- C-x i -- Insert contents of another file
- C-x C-w -- Write/Save-As
- C-x k -- Close current file
- Handling Errors
- C-g -- Aborts the current command
- C-_ or C-x u -- Undoes most recent change
Essential Commands
- Searching
- C-s -- Search forward
- RET -- Abort current search at current location
- Repeated C-s or C-r will search for next location
- Cutting and Pasting
- Backspace (and sometimes C-Backspace) will usually delete letter to left
- C-d (and often Delete key) will delete letter under cursor
- C-k -- Kill to end of line (i.e., cut)
- C-y -- Yank from kill buffer (i.e., paste)
- A sequence of repeated C-k will put all such lines in the same buffer
- Miscellaneous
- TAB -- Indent current line (depending on mode)
- C-x 1 -- delete all other windows within emacs
Files Created By Emacs
- Emacs will let you open new files
- If you close a file that was modified (without saving), it leaves a temporary file called #filename#
- If you edit and save an existing file, emacs renames the old file as filename~
Using UNIX
- Reminder: How to find information about UNIX commands/utilities?
Using UNIX
- Reminder: How to find information about UNIX commands/utilities?
- Type man <program> for any system command, most
utilities and system calls
- Type info <program> for any GNU utility or program (and man pages!)
- Check your reference books
- The rest: Google, instructors, etc.
- What if you don't know the name of the program?
info is a subset of emacs; info info to get started; man man; man is better than Google -- specific to your OS and installation
Note the man page sections for the system calls.
Use man -s to specify which section of the manual pages to search.
Using UNIX Shells
- When you run a program, e.g., ls or emacs, it
typically takes the place of the shell, and returns you to the shell when
it is finished.
- How can you stop the program if it is not running properly?
- How can you make it run in the background (so that you can continue to
use the shell)?
- How can you capture the output of a program to a file?
- How do you feed the contents of a file as input to a program?
- How do you take the output of a program and use it as the input to a
different program?
Finished Using UNIX...
Any questions?
Let's move on to C
The C Programming Language
- is low-level
- C can directly access structures that are tied to hardware
- is structured
- Structures are used to control the flow of execution (e.g., while,
for, if/then)
- is procedural
- can re-use code in subroutines (but not object-oriented)
- is weakly typed
- You can read data from memory as any type you like
- is staticly typed
- Types are only checked at compile-time
C versus C++
- C++ is mostly a superset of C
- So what are the major changes?
Do the students know?
Major C differences from C++
- No classes or objects -- all code is in functions
- C structures cannot have methods
- I/O in C is based on library functions
- No function overloading
- No new or delete (use library functions instead)
- No reference variables (aliases)
Other Differences
- Variables must be declared at the beginning of a block, typically at the beginning of the function
- This is no longer true for C99
- No bool datatype
- No << and >> operators for I/O
- Cannot substitute and, or, and not for boolean operators &&, ||, and !
- Different approach to strings
So what stays the same? Glad you asked...
Basic C Data Types
- Pretty much the same as in C++
- int (%d, -40), short int, long int, long long int, unsigned int
- float (%f, 3.14), double, long double
- char (%c, 'a'), unsigned char, signed char
- Variable names are also similar to C++
- made of letters, digits, underscore
- cannot start with a number
int is signed, 32 bits on typical (but not all) platforms. float typically 32bits, double typically 64, long double 128
Operators
As always, multiplication and division have precedence over addition/subtraction; use parenthesis to make things clear
Assignment Operators
- Assignment = (e.g., a=4;)
- Modify and assign
- Increment ++ and Decrement -- (e.g., a++; --b;)
- Combinations *=, /=, %=, +=, -=, &=, ^=, |=, <<=, >>=
a += 4;
b <<= 1;
Type Conversions
Quick test: what is the result of 1.0 + 3/2?
Control Flow
while (expression)
statement
for (expr1; expr2; expr3)
statement
do
statement
while (expression);
The
break statement drops control out of the innermost loop while
continue moves on to the next iteration.
Control Flow
if (expression)
statement1
else
statement2
switch (expression) {
case const-expression: statements
case const-expression: statements
default: statements
}
C Arrays
- Assuming we know the number of elements, we can easily create and
manipulate arrays of items.
int i[5];
i[0] = i[1] = 1;
i[2] = 2;
i[i[2] + 1] = i[2] + 2;
i[i[2] + i[2]] = i[3] + i[2];
- Unfortunately, we cannot set all values of an array at once, nor assign one array to another. Instead, we must iterate through each item.
int i, a[10];
// clear or copy all values
for ( i = 0; i++; i < 10 ) {
a[i] = 0; // or a[i] = x[i];
}
Character Arrays
- An array of characters works the same. We can create and initialize them with the same syntax.
char hello[] = {'h', 'e', 'l', 'l', 'o', '!'};
- There are many characters that are not letters, numbers, or keyboard symbols. Such characters are represented by an escape sequence using the backslash.
Common characters include:
\n a "newline" character (e.g., a line feed)
\b a backspace
\r a carriage return (without a line feed)
\' a single quote (e.g., in a character constant)
\" a double quote (e.g., in a string constant)
\\ a single backslash
- Character constants always use single quotes.
C Strings
- A string constant is zero or more characters enclosed in double quotes.
printf("Hello world.\n");
- Strings in C are always terminated internally by a null
character ('\0').
char word[] = { "Hello!" };
char word[7] = "Hello!";
char word[] = "Hello!";
char word[] = { 'H', 'e', 'l', 'l', 'o', '!', '\0' };
printf("She said %s\n", word);
Let's write code to concatenate two strings. Here is a skeleton that we can fill out.
Single quote for char constants, double quotes for strings. Must be null terminated. What is a null?
Using strings in C
- Often you'll need to know how long a string is
- E.g., to see if a copy of it will fit into a destination buffer
- You can call strlen(3c),
which returns the string length (i.e., the number of
characters in it), not including the terminating null:
char string7[] = "abc";
int len = strlen(string7);
printf("%d\n", len);
Which might be implemented as
int mystrlen(char str[]) {
int i;
for (i = 0; str[i]; i++) ;
return i;
}
- We can print strings using printf() with the %s format
(e.g., printf("%s\n", string5);).
C libraries use structs
- Consider acquiring the current time.
- The time(2) call returns the
number of seconds since the UNIX epoch (Jan 1, 1970).
- The localtime(3c) function converts that to a struct:
struct tm {
int tm_sec; /* seconds after the minute - [0, 61] */
/* for leap seconds */
int tm_min; /* minutes after the hour - [0, 59] */
int tm_hour; /* hour since midnight - [0, 23] */
int tm_mday; /* day of the month - [1, 31] */
int tm_mon; /* months since January - [0, 11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0, 6] */
int tm_yday; /* days since January 1 - [0, 365] */
int tm_isdst; /* flag for daylight savings time */
};
Pointers
- With pointers, we can manipulate addresses or contents referenced by the addresses.
- We can first declare a pointer variable
int *ip;
Which tells the compiler that variable
ip is of type
(int *) or equivalently that
*ip is of type
int.
Pointers
int *ip;
Pointers contain addresses (Note that ip above is currently
undefined).
We can set ip to the address of another variable easily, as in
int i = 5;
ip = &i;
At this point, the contents of ip and the address of i
are
the same -- they both refer to the same memory location, which contains the number 5.
Parameters in C Functions
What is call-by-value? Show example prog8-10.c
Pointer Declarations
- As with any other variable types, you can initialize the value of a pointer variable when you declare it, as in
int *ip = &i;
but you cannot initialize the value of the memory location to which it points,
as something like
int *ip = 5;
will only tell the compiler to use address 5 as the initial value for ip (and the contents of address 5 are undefined, and probably off-limits to your program anyway).
Pointer Declarations
- While the compiler thinks these are equivalent:
int *j;
int* j;
the latter leads to possible problems later, such as writing
int* i,j;
when you wanted two pointers.
Pointer arithmetic
- In addition to single variables, pointers can be used to access parts of an array.
int *ip;
int a[20];
ip = &a[3];
Given that ip points to element 3 of a, we can use pointer arithmetic to access elements before or after 3, as in
ip++;
*ip = 7;
*(ip+1) = 8;
*(ip-2) = 3;
which sets element 4 to 7, element 5 to 8 and element 2 to 3.
String operations using pointers
- mystrcmp() using pointers
char *p1 = &str1[0], *p2 = &str2[0];
while(1) {
if (*p1 != *p2)
return *p1 - *p2;
if (*p1 == '\0' || *p2 == '\0')
return 0;
p1++;
p2++;
}
mystrcpy() using pointers
char *dp = &dest[0], *sp = &src[0];
while (*sp != '\0')
*dp++ = *sp++;
*dp = '\0';
Null pointers
- A null pointer is a special value that is known to not point anywhere.
Such a pointer is never valid.
- One way to get a null pointer is to use the constant NULL:
#include <stdio.h>
int *ip = NULL;
and then you can test the value of ip to see if it is a valid pointer, as in
if (ip != NULL)
printf("%d\n", *ip);
NULL is implemented as a macro for the number 0, much like the
null character
'\0' is also the number 0, but is of type
#define NULL (void *)0
Null pointers
- Null pointers are useful as markers to say that the pointer is not ready for use, or for failure when you would otherwise return a valid pointer.
- For example, the strstr(3c) function returns a pointer to the first occurrence of one string within another string, but returns a null if not found.
- Also helps prevent the use of uninitialized pointers (e.g., those with undefined values) which can cause unrepeatable problems.
Pointers and arrays
- It turns out that pointers and arrays have much in common.
int a[10];
int *ip;
ip = a;
It is as if we had written ip = &a[0];
We can also use array subscripting with pointers. E.g.,
ip[3] == *(ip+3) == a[3];
is also valid and evaluates to true.
This is how the compiler lets us pass arrays as parameters!
- A function call: myfunc(a,10) is actually myfunc(&a[0],10)
- Similarly, the definition void myfunc(int array[], int size) is treated as if it had been void myfunc(int *array, int size) since later uses of array[x] are still permitted when array is a pointer.
Fun, eh?
Strings as pointers
- Since arrays and pointers can be used interchangeably, it is common to refer to and manipulate character pointers as strings.
- This means:
- Any function declared to take a string (char array), will also accept a character pointer, since even if an array is passed, the function actually receives as pointer to the first element of the array.
- printf's %s actually expects a character pointer.
- Many programs extensively manipulate strings as character pointers and never explicitly declare any actual arrays.
Strings as pointers
- One caveat in initialization, however.
char string1[] = "Hello 1";
char *string2 = "Hello 2";
string1[0] = 'J';
string2[0] = 'J';
The first assignment is fine; the second may crash! The first declaration created an array with the initial contents of "Hello 1". The second declaration created a pointer to a string constant, which might be placed in an area of memory that is read-only.
Static vs. Dynamic Allocation
- "Static" allocation of space is straightforward, with variables or arrays declared locally or globally.
char myarray[1000];
char *ptr=0;
for (ptr=myarray; (ptr-myarray) < 1000; ptr++)
// do something with each entry of myarray
Dynamic allocation asks the OS for space for the pointer to access
#include <stdlib.h>
char *myarray=0, *ptr=0;
myarray = (char *)malloc(1000); // ask for 1000 Byte block
for (ptr=myarray; (ptr-myarray) < 1000; ptr++)
// do something with each entry of myarray
Note the cast from malloc() -- this is because malloc returns void *
Why dynamic allocation?
- Dynamic allocation (e.g., with malloc(3c)is
- necessary when a function returns a pointer to
a structure created by the function (not just passed in)
struct myobj *create_object(void);
- useful when you don't know the size of the allocation at compile time
#include <stdio.h>
int numobjs;
int ret;
ret = scanf("%d", &numobjs);
// check return value for error
Potential Problem
int myfunc(void) {
int *ptr = (int *)malloc(10000 * sizeof(int));
if (!ptr)
exit(-1);
[some computation using space in ptr]
return ptr[0];
}
Important to realize that allocated space persists, even if no pointers point to it (C does not have automatic garbage collection)
- Forgetting this leads to "memory leaks", causing your program to
use more and more memory
Nothing "potential" here -- this is definitely a problem.
Returning memory
- Eventually, the space allocated via malloc may no longer be useful to your program. Perhaps
- the program is shutting down
- the pointer to the data is going away
- you want to be able to continue to allocate memory in the future
Returning memory
- Memory that has been malloc()ed is returned to the OS via free(3c)
#include <stdlib.h>
int *ip = (int *)malloc(1000 * sizeof(int));
...
free(ip);
ip = 0;
Note the null pointer assignment.
Note also that free() will succeed even if you
- free memory that was already free()d
- free random memory locations that were never malloc()ed
but either one will eventually cause your program to crash!
Functions that return pointers
- When using routines that return pointers, you must determine who is
responsible for the memory
- Possibilities include:
- Pointer is to a global value -- memory never needs to be free()d, but might get overwritten by later function calls (e.g., some networking code)
- Pointer is to a dynamically-allocated local structure that must be
destroyed/freed with another library call
- Pointer is to a dynamically-allocated block of memory that your code
must later free (e.g., strdup())
- Pointer is to a portion of some other block of memory that could later
move or be freed independently of this pointer
(e.g., strstr())
- In general, you need to know whether
- You are now responsible to free() this memory
- This memory might get overwritten in the future
Generally, either you need to free() or this is memory that you have no
control over...
User-Defined Structures
- We have previously mentioned structs, as in
struct complex {
double real;
double imag;
};
which defined a new type
struct complex.
We could also have declared some new variables of that type, in two ways:
struct complex {
double real;
double imag;
} var1, var2;
struct complex var3, var4;
Access to Structs
- We also saw that we could access contents using the period operator:
c1.real = c2.real + c3.real;
Pointers to structs work as expected:
struct complex *p1, *p2;
*p1 = *p2;
printf("%lf\n", (*p1).real);
Parentheses are needed. Could instead use -> operator:
printf("%lf\n", p1->real);
which is an equivalent (and common) shortcut.
User-defined types
- A struct is a user-defined type
- The typedef operator also permits us to use an alternate name
for a defined type. Thus,
typedef char *StringPtr;
typedef struct complex Complex;
StringPtr string;
Complex c1, c2;
would be equivalent to
char *string;
struct complex c1, c2;
We often define new type names
- for convenience
- to make the code more self-documenting
- to make it possible to change the actual base type used for a lot of variables without rewriting the declarations of all those variables