Cs 1101 C
Cs 1101 C
Cs 1101 C
Hugh Anderson
ii
Preface
The FOLDOC1 dictionary of computing denes C as: A programming language designed by Dennis Ritchie at AT&T Bell Labs ca. 1972 for systems programming on the PDP-11 and immediately used to reimplement Unix. C is often described, with a mixture of fondness and disdain, as "a language that combines all the elegance and power of assembly language with all the readability and maintainability of assembly language". And then continues to dene a (programming) methodology as: (a) An organised, documented set of procedures and guidelines for one or more phases of the software life cycle, such as analysis or design. Many methodologies include a diagramming notation for documenting the results of the procedure; a step-by-step "cookbook" approach for carrying out the procedure; and an objective (ideally quantied) set of criteria for determining whether the results of the procedure are of acceptable quality. An example is The Yourdon methodology. Or (b): A pretentious way of saying "method". This course attempts to teach some aspects of C programming, and programming methodology. At the end of the course a student should know many of the useful features of the C language, and be able to produce a program using professional programming techniques.
iii
iv
Throughout the lectures, I will try to follow four principal threads: 1. Programming in C, 2. Computer science, 3. Software engineering techniques and methods including good practice, and 4. Useful information The following four symbols are used throughout the notes, and indicate the four principal threads. The icons normally appear in the margins of the notes.
i=1 n
This one represents an informative section (history, how to use a tool and so on)
In addition, code listings, specications, algorithms and so on are consistently presented in framed, labelled boxes. The computer science content of this course is introduced in a gradual manner, leaving in-depth analysis until later. Thanks to Prof Tony Adams (USP, Fiji Islands) for his C++ notes, and useful ideas.
vi
Contents
1 Introduction 1.1 1.2 How to survive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.4 1.5 1.6 1.3.1 1.4.1 Assessment and grading . . . . . . . . . . . . . . . . . . . . . . . . . . . . People . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The course text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Course notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C compilers and other tools . . . . . . . . . . . . . . . . . . . . . . . . . . Ambiguity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 1 2 2 3 3 3 4 4 5 5 5 6 7 9 10 11 12 14 14 15 17 18 20
2 Programming 2.1 Hello Singapore - <printf> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 2.1.2 2.2 2.3 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Compile and execute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Specication of programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Further C language elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 2.3.2 2.3.3 Square - <scanf> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sum - <assign> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Summation - <for> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4
A useful tool - indent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . More C constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 3.2.2 3.2.3 3.2.4 The function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Compound-statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The while-loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3
Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Getting our feet wet 4.1 4.2 4.3 4.4 4.5 If-statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Parameters and return values . . . . . . . . . . . . . . . . . . . . . . . . . . The math library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Iteration and recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5 Bugs n stuff 5.1 Error-nding (debugging) tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.1 5.1.2 5.2 5.3
Manual pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basic rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 Reserved words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rules for legal identiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . The preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Headers vs header les . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Scope of names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Function prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6 Low ying data structures 6.1 Simple data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 Initializing variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Integer storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Size of simple types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The oat and double types . . . . . . . . . . . . . . . . . . . . . . . . . . . Boolean types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CONTENTS 6.1.6 Character storage - the char type . . . . . 6.1.7 Type coercion - cast . . . . . . . . . . . 6.1.8 Precedence of operators . . . . . . . . . Data structures - the array . . . . . . . . . . . . 6.2.1 Declaration and initialization of an array . 6.2.2 Two dimensional arrays . . . . . . . . . Data structures - struct . . . . . . . . . . . . . . Random numbers . . . . . . . . . . . . . . . . . Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ix 54 56 56 57 58 58 60 61 62 63 64 65 66 66 67 67 67 67 68 69 70 70 70 71 71 71 71 72 72 72 72 72 73 74 74 74 75 76
6.2
7 Pointers and strings 7.1 Pointers . . . . . . . . . . . . . . . . . . . . . . . 7.2 Strings . . . . . . . . . . . . . . . . . . . . . . . . 7.2.1 Sample string functions - string copy . . . 7.2.2 Sample string functions - string concatenate 7.2.3 Sample string functions - string compare . 7.2.4 Sample string functions - string length . . . 7.2.5 Sample string functions - print-to-string . . 7.3 Memory allocation . . . . . . . . . . . . . . . . . 7.4 Summary of topics . . . . . . . . . . . . . . . . . 8 Files n stuff 8.1 File input and output . . . . . . . . . . . . . . . . 8.1.1 Stream of characters vs structured . . . . . 8.1.2 Names . . . . . . . . . . . . . . . . . . . 8.2 The C stream le API . . . . . . . . . . . . . . . . 8.2.1 Create le - fopen() . . . . . . . . . . . . . 8.2.2 Close le - fclose() . . . . . . . . . . . . . 8.2.3 Read and write les - fprintf() and fscanf() 8.3 The C raw le API . . . . . . . . . . . . . . . . . 8.3.1 Create le - open() . . . . . . . . . . . . . 8.3.2 Close le - close() . . . . . . . . . . . . . 8.3.3 Read and write les - read(), write() . . . . 8.3.4 Control attributes - fcntl() . . . . . . . . . 8.4 Network programming . . . . . . . . . . . . . . . 8.5 More operators . . . . . . . . . . . . . . . . . . . 8.5.1 Boolean operators . . . . . . . . . . . . . 8.5.2 Increment and decrement operators . . . . 8.5.3 Operator assignments . . . . . . . . . . . . 8.6 Summary of topics . . . . . . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
CONTENTS 77 78 79 80 81 82 83 85 86 86 86 87 88 90 91 93 95 95 96 97 97 98 98 99
Bubble sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Insertion sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Shell sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Selection sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quick sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10 GUI programming 10.1 Window systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.1 Win32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.2 X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.3 Thin client systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Direct calls to the Win32 API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 OO GUI toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.4 Scripting languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.5 Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Arbor day 11.1 Array of pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.1 Parameters to programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2.1 Simple singly-linked list . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2.2 Sorted singly-linked list . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3 Binary tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3.1 Creating a node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.3.2 Insert node in tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 11.3.3 Find node in tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 11.4 Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 12 Building larger C programs 103
12.1 Revision control systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 12.2 Makele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 12.3 Congure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 12.4 Summary of topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Chapter
1
i
1
Introduction
Ofcially, the aim of this module is to
introduce students to the discipline of computing and to the problem solving process. The module stresses on good program design, and programming styles, and structured program development using a high-level programming language. Some basic concepts in procedural abstraction, structured programming and top-down design with stepwise renement will be introduced. Topics to be covered include: algorithm design process, program development/coding/debugging, programming concepts in a high-level language including program structure, simple data types and structured types, and various control structures (sequencing, loops, conditional, etc.), and linear data structures, such as arrays and linked-lists. The utility of recursion will also be highlighted using a variety of sorting algorithms. Laboratory work is essential in this course. Have you got that? That is the ofcial view, but I can see a different way of looking at this. From a student-oriented point of view, the aims could be: 1. to survive the semester, and 2. to learn some useful stuff about programming along the way. And to pass of course! In this rst chapter, we begin by giving some initial tips on how to survive this course, some relevant history about C, and we make some observations about programming in general.
Introduction Programming is a practical subject. You will need to write, type in, correct and run programs. Attending lectures, and hoping to pass the nal exam may work ne in some subject areas, but not this one. The good news is that you will be able to use your computer at home to develop your programming skills. Students who do well do the following things: attend laboratory classes as well as lectures and tutorials, start laboratory assignments as soon as they are given out, do the tutorial exercises, and seek help if things get difcult. When you get help from your friends or tutors, make sure that you understand it (for instance, if you have had help with an assignment you should be able to do it again on your own a couple of weeks later). Aaron Tan has assembled a range of documents related to surviving on the web site at http://www.comp.nus.edu.sg/cs1101c/5_tips/tips.html.
Laboratory work will be done using the CourseMaster automated C programming system, with a series of programming tasks that you may work on in the laboratories, or in your own time. At any time, you may use the CourseMaster interface to check your current assessment, and if you wish, re-submit to get a better score.
1.2 Resources
There are lots of resources available to you. There is no excuse that starts with I could not nd anything.... First there are people resources, information in the form of books and Internet sites, and at least three sets of course notes.
1.2 Resources
1.2.1 People
We all prefer to get our information from people. Even if it is wrong. We are people-kind-of-people. You can talk to: Colleagues: Your fellow students often have discovered things already. In a University, we encourage people to be free with their knowledge - so if you know something, share it with your colleagues. If you want to know something and the student on the next table seems to know, introduce yourself and ask. Tutor, Lab assistants: Should be knowledgable in many of the areas covered, and if they cant answer your questions, they will nd out! Lecturer: Ill try to make myself available at xed times. You may also e-mail me if you are still having problems after consulting your tutor. Woman you met in bus: Before accepting what she says, ask for her name. If it is Thompson or Ritchie...
1.2.2 Internet
Aaron Tan: has developed a wonderful web site here at NUS with lots of pointers to various resources related to both survival, programming methodology and C. BBS: The SoC BBS often has an active site devoted to cs1101c. Various: There are literally hundreds of learn C programming courses to be found on the Internet, with a range of quality levels. You may wish to look around at them. Announcements via e-mail: If I have announcements to make, they will be sent to you at your ofcial NUS e-mail address. You are advised to read your e-mail as often as you can. If you have an external account, ensure that your NUS mail is redirected there.
by H.M. Deitel & P.J. Deitel, Prentice-Hall, ISBN 0-13-089572-5. You are strongly urged to get the textbook. It is available at the Forum co-operative bookstore.
Introduction
1.3 C background
The C programming language was called "C" because many features derived from an earlier compiler named "B", which in turn was developed from BCPL. BCPL was a simpler version of a language called CPL, which owed many of its features to a language called Algol 60. The names most associated with this early development of C were Ken Thompson and Dennis Ritchie. Both were employed at Bell Telephone Laboratories and were the chief developers of the UNIX operating system. Ritchie has written a short history of C which you may nd interesting. It is found at http://cm.bell-labs.com/cm/cs/who/dmr/chist.html. UNIX has been distributed freely in the academic world since those early days, and since every UNIX system was written in C, and came with free C compilers, it became immensely popular. Even now, nearly 30 years after it was developed, C is still the main language used in systems and applications programming. The main features of C are: Simplicity - C has a relatively small number of components, although some programmers say it has too many! Efciency - C has commands that are directly oriented towards the low level instructions, and can often produce very fast and small code. When C was developed, computers typically had 8KB of memory (8192 bytes), and so code size efciency was a great concern. Portability - C and UNIX are available on virtually every platform. Brian Kernighan and Ritchie published the book The C Programming Language in 1978, effectively setting the standard for C. This standard became ANSI C after some modications. C itself has spawned other languages - Concurrent C, Objective C, and in 1986, C++. All of these languages accept most elements of ANSI C, but provide extra facilities.
i=1
1.4.1 Ambiguity
A rst problem is that of ambiguity. When we hear something, we interpret it differently according to context. For example: Stay away from the bank! This could be 1. A Dad telling off his 5 year-old son - meaning that there is a dangerous bank, or... 2. The Bank manager instructing her (ex) employee to stay well away from the Bank, or...
Introduction This sort of ambiguity is simple, and can be resolved fairly quickly by nding out what sort of bank is being referred to. There are more complex forms - for example consider this sentence: Woman without her man is nothing Does this mean 1. Woman, without her man, is nothing. - or - does it mean 2. Woman! Without her, man is nothing. The computer is too simple to deal with this level of structural ambiguity, and so we tend to use very specic computer languages which are insensitive to context. These languages are called context-free.
Questions
1. In what computer language were early C compilers rst written?In what computer language are C compilers written now? 2. What is UNIX? 3. What is ambiguous about each of the following sentences? What type of ambiguity is demonstrated? (a) (b) (c) (d) POLICE BEGIN CAMPAIGN TO RUN DOWN JAYWALKERS DRUNK GETS NINE MONTHS IN VIOLIN CASE SQUAD HELPS DOG BITE VICTIM MINERS REFUSE TO WORK AFTER DEATH
4. Devise a set of instructions to help someone read the time from an analog clock. Play devils advocate with your instructions, and nd an error. 5. What is ANSI? 6. What is GNU, and the GNU copyleft? 7. Find the sources for a C compiler, and count the lines in it. How did you do it? 8. Differentiate between a compiler and an interpreter
Further study
Textbook, chapter 1, sections 1.1 to 1.18 Textbook Chapter 2, sections 2.1 to 2.5 Self-review exercises, chapter 1 Aarons notes for Lecture 1 at http://www.comp.nus.edu.sg/cs1101c/lect/lect01color.doc
Introduction
Chapter
i=1 n
Programming
At this stage, a distinction is made between an algorithm, and code.
An algorithm is a set of instructions that describe a method for solving a problem. An algorithm is normally given in some mix of computer code and English. This is often called pseudo-code. When an algorithm is discussed in this text it will look like the following example:
A code-listing (program,procedure,function,method) is an actual implementation of some algorithm. It can be compiled and executed, either by itself - as in most examples in this text, or in conjunction with other code-listings. When code is discussed in this text it will look like the following example:
HelloSingapore.c
CODE LISTING
/* ****************************************************************** Author: Hugh Anderson Date: 01/11/2000 Description: This is an initial Hello World program Revisions: None yet... ****************************************************************** */ main () { printf ("Hello Singapore!\n"); }
10
Programming
HelloSingapore.c
/* ****************************************************************** Author: Hugh Anderson Date: 01/11/2000 Description: This is an initial Hello World program Revisions: None yet... ****************************************************************** */ main () { printf ("Hello Singapore!\n"); }
Comment: The rst lines of the program are an (optional) comment. Comments may be either done using the symbol // (two forward slashes - which tell the C compiler to ignore the rest of the line) or by placing your comments between the /* and */ symbols. You are to put header comments something like the above one in every program you submit during this course. main(): The main part of the program to be run (we say executed!) is always called main. This is just a tradition. It could have been called program or starting-place or just about anything, but the designers decided to use the word main. Not Main. not MAIN. Not MaIn. In C, the case (uppercase/lowercase) matters, and so Main and main are different things. So it is: main. Body: The body of the program contains a single statement which prints out the message Hello Singapore! followed by a newline character. The printf is called a statement or a command. Format: All the other brackets and semicolons are needed by the compiler so it can tell unambiguously what you want to do - there will be more explanations later in the course. The <printf> construct: The C statement printf can be used a few different ways, but this way is the simplest. The string within the quote marks is printed out on the console (the same place where you typed the command). The \n indicates that a new line is added to the output - shifting succeeding outputs onto the next line. The format given here for printf is: printf ( <string> ) ; On a UNIX system, the online help can give you detailed information about each C programming construct. - If you use the command man printf, you will get something like this:
NAME printf - write formatted output SYNOPSIS int printf(const char *format, ...); DESCRIPTION The functions in the printf family produce output according to a format as described below. The functions printf and vprintf write output to stdout, the standard output...
These manual pages take a little getting used to, but are normally a quick way to nd out details of commands.
11
2.1.1 Compilation
This rst C program is a source le. That is, it is written in the C source language. It now has to be turned into a form that the computer can use - an executable. The process of converting a source le into an executable program is called compilation, and it is performed by a compiler:
i=1
Source
Compiler
Executable
C
The compiler splits the program into the parts that are important to it - called tokens. For our rst program, the tokens would be: main ( ) { printf ( Hello Singapore!\n ) ; } We could write our program just like this:
main(){printf(Hello Singapore!\n);},
and the program would compile and run accurately. However, we do not do this. It is common programming practice to try as much as possible to make our programs readable. Here is an example of correct, but extremely hard to read code:
CODE LISTING horrible.c float s=1944,x[5],y[5],z[5],r[5],j,h,a,b,d,e;int i=33,c,l,f=1;int g(){return f= (f*6478+1)%65346;}m(){x[i]=g()l;y[i]=(g()l)/4;r[i]=g()>>4;}main(){char t[1948 ]=" MYmtw%FFlj%Jqig~%jqig~Etsqnsj3stb",*p=t+3,*k="3tjlq9TX";l=s*20;while(i<s) p[i++]=\n+5;for(i=0;i<5;i++)z[i]=(i?z[i1]:0)+l/3+!m();while(1){for(c=33;c<s; c++){c+=!((c+1)%81);j=c/s.5;h=c%81/40.01;p[c]=37;for(i=4;i+1;i)if((b=(a=h*x [i]+j*y[i]+z[i])*a(d=1+j*j+h*h)*(r[i]*r[i]+x[i]*x[i]+y[i]*y[i]+z[i]*z[i]))>0) {for(e=b;e*e>b*1.01||e*e<b*.99;e=.5*(e*eb)/e);p[c]=k[(int)(8*e/d/r[i])];}}for (i=4;i+1;z[i]=s/2,i)z[i]=z[i]<0?l*2+!m():z[i];while(i<s)putchar(t[i++]5);}}
This code comes from an interesting web site devoted to the worst-of-the-worst-of-the-worst C code. The International Obfuscated C Code Contest at http://www.ioccc.org/. C programmers from around the world compete to write the worst C code every year. You may nd it interesting to nd out how not to write C.
12
Programming
Lets see a compilation, and rst execution of this program on each of the three platforms mentioned in section 1.3.1. Here is an example of a compilation using the GNU C compiler on one of the UNIX machines here at NUS. The commands that I typed are marked with little rounded boxes.
(List the source of the program)
13
If you have purchased the Deitel and Deitel book, it may have a CD in the back, which contains a command line version of Borlands C compiler. This may also be used, and looks like this:
14
Programming
The format followed for specication of programs in these notes is similar to that used by the CourseMaster system. A specication looks like this:
Specication 1: Convert inches to feet and inches Description: Preconditions: Postconditions: Hints: Sample input/output: A complete program to convert an input inches number to two - feet and inches. The values must be printed. None The new feet value times 12 plus the new inches value is the same as the oldinches value. Use two (or three) integer variables. Perhaps search for MODULO, and use it. suna> inchtofeet
Please enter inch value => 46 46 inches is 3ft 10in.
inchtofeet
TRUE (f 12) + i = i
suna>
Title line: - includes a brief description, and the required name for the routine or executable. Description: - a short English language description of the programming task. Preconditions: - the minimum requirements for the code to work correctly. In the preceding sample, the program is expected to work in all circumstances - there are no preconditions. More formally we might say that the precondition is TRUE.
i=1
Postconditions: - the expected result after the program or code segment is run. This may be given informally or more formally (as shown on the right of the specication above). A short form for formally specifying this program is: i, f : [TRUE, (f 12) + i = i ].
Hints: - Here are brief hints which can help you design and structure your program. Sample input/output: This may take the form of a sample run of the program (as shown above), or it may just be a brief discussion of the likely test data and expected results. If a sample run is given, your program MUST perform exactly as the sample run does. The CourseMaster system in addition has an Assessment eld which gives you hints as to how the automatic assessment of your program is done, and how to get the best mark.
15
sqrnum
A complete program to calculate the square of a given input number. The values must be printed. None TRUE
suna>
CODE LISTING
sqrnum.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to display the square of an input number Revisions: None yet... ****************************************************************** */ main () { int inputnum; printf ("Please enter number ==> "); scanf ("%d", &inputnum); printf ("%d squared is %d.\n", inputnum, inputnum * inputnum); }
Unfortunately, we have had to introduce some new things into our program. They are briey described below. Variable declaration - <int> The int inputnum; declaration tells the compiler that your program is going to want a storage place in which the program can store a value. In this case, the value is an integer value. Within the program we can refer to this storage location by the name we specify: The name of this storage place is inputnum Its value is whatever-we-put-into-it! We call these declarations variable-declarations, and the things themselves are called variables. We can declare as many variables as needed. More on the <printf> construct We now also see a second way of using the C printf statement. The rule is: printf ( <format> , <list of values/variables> ) ;
16
Programming
The <format> part is a string which contains information about how to print the values or variables. Within the string, the %d specier indicates that a number is to be printed in decimal. For example: Statement
printf(%d,6+12); printf(The sum of %d and %d \n is%d\n,6,12,6+12);
The \n specier indicates that a new line is to be started. Here is a short summary of the most important format speciers: Specier \n %s %d %x What it does newline prints a string prints a decimal value prints a hexadecimal value
Getting input - <scanf> A complementary function to printf is scanf. Its purpose is to read an input stream, and read input values. The scanf call will match the input stream with the required data types, and our initial use is very simple: scanf( <format> , &<named variable> ); The & character is telling the scanf routine the address of the place where it should return the value to. In this case the address of some named variable. Sequencing of C statements If we have two or more C statements that we wish to be performed one after the other, we write them one after the other - one-to-a-line. An example: printf(Hi); printf( there\n); printf(Dude!\n); This prints out Hi there Dude!
17
sum
A complete program to calculate the sum of two given input numbers. The values must be printed with the sum. None TRUE sum = in1 + in2
suna>
Here is a solution to this using three variables, one to hold each of the input values, and the third used to calculate the sum:
CODE LISTING
sumb.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to display the sum of two input numbers this one uses three variables Revisions: None yet... ****************************************************************** */ main () { int in1, in2, sum; printf ("Please enter first number ==> "); scanf ("%d", &in1); printf ("Please enter second number ==> "); scanf ("%d", &in2); sum = in1 + in2; printf ("The sum of %d and %d is %d.\n", in1, in2, sum); }
The <assignment> construct When a programming language has an assignment statement, we call the language a von-Neumann language, because the assignment explicitly indicates that our computer has a readable and writeable memory - John von-Neumanns engineering contribution to the computer. The structure of an assignment statement is: <variable-name> = <expression> ; The <expression> is evaluated (worked out), and assigned into the computers memory at some location <variable-name>.
18
Programming
Note that we need not have used three variables - we could have just used two. Here is a solution using two variables, one to hold each of the input values:
CODE LISTING
sum.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to display the sum of two input numbers this one uses two variables Revisions: None yet... ****************************************************************** */ main () { int in1, in2; printf ("Please enter first number ==> "); scanf ("%d", &in1); printf ("Please enter second number ==> "); scanf ("%d", &in2); printf ("The sum of %d and %d is %d.\n", in1, in2, in1 + in2); }
sumton
in > 0 result = in
i=1
suna>
CODE LISTING
sumton.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to display the sum from 1 to a specified input number Revisions: None yet... ****************************************************************** */ main () { int in1, result, count; printf ("Please enter input value ==> "); scanf ("%d", &in1); result = 0; for (count = 1; count <= in1; count = count + 1) { result = result + count; } printf ("The sum from 1 to %d is %d.\n", in1, result); }
2.3 Further C language elements The <for-loop> construct The structure of the for-loop statement is: for ( <beginning> ; <end test> ; <change> ) { <statement> }
19
The <beginning> statement is done rst - we often set some loop counter variable to have its beginning value here. The <end-test> expression checks if the loop is nished. The <change> part increments or changes the loop variable in some way. If we had: for ( i=1 ; i<=10 ; i=i+1 ) { <statement> } Then the <statement> would be executed 10 times.
20
Programming
The compilation process - tokens, source, compilers Readability and code style - bad code Environments in which to work - CYGWIN, UNIX, DJGPP, BCC Specications - informal and formal
Questions
1. Differentiate between a specication and an implementation. 2. Tabulate the size in bytes of the executables for our rst listing on each of the platforms. Why are they different? 3. Write an algorithm in pseudocode for nding a word in a dictionary. 4. What is wrong with the following program? MAIN(){PRINTF(HELLO SINGAPORE!\N);} 5. What is wrong with the following program? main(){printF(HELLO SINGAPORE!\n);} 6. What is the output of this code segment: for (i=0;i<10;i++) printf(%d\n,i); 7. Under what conditions will the following post-condition be unobtainable? y = a/b 8. In the form given in these notes, give a long specication of a program to calculate the hypotenuse of a right-angled triangle given the two other sides. 9. Give the previous example in the short formal specication style as well. 10. State in words the meaning of the following specication: x, y : [x > 0, y = x + 1].
Further study
Textbook, chapter 2, sections 2.5 to 2.6 Textbook Chapter 3, sections 3.1 to 3.8
Chapter
i=1 n
More structure
The two terms syntax and semantics are often mentioned in relation to programming languages.
Some denitions: Syntax: The structure of components of some language. It is relatively easy to get the syntax correct in our programs - we just follow a simple set of rules. A languages syntax is described by a grammar. Semantics: The meaning of a string (or sentence) in some language. It is much harder to understand the semantics of a program, than it is to verify the correct syntax.
formatted.c
/* ****************************************************************** Author: Hugh Anderson Date: 01/11/2000 Description: This is a test file Revisions: None yet... ****************************************************************** */ #define _REENTRANT #include <iostream> #include <pthread.h> int counter = 0; int turn = 10; void *many (void *arg) { pthread_t tid; cout << counter << ":"; counter = counter + 1; if (turn > 0) {/* decrement turn by 1 */ pthread_create (&tid, NULL, many, (void *) 0); cout << turn << endl;pthread_join (tid, NULL);}cout << endl;} main (){ many (NULL);}
21
More structure
formatted1.c
/* ****************************************************************** Author: Hugh Anderson Date: 01/11/2000 Description: This is a test file Revisions: None yet... ****************************************************************** */ #define _REENTRANT #include <iostream> #include <pthread.h> int counter = 0; int turn = 10; void * many (void *arg) { pthread_t tid; cout << counter << ":"; counter = counter + 1; if (turn > 0) { /* decrement turn by 1 */ pthread_create (&tid, NULL, many, (void *) 0); cout << turn << endl; pthread_join (tid, NULL); } cout << endl; } main () { many (NULL); }
There are some points about this program. Firstly - the specic formats chosen are adjustable. If we use different parameters to the indent command, the program will be formatted differently. In this case, every command is on a different line, the target of the if-statement is indented, and the indenting is consistent throughout the program.
3.2 More C constructs We begin by writing the main line of the program as shown below:
CODE LISTING
23
thefunction.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of functions Revisions: None yet... ****************************************************************** */ void readin () { /* Nothing here yet... */ } void calculate () { /* Nothing here yet... */ } void printout () { /* Nothing here yet... */ } main () { readin (); calculate (); printout (); }
We have now subdivided our task into three smaller tasks, which we address in turn. A C program may only have a single main, but may have many functions. The following points give guidelines for the use of functions:
1. C functions may have anything from 1 to 50 lines of code in them, but if they get too large, you should consider dividing them - in turn - into smaller functions. 2. A function should perform a single sensible subdivision of the required activity - not half of one activity and half of another.
When we do this sort of program development, we begin by writing down the mainline, subdividing our tasks into some set of smaller tasks. We can check and compile that this mainline works correctly, by just using empty functions. The term commonly used for these empty functions is stub procedure or stub function.
3.2.2 Compound-statements
Statements may be simple ones, such as the ones we have already met, or they may be compound statements. A compound statement is made up from a series of simple statements, grouped within curly braces. Heres an example:
24
More structure
CODE LISTING
compund1.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of compound Statements Revisions: None yet... ****************************************************************** */ main () { int in1, result, count, old; printf ("Please enter input value ==> "); scanf ("%d", &in1); result = 0; for (count = 1; count <= in1; count = count + 1) { old = result; result = result + count; printf (" Adding %d to %d gives %d\n", count, old, result); } printf ("The sum from 1 to %d is %d.\n", in1, result); }
In this example, we have added lines of code to help us discover a possible error in the program.
We now introduce a new C programming constructs called while. The while-statement executes its body as long as some condition is true. Here is an example of the use of the while programming construct:
Specication 5: Calculate the sum-up-to-n repeatedly Description: calculate the sum from 1 to this number n. The values must be printed. t repeats until n is 0. Preconditions: Postconditions: Hints: Sample input/output: The input value must be greater than 0 The result is the sum from 1 to n Use two integer variables. Perhaps use a for loop. suna> sumton
Please enter input value => 4 The sum from 1 to 4 is 10. Please enter input value => 5 The sum from 1 to 5 is 15. Please enter input value => 0
while1
in > 0 result = in
i=1
suna>
25
CODE LISTING
while1.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the while statement Revisions: None yet... ****************************************************************** */ main () { int in1, result, count; printf ("Please enter input value ==> "); scanf ("%d", &in1); while (in1 != 0) { result = 0; for (count = 1; count <= in1; count = count + 1) { result = result + count; } printf ("The sum from 1 to %d is %d.\n", in1, result); printf ("Please enter input value ==> "); scanf ("%d", &in1); } }
In the code listing, we continue to execute the statements until the user enters in the number 0. This code listing is perhaps not the best way to write this code, as we have had to repeat the same two lines in two different places within the program. Can you suggest a better way to write this program? The syntax for the while loop is: while ( <condition> ) <statement> ; In the example program given above, the statement that belongs to the while loop is a compound statement.
If you know exactly how many loops you wish to do, you should use the for-loop. If you do not know how many loops, you should use the while-loop.
Let us look at two different samples. In the rst example we will calculate an average from three input numbers:
26
Specication 6: Calculate the average Description: Preconditions: Postconditions: Hints: Sample input/output: calculate the average. None The result is the average Use integer variables. Perhaps use a for loop. suna> for1
Please enter input value => 4 Please enter input value => 5 Please enter input value => 9 The average is 6.
More structure
for1
A complete program to ask for three input numbers, and then TRUE result = (in1 + in2 + in3 )/3
suna>
CODE LISTING
for1.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the for statement Revisions: None yet... ****************************************************************** */ main () { int in1, result, loops; result = 0; for (loops = 0; loops < 3; loops = loops + 1) { printf ("Please enter input value ==> "); scanf ("%d", &in1); result = result + in1; } printf ("The average is %d.\n", result / 3); }
In our second example we read until the input number is 0. In this case we use the while loop.
Specication 7: Calculate the average Description: Preconditions: Postconditions: Hints: Sample input/output: A complete program to ask for input numbers, and then calculate the average. None The result is the average Use integer variables. Perhaps use a for and a while loop. suna> for1
Please enter input value => 5 Please enter input value => 9 Please enter input value => 0 The average is ==> 7.
while2
TRUE result = (
n 1
in)/n
suna>
27
CODE LISTING
while2.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the while statement Revisions: None yet... ****************************************************************** */ main () { int in1, result, count; result = 0; count = 0; printf ("Please enter input value ==> "); scanf ("%d", &in1); while (in1 != 0) { result = result + in1; count = count + 1; printf ("Please enter input value ==> "); scanf ("%d", &in1); } printf ("The average is ==> %d\n", result / count); }
3.2.4 Libraries
Large parts of many programs do the same things over and over again. Commonly used routines are put in libraries, which may be linked in at run time. There are three distinct advantages:
1. Saving on disk space 2. Routines can be updated 3. Routines are more reliable
For example: All C routines (clearscreen etc) are kept in a common place (libc.a in a UNIX system). When you run a program, the OS uses the latest version of libc.a automatically.
Note - Most compilers can also statically link in the needed libraries if necessary. The executables are larger. Note - In general you should not bypass library or system calls. If you do, your programs date quickly.
When we use a library routine, it saves us having to create a lot of new code. For example in the following code listing, we reuse system libraries to write a graphical application:
28
More structure
CODE LISTING
qt.c
/* ****************************************************************** Author: Hugh Anderson Date: 01/11/2000 Description: This is an initial Hello World program for Qt Revisions: None yet... ****************************************************************** */ #include <qapplication.h> #include <qpushbutton.h> int main (int argc, char **argv) { QApplication a (argc, argv); QPushButton hello ("Hello world!", 0); hello.resize (100, 30); a.setMainWidget (&hello); hello.show (); return a.exec (); }
We specify header les which dene the functions which we want to use, and when we compile, we instruct the compiler where to nd the library: g++ -o qt qt.c -lqt
29
Questions
1. Differentiate between a syntax error and a semantic error. 2. Which of the following sentences follow this grammar rule? <sentence> ::= <noun> <verb> <noun> (a) (b) (c) (d) CAR GO RUN DOG EAT DOG CATS AND DOGS FOOD SLEEP RAIN
3. How would you make the indent program turn compound statements from
for (...) { code }
into...
for (...) { code }
Further study
Textbook Chapter 3, sections 3.9 to 3.11 Textbook Chapter 4, sections 4.1 to 4.12 Textbook Chapter 5, sections 5.1 to 5.15
30
More structure
Chapter
i=1 n
technique for performing repetitive tasks.1 However the two techniques are normally used at different times - even though they are similar. A recursive computation may normally have speed overheads - it will be slower - due to the normal way in which recursion is implemented in a computer. C programmers normally say that the iterative computation is easier-to-understand. However, we do use a recursive technique when the task or data itself exhibits a recursive property in this case the software is easier to write and understand if we use recursion. As an example of a task which exhibits a recursive property, imagine specifying a function on the non-negative integers such as sum in terms of itself by saying that the sum of n elements is the same as the sum of n 1 elements plus n. When we do this we say that the function is recursive. sum(n) = sum(n 1) + n When we dene something in this way, we have to also specify a base case for the function if we wish to be able to calculate its value. For example: sum(0) = 0 sum(n) = sum(n 1) + n We may now solve the function for any non-negative integer n. Mathematicians will recognize this as a recurrence relation, and have an array of techniques for solving such relations. Later in this chapter we will give examples of both iterative and recursive versions of the same computation.
4.1 If-statement
The if-statement is used whenever you have to make a choice between two alternatives. In the above function for example, if we were calculating the result, we may use the if-statement to differentiate between the case when the n-value was 0 (in which case the result is 0), or the case that the n-value was not 0 (in which case the result is sum(n 1) + n).
1
31
32 The syntax for the if-statement is: if ( <condition> ) <statement> ;2 An alternative sytax is: if ( <condition> ) <statement> ; else <statement>;3 Here is an example of the use of the if-statement:
Specication 8: Big or small? Description: Preconditions: Postconditions: Hints: Sample input/output: Perhaps use a while loop. Perhaps use an if - else statement. suna> if1
Please enter input value => 4 It is smaller than 10! Please enter input value => 10 It is 10 or bigger! Please enter input value => 0
if1
A complete program to ask for an input number n, and then tell you if it is larger or smaller than 10 The input value must be greater than 0 in > 0
suna>
CODE LISTING
if1.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the if statement Revisions: None yet... ****************************************************************** */ main () { int in1; printf ("Please enter input value ==> "); scanf ("%d", &in1); while (in1 != 0) { if (in1 >= 10) { printf ("It is 10 or bigger!\n"); } else { printf ("It is smaller than 10!\n"); } printf ("Please enter input value ==> "); scanf ("%d", &in1); } }
i=1
4.2 Functions
The function is a fairly clear mathematical concept, embodying the idea of a mapping from the domain to the range of the function.
2 3
I think it is better to use if ( <condition> ) { <statements> } I think it is better to use if ( <condition> ) { <statements> } else { <statements }
4.2 Functions For example, a well known function is the sin function - perhaps used like this: y = sin(x)
33
mapping a real value x to a real value y. Note that the domain and range of the function are not necessarily the same. In the above, we have used the terms domain and range, but you may be happier with the terms input and output. In C programming, we may dene our own sections of code, and they are commonly called functions. However - they may not have inputs, and also may not have outputs! The term has stuck in the C programming world, even when the C routines do not return output values. You may also hear the following terms used in a loose fashion - methods, routines and procedures - they all may be used to refer to much the same thing - the programming construct where we dene a section of code, so that later we may refer to it by name - without having to repeat all the code again. Here is an example of a C function dened so that our program is shorter - this is an example of code re-use:
Specication 9: Big or small? Description: Preconditions: Postconditions: Hints: Sample input/output: Use a function suna> if2
Please enter input value => 4 It is smaller than 10! Please enter input value => 0
if2 in > 0
suna>
CODE LISTING
if2.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the if statement Revisions: None yet... ****************************************************************** */ int in1; void getnum () { printf ("Please enter input value ==> "); scanf ("%d", &in1); } main () { getnum (); while (in1 != 0) { if (in1 >= 10) { printf ("It is 10 or bigger!\n"); } else { printf ("It is smaller than 10!\n"); } getnum (); } }
34
if3 in > 0
suna>
CODE LISTING
if3.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the if statement Revisions: None yet... ****************************************************************** */ void getnum (int *val) { printf ("Please enter input value ==> "); scanf ("%d", val); } main () { int in1; getnum (&in1); while (in1 != 0) { if (in1 >= 10) { printf ("It is 10 or bigger!\n"); } else { printf ("It is smaller than 10!\n"); } getnum (&in1); } }
35
You may read more detail about each function by using the manual page for that function. When using the library, you must include the math header le in the source, and link with the math library when compiling: gcc -o dosin -lm dosin.c
Specication 11: Calculate sin value Description: Preconditions: Postconditions: Hints: Sample input/output: Calculate the sin value Use the math library suna> if1
Please enter input value => 4 The sin of 4.000000 is -0.756802
dosin
A complete program to calculate and display the sin value of an entered value. output = sin in
suna>
CODE LISTING
dosin.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the math library Revisions: None yet... ****************************************************************** */ #include <math.h> main () { float in1, out1; printf ("Please enter input value ==> "); scanf ("%f", &in1); out1 = sin (in1); printf ("The sin of %f is %f\n", in1, out1); }
36
i=1
suna>
CODE LISTING
fac.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to calculate the factorial of a specified input number Revisions: None yet... ****************************************************************** */ main () { int in1, result, count; printf ("Please enter input value ==> "); scanf ("%d", &in1); result = 1; for (count = 1; count <= in1; count = count + 1) { result = result * count; } printf ("The factorial of %d is %d.\n", in1, result); }
CODE LISTING
fac1.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to calculate the factorial of a specified input number Revisions: None yet... ****************************************************************** */ int fac (int n) { if (n == 0) { return 1; } else { return n * fac (n 1); } } main () { int in1, result, count; printf ("Please enter input value ==> "); scanf ("%d", &in1); result = fac (in1); printf ("The factorial of %d is %d.\n", in1, result); }
37
Questions
1. Calculate the value for f (4) for the following recursive function denition: f (0) = 1 f (n) = (f (n 1) n) + n 2. Write a recursive C function which calculates the sum function dened at the beginning of this chapter.
Further study
Textbook, Chapter 4, sections 4.7
38
Chapter
Bugs n stuff
For many years the software industry has referred to software errors as bugs. This trivializes the
issue, and gives the software developer a false sense of security. (Once Ive removed these last few bugs, it will be perfect!) In reality, the software developer may stand on very shaky ground: Once Ive removed these last few bugs, it will be: a system that has been known to work for 11 minutes continuously... (!) a system that still contains code that I put in last month that I dont know why it is there... The Internet newsgroup comp.risks reports on Risks to the Public in the Use of Computer Systems and Related Technology, and makes interesting reading. We nd that: The Arizona lottery never picked the number 9 (a bad random number generator) The LA counties pension fund has lost US$1,200,000,000 through programming error. Windows NT corrupts lespace and deletes directories under some circumstances. A Mercedes 500SE with graceful no-skid brake computers left 200 metre skid marks. A passenger was killed. The following points may help in improving the quality of software: Design software before building it - You would not pay much for a house without plans or that had not been designed rst - why would you pay for software without similar sureties? Reduce the complexity - By localizing variables, use of modularity, well dened interfaces and so on. Enforce compatibility with design - Easier to say than to do.
ERROR!
39
40
Bugs n stuff
net.c
/* Initialization... */ printf ("Trying to open socket...\n"); if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) fprintf (stderr, "server: cant open stream socket\n"); /* The main event loop... */ for (;;) { clilen = sizeof (cli_addr); printf ("clilen is %d\n", clilen); ....
41
5.1.1 Insight
Here is another windowed interface to gdb - insight. This one will work on Windows platforms (as well as UNIX), and is found at http://www.comp.nus.edu.sg/cs1101c/hugh/downloads/insight.zip. Download the le to your machine (it is about 2.5MB), and use WinZip to unzip into C:\ (or D:\. Then add the directory C:\insight\bin to your computers PATH by modifying C:\autoexec.bat as you did before:
set PATH=C:\insight\bin;C:\pfe;C:\cmc;C:\cygnus\cygwin-b20\h-i586-cygwin32\bin;%PATH%
You may now run insight by typing gdb and loading up a le, or by using the latest pfe.reg le (which has a menu item for the debugger). Note that when you compile for insight you must tell the compiler that you are going to use the debugger by using the -g option: gcc -g -o myprog.exe myprog.c.
42
Bugs n stuff
43
You may now run the man command and get online help for most C functions. For example man will give you detailed help for the C printf() system call, and man -t printf > printf.ps will produce a nice (printable) postscript version - something like this:
printf
PRINTF(3) Linux Programmer s Manual PRINTF(3)
NAME
printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output con version
SYNOPSIS
#include <stdio.h> int printf(const char * format,. ..); int fprintf(FILE * stream,const char * format,. ..); int sprintf(char * str,const char * format,. ..); int snprintf(char * str,size_t size,const char * format,. ..); #include <stdarg.h> int vprintf(const char * format,va_list ap); int vfprintf(FILE * stream,const char * format,va_list ap); int vsprintf(char * str,const char * format,va_list ap); int vsnprintf(char * str,s ize_t size,const char * format,va_list ap);
DESCRIPTION
The printf family of functions produces output according to a format as described belo w. The functions printf and vprintf write output to stdout,the standard output stream; fprintf and vfprintf write output to the given output stream; sprintf, snprintf, vsprintf and vsnprintf write to the character string str. These functions write the output under the control of a format string that specifies ho w subsequent ar guments (or ar guments accessed via the v ariable-length argument facilities of stdarg (3)) are converted for output. These functions return the number of characters printed (not including the trailing \0 used to end output strings). snprintf and vsnprintf do not write more than size bytes (including the trailing \0), and return -1 if the output w as truncated due to this limit. (Thus until glibc 2.0.6. Since glibc 2.1 these functions return the number of characters (excluding the trailing \0 which would ha ve been written to the final string if enough space had been a vailable.) The format string is composed of zero or more directi ve s: ordinary characters (not %), which are copied unchanged to the output stream; and con version specifications, each of which results in fetching zero or more subsequent ar guments. Each conversion specification is introduced by the character %
44
Bugs n stuff
You should never re-dene or use these words in any way except the intended one. Other names we have used (such as printf) identify particular library functions, and may be redened (although of course you will no longer be able to use them.) Earlier in this course, it was mentioned that C is both a too simple language, and too complex. The table above shows that the language does have a very small number of components, and thus deserves to be called simple. However C always comes with many libraries that contain hundreds - perhaps even thousands - of routines. Names we have used (such as printf) identify particular library functions, and may be redened (although of course you will no longer be able to use them.) Note the difference between while and printf() in this context - while is a part of the underlying C language, but printf() is a library routine.
The variable bank_balance is much more understandable than the variable at location 0x0112ca4e.
5.3 Basic rules This leads to some other cosmetic rules: 1. Use reasonably short meaningful identiers.
45
2. It is common to use UPPERCASE for constant values and lowercase, or mixed case, for variables and functions. (for example PI may be 3.14159, and BankBalance or bank_balance may be a name associated with a variable containing a bank balance.) 3. It is OK to use single-letter identiers (i,j,k) for anonymous loop counter variables (rather than count1, count2, count3). 4. Be consistent in your use of names. With some practice, it is possible to write very readable and understandable programs if you choose good variable and function names.
46
Bugs n stuff
47
CODE LISTING
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of nested for statements Revisions: None yet... ****************************************************************** */ main () { int in1, i, outer; scanf ("%d", &in1); for (outer = 1; outer <= in1; outer = for (i = 1; i < outer; i = i + 1) printf ("+"); } for (i = 1; i < 40 (outer * 2); printf (" "); } for (i = 1; i < outer; i = i + 1) printf ("+"); } printf ("\n"); } }
outer + 1) { { i = i + 1) { {
48
Bugs n stuff
CODE LISTING
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of the switch statement Revisions: None yet... ****************************************************************** */ main () { char ch; scanf ("%c", &ch); switch (ch) { case a: printf ("You keyed a\n"); break; case b: printf ("You keyed b\n"); break; case c: printf ("You keyed c\n"); break; case d: printf ("You keyed d\n"); break; default: printf ("You did not key either a,b,c or d!\n"); } }
49
Questions
1. What do the letters in gdb stand for? 2. Investigate the Ariane 5 and Therac 25 incidents in relation to risks. What lessons can we learn from these incidents? 3. Which of the following are valid C identiers:
_hello, __hello, 2much, TooLittle, nearly-enough, nearly_enough.
4. Find the header les for C (stdio.h and so on) on a UNIX or Windows system. Give the function prototypes for printf and scanf. 5. Write the rst few lines of this C program:
CODE LISTING program mymain begin writeln( "Hello World!\n" ); end
pas.pas
Further study
Textbook, Chapter 5,6,7
50
Bugs n stuff
Chapter
6
i
51
52
Each of these variables is stored internally in the computer as a group of bits (binary digits). For example, the letter A is stored in a char variable as the eight bits 01000001.
Note that if you do not initialize the variables, there is no guarantee that they will have any xed value when your program runs, although some systems initialize them to have values of ZERO.
maxint.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the maximum size of an integer Revisions: None yet... ****************************************************************** */ main () { int i; for (i = 0; i < 4; i = i + 1) { printf ("2147483646+%d is %d\n", i, 2147483646 + i); } }
53
If the compiler uses 16 bits for an integer then the integer values are restricted to -32768 to 32767, (215 to 215 1). If it uses 32 bits then the integer values are restricted to -2147483648 to 2147483647 (231 to 231 1). If you need more (or less) range, you can use the long (or short) keyword.
sizeof.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the size of various simple data types. Revisions: None yet... ****************************************************************** */ main () { char c; short h; int i; long int j; long long int k; float l; double m; printf ("The size of char is %d bits\n", sizeof (c) * 8); printf ("The size of short is %d bits\n", sizeof (h) * 8); printf ("The size of int is %d bits\n", sizeof (i) * 8); printf ("The size of long int is %d bits\n", sizeof (j) * 8); printf ("The size of long long int is %d bits\n", sizeof (k) * 8); printf ("The size of float is %d bits\n", sizeof (l) * 8); printf ("The size of double is %d bits\n", sizeof (m) * 8); }
54
The computer gets the value of a and if it is less than 3 returns a 1 and if not returns a 0. It works very much like a function and we can replace it with a function:
int checksize( int x, int y ) { if (x<y) return 1; else return 0; }
The function is far longer than it needs to be. A quicker way, but possibly harder to understand initially, is:
int checksize( int x, int y ) { return x<y; }
6.1 Simple data types To get the value of a digital character such as 3, we can use:
value = (int) 3 - (int) 0;
55
Each character is given a particular 7 bit pattern - any unambiguous pattern would do but there is an international agreement that particular patters are interpreted as particular characters - ASCII - the American Standard Code for Information Interchange. This code denes 128 (128 = 27 ) characters:
Dec 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Hex 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ Char nul soh stx etx eot enq ack bel bs ht lf vt ff cr so si dle dc1 dc2 dc3 dc4 nak syn etb can ew sub esc fs gs rs us Dec 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 Hex 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F Char spc ! # $ % & ( ) * + , . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? Dec 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 Hex 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F Char @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ Dec 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 Hex 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F Char a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ del
56
>
+=
-=
A simple example:
a=b=c+2;
adds 2 to c, and assigns the result to b, and then to a. It acts in this way:
a=(b=(c+2));
However,
a=(b=c)+2;
assigns c to b, and then adds 2, putting the result of this calculation in a. A general rule to cope with all this confusion is: Use brackets...
57
instead of:
process( rec0 ); process( rec1 ); process( rec2 ); ...
C arrays all start at element 0 - that is, the rst item in a C array is numbered 0. C arrays are xed (static) in size, and each of the array elements appears one-after-the-other in the memory of the computer:
rec
rec[0] rec[1] rec[2] rec[3] rec[4] rec[5] rec[6] rec[7] rec[8] rec[9] 41 9 0 16 7 11 1 1543 222 7
When you write programs in C, it is easy to refer to (say) rec[100] for the 100th element of an array by mistake. This causes no compile time error, as C does not check array bounds. However it may
58
(or may not) cause run time errors and confusion. In general, this reference may refer to a completely different variable - perhaps a part of the one declared next in the program. The array name itself refers to the machine memory address of the rst element of the array.
We can pre-initialize arrays in a similar manner to the way in which we pre-initialize simple variables:
int recs[10] = { 26, 42, 14, 2, 1, 0, 0, 0, 45, 11223 }; char s[4] = abc;
In the s[4] example above, the longest string that may be stored in s is 3 characters. Each C string is terminated by a NULL (or 0) character, and so we need to leave space for this terminating (or nishing) character. A char (character) array is commonly used to store strings, and we may x the size by initializing the char array. The array size will be one more than the number of characters in the string:
char s[] = A short message!;
Note: hello represents the string containing the ve characters h, e, l, l and o, and terminated with a NULL char - a total of 6 characters. By contrast, h represents the single character.
Note that a suitable way to print out (or indeed do any process on) a multi-dimensional data structure is by using a nested for-loop:
59
CODE LISTING
nested.for.array.c
/* ****************************************************************** Author: Hugh Anderson Date: 22/11/2000 Description: This is a program to demonstrate the use of nested for statements for processing multidimensional arrays Revisions: None yet... ****************************************************************** */ main () { int i, j; int rec[3][2] = { {26, 42}, {14, 2}, {1, 0} }; for (i = 0; i < 3; i = i + 1) { printf ("Row %2d: ", i); for (j = 0; j < 2; j = j + 1) { printf ("%6d", rec[i][j]); } printf ("\n"); } }
We can also use these 2D arrays to create arrays of strings, and then use a for loop to iterate through them:
array.of.string.c
char translate[5][6] = { "zero", "one", "two", "three", "four" }; int main () { int i; for (i = 0; i < 5; i = i + 1) { printf ("%d is %s.\n", i, translate[i]); } return 0; }
[hugh@stf-170 programs]$ gcc array.of.string.c [hugh@stf-170 programs]$ ./a.out 0 is zero. 1 is one. 2 is two. 3 is three. 4 is four. [hugh@stf-170 programs]$
60
struct1.c
The typedef struct denition allows us to predeclare structs and then use them as needed:
typedef struct { char name[30]; char address[3][30]; oat balance; } account; account allaccounts[100];
61
62
In this section, we introduced the following topics: Program checking - lint Revision of simple data types Internal storage of simple data types Two dimensional arrays and more complex structures Random numbers
Questions
1. Which of the following numbers may be represented exactly in a oat: 1, 100, 0.5, 0.3, 0.25 2. Given the expression y = ax3 + 7, which of the following (if any) are correct C statements for this equation?
y = a*x*x*x + 7; y = a*x*x*(x + 7); y = (a*x)*x*(x + 7); y = (a*x)*x*x + 7; y = a*(x*x*x) + 7; y = a*x*(x*x + 7);
3. Write a short function upcase() which is passed a char parameter, and returns the same character unless it is a lowercase ASCII character. If it is a lowercase ASCII character, the function returns the equivalent uppercase ASCII character.
Further study
Textbook, Chapters 5&6.
Chapter
i=1 n
Execute
10
Program Fetch...
Next Instruction a=10; a=a+1; for ( i=1; i<a; i++ ) {
The elements of the model are: A linear memory, each location addressed or identied by number. In the diagram above, memory location 1 is used for the C variable a. An execution unit, which has a pointer to the current C instruction to be executed. An area of memory used for storing the program (which is in a machine language, not C). A fetch-execute cycle.
During normal operation of the C machine, the rst instruction to execute is the one at the beginning of the main(). 63
64
7.1 Pointers
Our model of the C machine includes a memory space, with a large number of memory slots into which we store our program variables. In the programming language, we address these slots by name, but internally the computer refers to them by number. The specic location of a variable in memory is called its address, and in some situations it is useful to refer to variables by their address - particularly when trying to get the most speed out of some code. For example, if we were trying to zero-out all the elements of an array, we may have code something like this:
for ( i=0; i<sizeof( myarray ); i=i+1 ) { myarray[ i ] = 0; }
This code is ne, and very readable, but requires the calculation of the addess of the ith element of the array every time through the loop, along with the loop increment. We can use pointers to get the same effect
for ( ptr=&myarray; ptr<(&myarray+sizeof( myarray )); ptr=ptr+1 ) { *ptr = 0; }
When this code is translated to machine code by the compiler, the resultant code is smaller and faster, without the continual recalculation of the address of each element of the array. We refer to the address of a variable by prexing the variable name with the & character. We have been using pointers whenever we use scanf() - the &varname in scanf( %f, &varname ) refers to the address of varname. Addresses are 16 bits in size on a 16 bit machine, 32 bits in size on a 32 bit machine and so on. We can declare variables that contain other variable addresses. For example:
int *pmyint;
7.2 Strings We dereference a pointer variable by prexing the name with the * character. For example:
int a=1,b=2; int *p; p = &a; printf( The address of a is %d, and the value there is %d\n, p, *p ); p = &b; printf( The address of b is %d, and the value there is %d\n, p, *p );
65
Pointers cause a lot of problems in C programs - if you attempt to access the memory pointed at by an arbitrary pointer, and that memory is not available to you, then you may get a segmentation fault. This means that your program has tried to access memory that does not belong to it, and the OS has trapped this reference, and terminated the program. Why do we use pointers? 1. For speed, and 2. because we want dynamic structures - that are created only at run time. A very common fault in C programs with pointers is to forget to initialize the pointer, leading to a segmentation fault.
7.2 Strings
Standard C contains no special type for a string, and so a string is made from arrays of characters. The C string is a character array in which the last character is a NULL (\0). C strings are of unlimited size, and may contain any ASCII character - except for the NULL character (for obvious reasons). We can create a string easily:
CODE LISTING
int main () { char mystr[10]; mystr[0] mystr[1] mystr[2] mystr[3] mystr[4] = = = = = H; u; g; h; \0;
C
string1.c
66
and the result will be the same as above except that it will have made its size to be 4 since that is all that is needed. What we cannot do is:
char str[5]; str = "abc";
In order to do that we have to copy one string to another. We could write a function:
void copystrng(char s1[], char s2[]) { int i=0; while (s2[i] != \0) { s1[i] = s2[i]; i++; } s1[i] = \0; }
and this will copy the string in s2 and put it into s1. However, this sort of thing is so commonly done that a similar function exists in a standard library accessed via the <string.h> header le:
The strcpy() function copies the string pointed to by src (including the terminating \0 character) to the array pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy. The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no NULL byte among the rst n bytes of src, the result will not be null-terminated.
The strcat() function appends the src string to the dest string overwriting the \0 character at the end of dest, and then adds a terminating \0 character. The strings may not overlap, and the dest string must have enough space for the result. The strncat() function is similar, except that only the rst n characters of src are appended to dest.
67
The strcmp() function compares the two strings s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. The strncmp() function is similar, except it only compares the rst n characters of s1.
The strlen() function calculates the length of the string s, not including the terminating \0 character.
The snprintf() function does not write more than size bytes.
The calloc() system call allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is set to zero. The malloc() system call allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared. The free() system call frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undened behaviour occurs. If ptr is NULL, no operation is performed.
68
In this section, we introduced the following topics: The C programming model Pointers Strings Memory allocation
Questions
1. Give initializer declarations for an array of 12 integers containing the days in each month of 2001. 2. On some computers, the bytes of an integer are stored in different orders. This is called the endianness of a computer. Write a short program using a char pointer and an integer which discovers the ordering of successive bytes of an integer. 3. Write a function to concatenate two strings, which accepts two input strings as parameters, and returns a pointer to a new string. Your routine should use malloc().
Further study
Textbook, Chapters 7 and 8
Chapter
8
1
Files n stuff
A possible technique for developing software is to progress through the following stages:
Task specication - research the situation, developing a clear specication or denition of the task to be performed. Analysis - look at the problem from the point of view of the user. Develop a logical plan of the system - how it should appear to the user. Design - Develop the algorithm(s) to execute the logical plan. Pseudocode, or other representations. Implementation - Code in the language of your choice. Testing - Use a test plan, and document the testing process. To aid in documenting and designing software, we use diagrams of different sorts. A very old and very simple technique is the ow diagram. The ow diagram uses differently shaped boxes to represent different programming constructs. The principal problem with this diagramming technique is that it is not scalable - the components of the diagram always represent the lowest level program structures. Nowadays we use better tools - the UML (Unied Modelling Language) has a range of modelling and diagramming techniques, and some tool support to check the correctness of designs before implementation.
start
Activity fork
merge
join
end
69
70
Files n stuff
8.1.2 Names
The le names can be of different sizes, and use different characters. DOS le names: DOS les use an 8-byte name with a 3-byte extension - XXXXXXXX.YYY. There are limits on the characters allowed in le names. UNIX le names: UNIX systems vary, but typically allow 255 character le names, using nearly any characters, and being case sensitive. The name a.b.c.d.e()[]\ is a perfectly valid UNIX le name. So is a name with spaces!. NT le names: NT systems support a range of le systems, including the older DOS based le systems. When using NTFS, le names are case insensitive, and may be up to 255 characters long, using nearly any characters.
71
file1.c
main () { FILE *f1; int i; f1 = fopen ("myfile.dat", "w"); for (i = 0; i < 10; i = i + 1) { fprintf (f1, "Line %d Hi there!\n", i); } fclose (f1); }
The fopen function opens the le whose name is the string pointed to by path and associates a stream with it. The argument mode points to a string containing the mode in which the le is to be opened.
The fclose function dissociates the named stream from its underlying le or set of functions. If the stream was being used for output, any buffered data is written rst, using fush(3).
These system calls act in a similar way to the printf() system call.
2
72
Files n stuff
The string path points to a path name naming a le. open opens a le descriptor for the named le and sets the le status ags according to the value of oag. oag values are constructed by OR-ing Flags
The parameter ldes is a le descriptor obtained from a creat, open, dup, fcntl, pipe, or iocntl system call. The system call close closes the le descriptor indicated by ldes. All outstanding record locks owned by the process (on the le indicated by ldes) are removed.
The system call read attempts to read nbyte bytes from the le associated with ldes into the buffer pointed to by buf. If nbyte is zero, read returns zero and has no other results. The parameter ldes is a le descriptor obtained from a creat, open, dup, fcntl, pipe, or ioctl system call. The system call write attempts to write nbyte bytes from the buffer pointed to by buf to the le associated with ldes. If nbyte is zero and the le is a regular le, write returns zero and has no other results.
The fcntl system call provides for control over open descriptors. A large number of operations are possible using the fcntl call.
73
#define SERV_TCP_PORT 9000 #define MAXLINE 512 main (argc, argv) int argc; char *argv[]; { /* Declarations... */ int loc, sockfd, newsockfd, clilen; struct sockaddr_in cli_addr, serv; /* Initialization... */ printf ("Trying to open socket...\n"); if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) fprintf (stderr, "server: cant open stream socket\n"); bzero ((char *) &serv, sizeof (serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = htonl (INADDR_ANY); serv.sin_port = htons (SERV_TCP_PORT); if (bind (sockfd, (struct sockaddr *) &serv, sizeof (serv)) < 0) fprintf (stderr, "server: cant bind local address\n"); listen (sockfd, 5); /* The main event loop... */ for (;;) { clilen = sizeof (cli_addr); printf ("clilen is %d\n", clilen); newsockfd = accept (sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) fprintf (stderr, "server: accept error\n"); write (newsockfd, "Hughs Server\n", 14); process (newsockfd); close (newsockfd); } }
74
Files n stuff
In C these operators are written as && and ||. There is no implementation of the exclusive-or operator in C. The unary operator NOT is ! in C. Up to now we have had Boolean expressions such as (a>=3) which is TRUE if the value of a is greater than or equal to 3 and FALSE otherwise. If we wanted an integer to be within a range such as greater than 3 and less than or equal to 10, we can write this as (a>=3 && a<=10).
This is so commonly done, that C has a special syntax specically for this - the increment operator:
count++; or ++count;
If the assignment is on its own there isnt a difference between them but if the increment operator is used in an expression then the order may matter. pre-increment: ++a means increment and then use the value of a. post-increment: a++ means use the value of a and then increment. We have a matching decrement operator: .
75
76
Files n stuff
In this section, we introduced the following topics: Software development process Diagramming methods File operations More operators
Questions
1. Declare four char variables in a program, and determine the addresses of each. 2. Write a small program which asks for the names of two les, opens the rst one and copies any lines that start with a digit to the end of the other le. 3. Give a function prototype for a function which does not return any value. 4. What is the value of x after x = 1; x=(x == (x+=1)+1);. 5. What will be the values of the three parameters to this function?
a=1; myfunc( ++a, ++a, ++a );
Further study
Textbook, Chapter 11
Chapter
i=1 n
Sorting
The study of sorting algorithms is traditional in introductory programming courses. The reason for
this is that the study gives us a vehicle for showing a range of different algorithms for the same problem. The sort problem can be clearly and simply expressed (something like sort all those numbers in ascending or descending order). As we will see though, there are many solutions - each using a different algorithm. There are many ways of performing sorts on unsorted data, with no one best way, as some sorts are faster when the data is almost completely sorted, but very slow when the data is a randomly distributed. Comparison sorting algorithms have a range of speeds - mergesort, heapsort, and the best case of quicksort will take O(n log n), while insertion sort, selection sort, and the worst case of quicksort all take O(n2 ). The Big-O notation given here describes the amount of time taken by an algorithm. When we use the term O(n2 ) we mean that the time for the algorithm is of-the-order-of n2 - in this case the square of the number of things to be sorted. Here is a chart showing how O(n log n) and O(n2 ) are related: n O(n log n) O(n2 ) 1 0 1 10 10 100 100 200 10000 1000 3000 1000000 10000 40000 100000000
As you can see, O(n log n) is faster than O(n2 ). You can see animations of different sorting algorithms at http://www.epaperpress.com/s_man.html. In the following examples, we assume that the initial data to be sorted is stored in an array, but of course the algorithms might be used to sort any data structures.
77
78
Sorting
i=1
BubbleSort.c
for (P = 0; (P < N 1) && (switched == TRUE); P = P + 1) { switched = FALSE; for (j = N 1; j > P; j = j 1) { if (A[j] > A[j 1]) { switched = TRUE; tmp = A[j]; A[j] = A[j 1]; A[j 1] = tmp; } } } }
79
i=1
InsertionSort.c
for (P = 1; P < N; P = P + 1) { tmp = A[P]; for (j = P; (j > 0) && (A[j 1] > tmp); j = j 1) A[j] = A[j 1]; A[j] = tmp; } }
80
Sorting
i=1
We now have (many of) the smaller elements clustered near the left side of the array. If we perform a 1-sort now, there will be few exchanges, as elements do not have to move all the way from one end to the other of the array.
ShellSort.c
for (inc = 1; inc <= N; inc = inc * 2); for (inc = (inc / 2) 1; inc > 0; inc = (inc 1) / 2) { for (P = inc; P < N; P = P + 1) { tmp = A[P]; for (j = P; j >= inc && A[j inc] > tmp; j = j inc) A[j] = A[j inc]; A[j] = tmp; } } }
To get the most effective shell sort, it is best if there are no factors common between the numbers used for the values of k in the k-sequence. The shell sort efciency depends on the choice of the k-sequence, and has a best case running time of O(n log n), and a worst case running time of O(n2 ).
81
i=1
SelectionSort.c
for (P = 0; P < N 1; P = P + 1) { minIndex = P; for (j = P + 1; j < N; j = j + 1) { if (A[j] < A[minIndex]) minIndex = j; } tmp = A[P]; A[P] = A[minIndex]; A[minIndex] = tmp; } }
82
Sorting
i=1
The quicksort algorithm has an average running time of O(n log n), although it is possible to choose very bad pivots and get a worst case running time of O(n2 ).
83
Questions
1. Investigate the heap sort algorithm. What is its running time? How does it work? 2. What is another divide-and-conquer algorithm used in programming? If you dont know one, nd one. 3. Try out each of the sorting algorithms given here, by writing a main() which calls them with arrays of unsorted data. Put counters in the code to measure how many inner loops each procedure takes. 4. Write and test a program which sorts the characters in a string into ascending (ASCII) order. 5. Write and test a program which sorts the words in a string into ascending order. 6. Using the algorithm given in the code listing on page 80, give the k-sequence for an array with 47 elements.
Further study
Textbook, Chapter 6, section 6.6 Textbook, Chapter 7, section 7.6
84
Sorting
Chapter
10
GUI programming
Pronounced GOOEY programming!
GUI stands for Graphical User Interface, and its use here refers to programs which use the WIMP1 interface found in most workstations. - as opposed to the command-line interface found in a DOSprompt, or telnet session to a UNIX machine. Unfortunately, (again) there is no one standard for GUI programming, although the techniques that you learn in one standard are generally transportable to another. In the programs given so far in this text, there is a single thread-of-control, which we can examine by reading the main(). This code determines how a user is expected to interact with the program. This general program architecture is satisfactory for small programs with simple command-line user interfaces. However, graphical user interfaces have a much more complex thread-of-control. For example, at any time we may have a number of possible events about to occur - the user may ask for the window to be minimized, or resized, or click a button, or select a menu, or ... Our programs must respond to each of these events. The normal way to do this is by restructuring our programs as a group of functions - each of which responds to an event. These functions are called callbacks. Our GUI mainline code looks like this:
CODE LISTING GUICode.c #include <any GUI header files needed>
int main () { RegisterAllCallbacks (); LoopForever (); }
An interesting area of GUI programming is the development of abstract windowed environments, where we program using an abstract API. These systems normally allow the development of software which can be compiled for any environment.
1
85
86
GUI programming
Contrary to popular public opinion, Windows is not the only GUI window system. The Macintosh system, and the UNIX X window system both predate Microsofts GUI system, and each have interesting features that have yet to be added to Windows. For example the UNIX X window system allows a clear separation between the display and the processing, whereas Windows display and processing must be on the same machine. By far the most common GUI windowed environment on the desktop is the one found in Win95/98, and our programming API for it is known as Win32. We will briey look at three window systems.
10.1.1 Win32
Win32 is a generic name for 4 (slightly) different APIs for Microsoft operating systems. The Win32 API provides standard function calls for accessing the GUI, le system, processes and so on2 . The Win32 API on Win95 is a subset of those on WinNT, so applications written for Win95 should be portable to WinNT. The reverse is not always true, but most WinNT applications can run on WIn95/98. The normal way for you to access Win32 functions is by using a precompiled library from a C program.
10.1.2 X
The X window system3 is a well developed system which allows for multimedia software and hardware components to be distributed around a network. At its simplest level, it allows a program and its display to be on different computers. The architectural view of X is a little peculiar. The designers view the display as central to the system, and the software running on the display is called the X-server:
X display (server)
Clients
2 3
Not just the GUI. The system is called X, or the X window system. It is not called X-windows!
10.1 Window systems From the diagram, we can easily identify three essential components of X: 1. The X server - providing the high resolution graphical display(s), keyboard and mouse.
87
2. The X protocol - providing standard communication methods between the distributed software components. 3. X clients - programs with graphical display output, running on (perhaps) many machines. However, there are two other components: The Window manager(s) - providing decorations around each window. The Display manager(s) - providing access to the system.
88
GUI programming
It is possible to write applications that use the Win32 API directly. These programs tend to be long (that is - they have a lot of source lines).
In the same vein as our HelloSingapore example, we start with this program:
CODE LISTING
#include <windows.h> int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { MessageBox (NULL, "Hello, Windows!", "Hello", MB_OK); return 0; }
SimpleWin32.c
89
Another more complex example shows the avour of raw Win32 programming, although I have removed about 380 lines for clarity:
BiggerWin.c
CODE LISTING
#include <windows.h> #include <string.h>
int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { HWND hwndMain; /* Handle for the main window. */ MSG msg; /* A Win32 message structure. */ WNDCLASSEX wndclass; /* A window class structure. */ char *szMainWndClass = "WinTestWin"; memset (&wndclass, 0, sizeof (WNDCLASSEX)); wndclass.lpszClassName = szMainWndClass; wndclass.cbSize = sizeof (WNDCLASSEX); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = MainWndProc; wndclass.hInstance = hInst; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); RegisterClassEx (&wndclass); hwndMain = CreateWindow (szMainWndClass, "Hello", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL); ShowWindow (hwndMain, nShow); UpdateWindow (hwndMain); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam; }
The full source code and a makele is available at http://www.comp.nus.edu.sg/cs1101c/hugh/generic.tgz. When this is compiled, we get this:
Unfortunately, the full source code totals over 400 lines of C, and still doesnt actually do anything! If you are interested in learning more about Win32 programming, there is an interesting tutorial at http://www.winprog.org/tutorial.
90
GUI programming
Here is a part of a small C GUI application built using the Qt GUI toolkit:
CODE LISTING
#include <qapplication.h> #include "application.h" int main (int argc, char **argv) { QApplication a (argc, argv); ApplicationWindow *mw = new ApplicationWindow (); mw>setCaption ("Document 1"); mw>show (); a.connect (&a, SIGNAL (lastWindowClosed ()), &a, SLOT (quit ())); return a.exec (); }
SmallQtApp.c
91
In the following example, a Tcl/Tk program is integrated with a C program, giving a very small codesize GUI application, that can be compiled on any platform - windows, UNIX or even the Macintosh platform without changes.
CplusTclTk.c
char tclprog[] = "\ proc fileDialog {w} {\ set types {\ { \"Image files\" {.gif} }\ { \"All files\" *}\ };\ set file [tk_getOpenFile filetypes $types parent $w];\ image create photo picture file $file;\ set glb_tx [image width picture];\ set glb_ty [image height picture];\ .c configure width $glb_tx height $glb_ty;\ .c create image 1 1 anchor nw image picture tags \"myimage\";\ };\ frame .mbar relief raised bd 2;\ frame .dummy width 10c height 0;\ pack .mbar .dummy side top fill x;\ menubutton .mbar.file text File underline 0 menu .mbar.file.menu;\ menu .mbar.file.menu tearoff 1;\ .mbar.file.menu add command label \"Open...\" command \"fileDialog .\";\ .mbar.file.menu add separator;\ .mbar.file.menu add command label \"Quit\" command \"destroy .\";\ pack .mbar.file side left;\ canvas .c bd 2 relief raised;\ pack .c side top expand yes fill x;\ bind . <Controlc> {destroy .};\ bind . <Controlq> {destroy .};\ focus .mbar"; int main (argc, argv) int argc; char **argv; { Tk_Window mainWindow; Tcl_Interp *tcl_interp; setenv ("TCL_LIBRARY", "/cygnus/cygwinb20/share/tcl8.0"); tcl_interp = Tcl_CreateInterp (); if (Tcl_Init (tcl_interp) != TCL_OK || Tk_Init (tcl_interp) != TCL_OK) { if (*tcl_interp>result) (void) fprintf (stderr, "%s: %s\n", argv[0], tcl_interp>result); exit (1); } mainWindow = Tk_MainWindow (tcl_interp); if (mainWindow == NULL) { fprintf (stderr, "%s\n", tcl_interp>result); exit (1); } Tcl_Eval (tcl_interp, tclprog); Tk_MainLoop (); exit (1); }
The rst half of the listing is a C string containing a Tcl/Tk program. The second part of the listing is C code which uses this Tcl/Tk.
GUI programming
And the result is a simple viewer for GIF images. The total code size is 57 lines. The application looks like this when running:
93
Questions
1. Try out each of the programs given here. 2. Investigate the MFC. What is the simplest Hello-World program that can be written using it? 3. In object-oriented terms, differentiate between a class and an object. 4. Given the callback system architecture outlined in this chapter, what does the software do when no events are happening?
Further study
There is nothing in the textbook specically related to C and GUI, but chapter 29 has a discussion on GUIs, and programming them in Java.
94
GUI programming
Chapter
11
Arbor day
In this section, we look at ways in which we can combine simple variables, arrays, structs and
pointers to make more complex user-dened data structures. The reason we build these more complex structures is to assist our program structure to match the structure of our problem. If our problem has a complex structure, then it is best if our program structure maps onto this structure in some obvious way. Before showing sample code to manipulate these structures we introduce the C syntax for referring to structure elements using pointers. If we had a struct with components key and next, and a pointer p to that structure, then we refer to p->key and p->next. We also introduce a diagramming technique to help us remember the shape of user-dened data structures. A pointer is drawn as an arrow, a struct as a box.
p node p->next
In the above diagram we are looking at a pointer to a struct which itself has a pointer to something else.
NULL
95
96
Arbor day
If a pointer does not point to anything (yet) then we label it as NULL or NIL. We should always remember to initialize our array elements as NULL. In this example program we use an array of pointers to store some strings, which are created dynamically as needed using the malloc() call.
CODE LISTING
#include <string.h> #include <stdio.h> int main () { char str[80]; char *strings[10]; int i; for (i = 0; i < 10; i = i + 1) { fgets (str, 79, stdin); str[strlen (str) 1] = \0; strings[i] = (char *) malloc (strlen (str) + 1); strcpy (strings[i], str); } for (i = 9; i >= 0; i = i 1) { printf ("line %d was: %s\n", i + 1, strings[i]); } return 0; }
arrayofpointers.c
argcargv.c
When we type a command like del le1.txt, the del command looks at its own argument to nd out which le to delete.
11.2 Lists
97
11.2 Lists
The list structure can be extended as needed - when we need more items, we use malloc to create a new item, and then link it into the list. Our diagram is:
head NULL
head = NULL;
To manipulate nodes on a list, we have basic operations which insert and delete nodes. However it is common for our lists to be kept in sorted order to speed searching and if our data needs to be kept sorted. We will look rst at a simple list, and then at a sorted list. In the following codes we assume a head variable which is a pointer to the head of the list.
The previous code segments are not an exhaustive list of needed code, but introduce the way in which we can manipulate data structures linked by pointers.
1 2
This code fails to work if the head is NULL. This code also fails if the head is NULL.
98
Arbor day
It is often useful to keep items in a list sorted according to some criteria. As a simple example, assume we have a key eld in each of our list nodes:
struct node { struct node *next; int key; char *mydata; } node; typedef struct node *NODEPTR; NODEPTR head=NULL;
If we wish to keep our list sorted in (say) ascending order, we do this by only inserting in the correct position3 :
p1 = head; p2 = p1->next while ( (p2!=NULL) && (p2->key<newitem->key) { p1 = p2; p2 = p1->next; } p1->next = newitem; newitem->next = p2;
We will also need delete and search functions, but these are left as an exercise at the end of the chapter.
Maintaining a balanced binary tree is a little difcult - and the simple tree structure given here may result in an unbalanced tree - so we may not get search times as good as O(log2 n).
99
NULL
NULL
NULL
NULL
NULL
C
makenode.c
NODEPTR makenode (int key, char *mydata) { NODEPTR p; p = (NODEPTR) malloc (sizeof (node)); p>lhs = p>rhs = NULL; p>key = key; p>mydata = (char *) malloc (strlen (mydata) + 1); strcpy (p>mydata, mydata); return p; }
This code segment shows one method of displaying all nodes in the tree. This is a recursive routine, as the tree is a recursive structure (as discussed in class). Our algorithm is:
100 Here is the (recursive) code for displaying the elements in a tree:
CODE LISTING
Arbor day
inordertree.c
void inorderdisplay (NODEPTR this) { if (this != NULL) { inorderdisplay (this>lhs); printf ("Key: %6d, mydata: %s\n", this>key, this>mydata); inorderdisplay (this>rhs); } }
insertnode.c
NODEPTR insert (NODEPTR hd, NODEPTR rec) { if (hd == NULL) return rec; if (hd>key == rec>key) { free (hd>mydata); hd>mydata = rec>mydata; free (rec); } else { if (hd>key > rec>key) { hd>lhs = insert (hd>lhs, rec); } else { hd>rhs = insert (hd>rhs, rec); } } return hd; }
findnode.c
char * find (int key, NODEPTR hd) { if (hd == NULL) { return "Not found!"; } else { if (hd>key == key) { return hd>mydata; } else { if (hd>key > key) { return find (key, hd>lhs); } else { return find (key, hd>rhs); } } } }
101
Questions
1. In section 11.2.1, it is mentioned that the delete code fails to work if the head is NULL. Investigate this - why does it not work? 2. Fix the delete code in section 11.2.1 so that it does work even if the head is NULL. 3. In section 11.2.2, we give a awed insert routine. Write and test a delete and search routine for the same list structure. 4. In section 11.3, a recursive routine to print out a tree is given. Rewrite this using a non-recursive method.
Further study
Textbook, Chapter 12
102
Arbor day
Chapter
12
The open source Mozilla web browser has around 30,000,000 lines of C. Isnt there a joke about the New Zealander who was in a hurry to start raising a family - so rather than wait 9 months with one woman, he planned to only wait one month...
2
103
104
12.2 Makele
The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them. To use make, you must write a le called the makele that describes the relationships among les in your program, and the states the commands for updating each le. In a program, typically the executable le is updated from object les, which are in turn made by compiling source les. Once a suitable makele exists, each time you change some source les, this command:
make
12.3 Congure
105
The make program uses the makele data base and the last-modication times of the les to decide which of the les need to be updated. For each of those les, it issues the commands recorded in the data base. Normally you should call your makele either makele or Makele. Here is a simple makele which manages the recompilation of three C text les (one.c, two.c and main.c):
CODE LISTING all: main.exe
makefile
main.exe: one.o two.o main.o gcc o main.exe one.o two.o main.o one.o: one.c gcc c one.c two.o: two.c gcc c two.c main.o: main.c gcc c main.c
Note that: The unindented lines are dependency descriptions The indented lines are compile-link descriptions - you must use a TAB, not SPACES.
12.3 Congure
The rst step in compiling a new GNU package is often running the congure script in the source directory. This script is an autoconguration system which detects the system idiosyncracies of your computer - such as which operating system, which compiler and so on. The congure script automatically generates a Makele which you can then use to build the new system. The congure system is comprised of several different tools, and program developers must build and install all of these tools. However - people who just want to build programs from distributed sources normally do not need anything except a make program, and a C compiler.
autoconf
provides a general portability framework, based on testing the features of the host system at build time. plied Makele.
automake a system for describing how to build a program, permitting the developer to write a sim-
a standardized approach to building shared libraries. provides a framework for translation of text messages into other languages. autoconf requires the GNU version of m4 - a macro processing language automake requires perl.
106
In this section, we introduced the following topics: Software development in-the-large Revision control systems The makele The GNU congure system
Questions
1. Write a Makele which manages the recompilation of laboratory exercise u4ex6. 2. Download the chess game program from ftp://ftp.gnu.org/pub/gnu/chess/. Run ./congure unix and then make.
Further study
Textbook, Chapter 14, sections 14.5
Contents
107