CSc 352: Basic C Programming Saumya Debray Dept. of Computer Science The University of Arizona, Tucson debray@cs.arizona.edu What’s new relative to Java? • Some syntactic differences – a lot of the basic syntax is similar • assignment, conditionals, loops, etc. • The biggest differences are conceptual – procedural rather than object-oriented • no notion of classes and inheritance hierarchies – much closer to the machine • debugging sometimes requires thorough understanding of what’s going on at the machine level – explicit dynamic memory management (malloc, free) – pointers 2 A simple program a simple C program that prints out “hello” Points to note: • execution begins at main(): every program must have one • printf() is a standard C library routine 3 A simple program… invoking the C compiler compiler warning executable file produced by compiler executing the program 4 Gcc options: -Wall gcc –Wall generates warnings on questionable constructs • if no return type is specified for a function, it defaults to int • need to supply prototype for “printf” 5 Fixing the compiler warnings specifies prototype for printf() specifies return type explicitly 6 Summary • execution starts at main() – every program should have one • the return type for a function defaults to int – should specify explicitly: good style • need to supply prototypes for functions imported from elsewhere (e.g., standard libraries) – specified using “#include …” 7 Simple declarations and statements two uninitialized global variables, x and y, of type int a variable, z, of type int, that is local to the function main; initialized to 12 print format specifier: %d = “print a decimal value” simple arithmetic expressions and assignment statements 8 Simple conditionals, while loops if statement while statement error message: sent to stderr return value communicates normal/abnormal execution 9 For loops 10 Function calls, recursion function call recursion 11 Formatted Output: printf() • takes a variable no. of arguments: text: Ch. 3 Sec. 3.1 – printf(“…fmtStr…”, arg1, arg2, …, argn) • “… fmtStr…” is a string that can contain conversion specifiers • the no. of conversion specifiers should be equal to n • “regular” (non-%) characters in fmtStr written out unchanged – each conversion specifier is introduced by ‘%’ • this is followed by additional (optional) characters specifying how wide the output is to be, precision, padding, etc. • the conversion specifier indicates how the specified value is to be interpreted: – d = decimal integer, e.g.: printf(“value = %d\n”, n); – x = hex integer, e.g.: printf(“hex value of %d is %x\n”, x, x); – f = floating point number, e.g.: printf(“area = %f\n”, A); 12 Function calls (cont’d) What happens when this printf() is called? • the arguments are evaluated (as in all C function calls) • factorial(6) evaluated • when factorial() returns, the printf is executed: • printf(“ … ”, 720) • This causes the string factorial(6) = 720 to be printed out 13 Formatted Input: scanf() • takes a variable no. of arguments: text: Ch. 3 Sec. 3.2 – scanf(“…fmtStr…”, arg1, arg2, …, argn) • • • • “… fmtStr…” is a string that can contain conversion specifiers the no. of conversion specifiers should be equal to n argi are locations where values that are read in should be placed each conversion specifier is introduced by ‘%’ – similar to conversions for printf • execution behavior: – uses format string to read in as many input values from stdin as it can – return value indicates the no. of values it was able to read • return value of EOF (-1) indicates no further input (“end of file”). 14 scanf() • Specifying where to put an input value: – in general we need to provide a pointer to the location where the value should be placed • for a scalar variable X, this is typically written as &X – Examples: • scanf(“%d”, &n) : read a decimal value into a variable n • scanf(“%d %d %d”, &x, &y, &z) : read three decimal values and put them into variables x, y, z respectively – suppose the input contains the values 12, 3, 71, 95, 101. Then: » x 12; y 3; z 71; return value = 3 – suppose the input contains the values 19, 23. Then: » x 19; y 23; z is unassigned; return value = 2 15 Primitive data types: Java vs. C • C provides some numeric types not available in Java – unsigned • The C language provides only a “minimum size” guarantee for primitive types – the actual size may vary across processors and compilers • Originally C did not have a boolean type – faked it with ints: 0 false; non-0 true • hence code of the form: if ( (x = getnum()) ) { … } // if value read is nonzero – C99 provides some support for booleans 16 Primitive numeric types: Java vs. C Java byte short int ? long C C size unsigned char typically (and at least) 8 bits signed char typically (and at least) 8 bits char typically (and at least) 8 bits unsigned short int typically (and at least) 16 bits signed short int == unsigned short unsigned int 16 or 32 bits signed int same as unsigned int unsigned long int typically (and at least) 32 bits signed long int == unsigned long unsigned long long int typically (and at least) 64 bits signed long long int == unsigned long long Comments signedness is implementation dependent the “natural” size for the machine Note: the keywords in gray may be omitted 17 Signed vs. unsigned values • Essential idea: – “signed” : the highest bit of the value is interpreted as the sign bit (0 = +ve; 1 = ve) – “unsigned” : the highest bit not interpreted as sign bit • For an n-bit value: – signed: value ranges from 2n-1 to +2n-1 1 – unsigned: value ranges from 0 to 2n1 • Right-shift operator ( >> ) may behave differently – unsigned values: 0s shifted in on the left – (signed) negative values: bit shifted in is implementation dependent 18 Booleans • Originally, C didn’t have a separate boolean type – truth values were ints: 0 = false; non-0 = true – still commonly used in programming • C99 provides the type _Bool – _Bool is actually an (unsigned) integer type, but can only be assigned values 0 or 1, e.g.: _Bool flag; flag = 5; /* flag is assigned the value 1 */ • C99 also provides boolean macros in stdbool.h: #include <stdbool.h> bool flag; /* same as _Bool flag */ flag = true; 19 Arithmetic operators: Java vs. C • Most of the common operators in C are as in Java – e.g.: +, ++, +=, -, -=, *, /, %, >>, <<, &&, ||, … • C doesn’t have operators relating to objects: new, instanceof • C doesn’t have >>> (and >>>=) – use >> on unsigned type instead 20 Assignment: l-values increment x twice? what’s going on? 21 Assignment: l-values • The left-hand-side (destination) of an assignment has to be a location – this is referred to as an “l-value” • Consider an expression x ++ ++ the outer “++” operator attempts to increment x++ but x++ is an expression that has no location! error the ++ operator assigns the value x+1 to the location of x the expression ‘x ++’ has the value of x, but no location (as a side-effect, x gets incremented) x has a location and a value 22 What can be an l-value? • l-values: – names of variables of arithmetic type (int, char, float, etc.) – array elements: x[y] – also: • structs, unions, pointers, etc. (to be discussed later) • operations involving pointers (to be discussed later) • Not l-values: – names of arrays, functions – result of an assignment – value returned by a function call 23 Characters and strings • A character is of type char (signed or unsigned) – C99 provides support for “wide characters” • strings : – an array of characters – terminated by a 0 character (“NUL” : written ‘\0’) – Not a predefined type in C; string operations are provided through libraries C arrays don’t do bound-checking – careless programming can give rise to memory-corruption errors 24 Example of string manipulation declares an array of chars the input analog to printf: “read a value into str” (%s = “string”) print out a string (%s) 25 More strings… waits for input (from stdin) typed in on keyboard: stdin program output (stdout) end-of-file indicated by typing Ctrl-D 26 More strings… Oops! this means the program tried to access an illegal memory address 27 Functions from special libraries • Some library code is not linked in by default – Examples: sqrt, ceil, sin, cos, tan, log, … [math library] – requires specifying to the compiler/linker that the math library needs to be linked in • you do this by adding “lm” at the end of the compiler invocation: gcc Wall foo.c lm linker command to add math library • Libraries that need to be linked in explicitly like this are indicated in the man pages 28 Bit Operations • C provides operations to manipulate individual bits of values – done properly and in the right places, this can lead to elegant and efficient code – no. of bits per byte may vary across architectures: the macro CHAR_BIT (defined via #include <limits.h>) gives the no. of bits in a byte. 29 Bit Operations Operation Operator Bitwise complement (unary) ~ Bitwise shift >> << Bitwise AND & Bitwise XOR ^ Bitwise OR | precedence high low 30 Bit Masks • We can select (look at, define) specific bits of a value using bit masks (a fixed bit pattern). E.g.: – for least significant bit in a byte: 0x1 – for most significant bit in a byte: (0x1 << (CHAR_BIT–1)) • More generally: let MASK = 0 … 0i+1 1i 0i-1 … 00 then, for a value x: i only bit i of x: (x & MASK) x with bit i set to 1: (x | MASK) x with bit i set to 0: (x & ~MASK) 31 Example Use: Representing Fixed-size Sets • Intuition: – each distinct element that can be in the set is assigned a bit position – suppose x is a variable that holds such a set, and p is an element whose assigned position is k. Then: • • • • • to add p to x: x = x | (1 << k) to check whether x is in p: evaluate x & (1 << k) set union: bitwise or, | set intersection: bitwise and, & set complement: bitwise negation, ~ • But what if the size of the set is too big for an int? 32 Representing Fixed-size Sets • To handle sets that may be too large to represent using a single word (e.g., int): – each element still gets assigned a fixed position k (now k may be larger than 32 or 64) – use an array of ints (or longs) to represent the set – use a 2-level mapping to map the position k to a bit position in this array: 1 • first figure out which array element we need • then figure out which bit position in this array we need 33 Representing Fixed-Size Sets: Example • Suppose we have: – a set that can contain upto 350 elements – an int holds 32 bits – an element p corresponds to position 212 in the set • Then: – use an array of 11 ints to represent the set (11 x 32 = 352) – p is in element 7 of this array (e.g., A[7]) (element 7 holds positions 192–223) – within element 7 of the array, p is at bit position 20. 34