******** LESSON 1 ******** In lesson 1, we covered a broad introduction to C with detailed commentary on it's complexity and intricacies. It is not necessary for you to take detailed notes on all of this information, it is mostly just here for future reference. HOW TO COMPILE A PROGRAM IN C: There are a few ways to compile, depending on your operating system and IDE (integrated development environment). If you are using MICROSOFT WINDOWS XP or EARLIER: I highly recommend Bloodshed Software's Dev-C++ version 5 beta. It uses the MinGW compiler which is a Windows port of the GCC compiler. It is fantastic, but unfortunately it has not been known to work properly on Windows Vista or later. If you are using MICROSOFT WINDOWS VISTA or LATER: I regret to have to tell you that the best compiler for this operating system is Microsoft's Visual C++ Express Edition 2008. The Express edition is made available for free from Microsoft, although it is stripped down in functionality from the full "professional" or "enterprise" editions, which cost ridiculous amounts of money to purchase. WINDOWS ALTERNATIVE: You may also try downloading the Eclipse C++ IDE for MinGW for Windows, along with MinGW. I have not tested this combination personally but it may work. If you are using APPLE MAC OS X 10.0 or LATER: For Mac OS X, I would recommend using Xcode, which comes pre-installed on most OS X installations, but can also be downloaded freely. If you prefer not to use Xcode, you can also invoke GCC directly from the Terminal, or you can run an IDE like Eclipse with the C++ plugin. If you are using Linux: On Linux, my personal favorite IDE is just KATE (the KDE Advanced Text Editor), and then just invoking gcc directly from a terminal. Otherwise, there are numerous options such as Kdevelop, Eclipse, Gedit, Emacs, Vim, Pico and numerous others. HOW TO INVOKE GCC TO COMPILE A PROGRAM: If your program consists of a single file, it can be compiled like so: gcc -o executable sourcefile.c This compiles the file 'sourcefile.c' and creates a binary executable called 'executable'. If your program consists of multiple source files, it can be compiled like so: gcc -o executable file1.c file2.c file3.c If your program depends on a library, such as the math library, it can be compiled like so gcc -o executable file1.c file2.c -lm where -lm is the math library; or gcc -o executable file1.c file2.c -lyour_library If you wish to see all warning message (highly recommended for keeping your code squeaky clean), run: gcc -Wall -o executable file1.c file2.c -lm If you wish to create debug code for use with GDB, run: gcc -g -o executable file.c If you wish to optimize your executable for speed, run: gcc -O2 -o executable file.c There are numerous (hundreds) of other options that GCC accepts, which we will go over as we encounter the need for them. CHART OF ALL C OPERATORS: Comments: //text a comment (which is ignored by the compiler). comments beginning with '//' are terminated at the end of the line. /*text*/ a comment (which is ignored by the compiler). comments beginning with '/*' are terminated only by the '*/' symbol, thus /**/ comments can span multiple lines, unlike // comments Arithmetic: a * b returns the product of 'a' times 'b' a / b returns the quotient of 'a' divided by 'b' a % b returns the modulus (remainder) of 'a' divided by 'b' a + b returns the sum of 'a' and 'b' a - b returns the difference of 'a' minus 'b' Logical: !a returns the logical truth value of NOT 'a' a && b returns the logical truth value of 'a' AND 'b' a || b returns the logical truth value of 'a' OR 'b' Bitwise: ~a returns the bitwise value of the COMPLEMENT of 'a' a & b returns the bitwise value of 'a' AND 'b' a | b returns the bitwise value of 'a' OR 'b' a ^ b returns the bitwise value of 'a' XOR 'b' (EXCLUSIVE OR) Comparison: a == b returns true if 'a' EQUALS 'b' a != b returns true if 'a' DOES NOT EQUAL 'b' a > b returns true if 'a' is GREATER THAN 'b' a < b returns true if 'a' is LESS THAN 'b' a >= b returns true if 'a' is GREATER THAN OR EQUAL TO 'b' a <= b returns true if 'a' is LESS THAN OR EQUAL TO 'b' Assignment: a = b sets 'a' equal to 'b' and returns the new value of 'a' a *= b equivalent to a = a * b, returning the new value of 'a' a /= b equivalent to a = a / b, returning the new value of 'a' a %= b equivalent to a = a % b, returning the new value of 'a' a += b equivalent to a = a + b, returning the new value of 'a' a -= b equivalent to a = a - b, returning the new value of 'a' a &= b equivalent to a = a & b, ditto a |= b equivalent to a = a | b, ditto a ^= b equivalent to a = a ^ b, ditto Incremental: a++ increases a by 1 unit and returns the value of 'a' BEFORE incrementing ++a increases a by 1 unit and returns the value of 'a' AFTER incrementing a-- decreases a by 1 unit and returns the value of 'a' BEFORE decrementing --a decreases a by 1 unit and returns the value of 'a' AFTER decrementing Pointer operators: *a dereferencer; the value stored at the address pointed to by 'a' also known as the rvalue (right of the = sign, the 'real' value) *a (declaration): declares a variable to be a pointer, e.g. int *a or int **a or int ***a, etc &a referencer; the memory address of the variable 'a' also known as the lvalue (left of the = sign, the 'location' value) a.b returns element 'b' from the structure 'a', when 'a' is in static or local memory. a->b returns element 'b' from the structure 'a', when 'a' is in dynamic memory. equivalent to (*a).b Array indexing: a[b] returns the 'b'th element of the 'a' array. a[b] is the same as (*a + b) Delimiters: ; seperates statements or declarations. every statement and declaration must end with a ; , sperates variables or values, e.g. int a = 0, b[2] = { 1, 2 }; , (for-loop) can be used to seperate multiple statements within a for-loop initializer or iterator, e.g. for (a = 0, b = 10; a < 10; a++, b--) { } (code block) { and } delimit the scope of a code block. all functions must be enclosed within { and }, however, { and } can also be nested within functions to distinguish further different scopes. { } (structures) { and }; are used to enclose all possible elements within struct, union and enum structures, e.g. struct coord { int x, y; }; Literals: 'a' returns the character 'a' (of type char) "string" returns the string "string" (of type char*). string literals are implicitly null-terminated (so they may take up 1 more byte than shown) 0x123456789ABCDEF0 returns the hexadecimal value of 123456789abcdef0. any literal starting with '0x' is regarded as hexadecimal. hexadecimal numbers are case insensitive. hexadecimal is the base-16 number system (digits 0-F). 012345670 returns the octal value of 12345670. any literal starting with '0' is regarded as octal (for better or worse). octal numbers are base-8, with digits 0 through 7 only. 0b10011011 returns the binary value of 10011011 \ (string), when a string contains a \, it is followed by an 'escape' character. such characters are listed below Precedence: (a) parentheses increase the precedent of whatever they enclose. by default, C uses standard algebraic order of precedence (!,*,/,%,||,&&,+,-), so using parentheses allows you to override this e.g.: (a + b) * c would be the same as a * c + b * c Casting: (a)b returns the value of 'b' when cast to type 'a', e.g.: long x = 0x80000000; short y = (short)x; note that explicit casts such as this are required when a possible loss of precision or overflow occurs or when two types may not be compatible. Argument listing: (a, b, c, ...) declares a list of arguments called 'a', 'b', 'c', etc. used in function declarations and function calls. Function declarations: atype a (btype b, ctype c); declares a function called 'a' which returns a value of type 'atype', and which takes as arguments 'b' (of type 'btype') and 'c' (of type 'ctype'). atype a (btype b, ctype c, ...); using the elipsis (...) allows you to create a function with a variable number of arguments (functions like printf and scanf use this). in this case there are two fixed arguments, 'b' and 'c', and any number of arguments after that. Escape characters (occur within strings): \n newline \r carriage return \t tabulator \# the character whose ascii value is #, e.g. \0 for the NULL terminator character. \ a backslash followed by a newline continues the string on the next line, e.g.: "this string starts here and \ ends here" \" the quotation mark character \\ the backslash character "a" "b" strings which are seperated only by whitespace (no ; or other punctuation) are automatically concatenated during compilation. e.g. "tes" "ting" is the same as "testing" Preprocessor: #directive any line starting with a '#' is a preprocessor directive NOTES: 1. LOGICAL TRUTH VALUES: in C, any non-zero expression is regarded to be TRUE (also, non-NULL expressions, since NULL is defined as (void*)0) TABLE OF ALL PRIMITIVE TYPES IN C: char a signed 8-bit character, possible values '\0' through '\255', or -128 through 127. example: char c = 'x', d = 100; a char can be used as a number as well as a character. by default chars are signed (for better or worse). unsigned char the same as a (signed) char, except that it's numeric values are 0 to 255 instead of -128 to 127. Note that if you pass the '-funsigned-char' option to gcc, then 'char' will by default be unsigned. This is particularly important if you cast a char to an int type. short a signed 16-bit integer, ranges: -32768 to 32767. can also be declared as 'short int' (int is implied). unsigned short an unsigned 16-bit integer, ranges: 0 to 65535. can also be declared as 'unsigned short int' (int is implied). long a signed 32-bit integer, ranges: -2^31 to 2^31 - 1 (where ^ is exponent operator as opposed to xor). can also be declared as 'long int'. unsigned long an unsigned 32-bit integer, ranges: 0 to 2^32 - 1. can also be declared as 'unsigned long int'. long long a signed 64-bit integer, ranges: -2^63 to 2^63 - 1. can also be declared as 'long long int'. unsigned long long an unsigned 64-bit integer, ranges: 0 to 2^64 - 1. can also be declared as 'unsigned long long int'. int a signed integer, whose length is architecture dependent. On 32-bit platforms (intel 386, 486, pentium I, II, III, IV, core, Xeon, AMD K5, K6, K7, Athlon, PowerPC, Alpha, DEC and several others), an int is 32- bits. On 16-bit platforms (intel 8086, 8088, 80286), an int is 16-bits. On 64-bit platforms (intel EM64T, Itanium, core 2, AMD Athlon64, Turion64, Phenom, and several others), an int is 64-bits. The size of an int depends ON WHAT ARCHITECTURE YOUR COMPILER TARGETS, not on the OPERATING SYSTEM the program is being compiled on. It is possible, for instance, to target 64-bit architectures on a 32-bit system, even though the resultant code would not run on the current OS. To get 64-bit code, you need a 64-bit compiler, not merely a 64-bit OS. unsigned an unsigned integer, whose length is architecture dependent (see above). can also be declared as 'unsigned int' (int is implied). float an IEEE 754 compliant floating point number (can contain a fractional value). float is 32-bits double an IEEE 754 64-bit floating point number long double an IEEE 754 96-bit floating point number I also mentioned the four phases of compilation: 1. PREPROCESSING Any line in a C program starting with the '#' character is a preprocessor directive. The preprocessing phase takes in a C source file, and outputs a 'preprocessed' C source file. For example, all #define macros are applied throughout the source, all #include files are actually inserted into the source, and all #if conditionally compiled code is processed. Here is a list of all preprocessor directives: #if (condition) code block 1 #elif (condition) code block 2 #else code block 3 #endif The #if directive allows you to conditionally compile code. This is useful if you need a program to compile on multiple different platform architectures, e.g. Windows and Linux. Here is an example: #if (defined(_WIN32)) // this code is only compiled on the Windows platform (Win95 - Win7) #else // this code is compiled on all other architectures #endif #ifdef value code block 1 #else code block 2 #endif #ifndef value code block 3 #else code block 4 #endif The #ifdef directive tests if the constant 'value' is defined, and will compile the enclosed code only if it is. #ifndef is like #ifdef except that it will compile the enclosed code only if the constant 'value' is NOT defined. e.g.: #ifdef _WIN32 // this is the win32 code #else #ifndef HPUX // this code is compiled on other platforms except for HPUX #else // this code is compiled only on the HPUX platform #endif #endif #include #include "local include file" The #include directive includes header files into your program. This is required if you wish to import functions into your program that are defined in another file, for example if you wish to use standard library functions, then you will have to #include the necessary files. Example: #include // for numerous standard library functions #include // for printf, scanf, etc #include // for string functions #include // for standard error functions and the errno global variable #include // for time functions #include "mylibrary.h" // to include functions written in your own library #define MACRO VALUE The #define directive defines a macro. All occurences of MACRO will be replaced by VALUE in the source file. If you do not specify a VALUE, then MACRO will be replaced by nothing in the source file, but all #if defined(MACRO) tests will return true. Macros can also take arguments, for example: #define MACRO(x,y) expression(y, x) This replaces all occurences of MACRO(x, y) with expression(y, x). It keeps the original variables names. For example: MACRO(1, 5) becomes expression(5, 1) MACRO(a, b) becomes expression(b, a) MACRO(i++, j) becomes expression(j, i++) The point is that the macro variables x and y will take on whatever value is specified when calling the macro. NOTE on using macros with expressions that have side-effects: If you define a macro with arguments in which the arguments are evaluated more than once, and those arguments have side-effects (they change your program's state), then using the macro may not be desired. For example, consider the following: #define APLUSBPLUSA(a,b) (a + b + a) if you call APLUSBPLUSA(i++, j) then you end up with: (i++ + j + i++) here i is incremented twice which you may not want. Consider this alternative: int APLUSBPLUSA (int a, int b) { return a + b + a; } now if you call APLUSBPLUSA(i++, j) then i++ is calculated just once, and would be basically the same as (i++ + j + i), which is different from the macro version. 2. COMPILATION The compilation phase takes in a preprocessed C source file, and outputs assembly language code specific to the target archtecture (e.g., Win32 on intel x86 or Win32 on intel xeon or Linux on intel x86, or Solaris on SPARC or MacOSX on PowerPC, etc etc etc). 3. ASSEMBLY The assembly phase takes in assembly code and outputs binary code called 'object files' that contain symbolic placeholders. Symbolic placeholders are variables or functions that exist in external libraries or other object files. 4. LINKING The linking phase takes in object files and outputs the final binary executable file (the .exe or .dll file on windows, or the executable or .so file on linux, or various other binary formats). It is in this phase that all external references are resolved, and code from other libraries is actually inserted into the main code. Depending on whether you use 'static' linking or 'dynamic' linking, the output executable may be dependent on the existence of dynamic libraries in order to run (e.g., winsock code will require that wsock32.dll be present on the target machine). Statically linked code will remove this dependency but will result in a larger and possibly buggier executable. For example, if you statically link against an old version of Microsoft's wsock32.lib, and it is later revealed that that version of winsock has an exploit in it that is only fixed by a "hotfix," then the winsock exploit will always exist in your executable as well, unless you recompile (and re-distribute) your executable against a newer winsock. However, if you dynamically linked against wsock32.lib, then the Microsoft "hotfix" would resolve the exploit in your program automatically. In this case, dynamically linked executables are ideal. On the other hand, your program may contain a large amount of code from a third- party library such as the wonderful QT library. And perhaps your program only uses a small portion of code from the QT library. In this case, having an executable that is dynamically linked to QT may require you to distribute a potentially very large library along with your program, whereas, by statically linked to QT, your executable is only slighly larger and does not require you to distribute the entire QT library with your program. This is very analagous to .NET framework development caveats. If you decide to write a program in C# .NET 2008, then the code will be dependent on the .NET framework 3.0 being present on the target system in order to run. This means that if you wish to distribute your program, you will also have to direct users to download and install the .NET framework 3.0 to use your program, and that download could potential exceed several hundred megabytes. This is one of the biggest drawbacks to developing .NET code - many older systems will not be able to run it without installing a significant amount of dependent software. I digress... ******** LESSON 2 ******** In lesson 2, we covered some of the basic variable types, and input and output mechanisms, as well as basic flow constructs such as for() and while(). Example 1 from lesson 2: 1. write a program to count from 1 to 10 CODE: #include int main () { int i; for (i = 1; i <= 10; i++) { printf("%d\n", i); } return 0; } NOTES: 1. VARIABLES In this program, there is a single variable called 'i' which is of type integer. In general variables are declared in the following ways: type variable; type variable1, variable2; type variable = value; type variable1 = value1, variable2 = value2; Where type is either a primitive type or a user defined type. Primitive types are enumerated above. 2. MAIN - entry point for all programs All programs in C start at the main() function. main() can be defined in one of two ways: int main (); int main (int argc, char **argv); In the latter, we are able to store and process command-line arguments with the variables argc (argument count) and argv (argument values). 3. FOR LOOPS - for(). The syntax of a for() loop is: for (initializer; conditional; iterator) { code; } The 'initializer' is run a single time before the loop starts. The loop then runs as long as 'conditional' is true (in C, true means nonzero. Anything EXCEPT 0 or NULL is true). The loop then runs 'code', and then 'iterator', before checking the conditional and continuing on the next iteration of the loop. Thus, in the example above, first we set i to 1, then we check that i is less than or equal to 10, and then we call the printf() function, then increment i (i++). The above for() loop runs identically to the following while() loop: initializer; while (conditional) { code; iterator; } 4. printf format The printf function takes at least one argument: the format string, followed by any arguments which are specified in the format string. The format string contains the following special symbols: %% replaced with % sign %c replaced with a single character from a char variable %s replaced with a NULL-terminated string from a char* variable %d or %i replaced with a base 10 number from an int variable %ld or %li replaced with a base 10 number from a long int variable %x or %X replaced with a hexadecimal number from an int variable (%x uses lowercase digits, %X uses uppercase digits) %lx or %lX replaced with a hexadecimal number from a long int variable %lld or %lli or %llx or %llX as above, except using a long long int variable. Note that these type symbols work on GCC but are BROKEN on Microsoft's C/C++ compiler. %ud, %ui, %uld, %uli, %ux, %uX, %ulx, %ulX as above except using unsigned integer types %ulld, %ulli, %ullx, %ullX as above using unsigned long long int types, again, broken in Microsoft's compiler. %f replaced with a floating point number from a double variable %lf replaced with a floating point number from a long double variable %p replaced with the numeric memory address of a pointer variable Note: do NOT use %d to print a pointer memory address, use %p instead. Using %d will make your code break on platforms where pointers are not 32-bits in length. Example 2 from Lesson 2: 2. write a program that displays all possible coordinates on a chess board (from a,1 to h,8) CODE: #include int main () { int x, y; for (x = 1; x <= 8; x++) { for (y = 1; y <= 8; y++) { printf("%c,%d\n", (char)(x + 'a' - 1), y); } } return 0; } NOTES: Here, in the printf line, we cast the integer 'x' into a char, by creating an explicit cast in the form of (char)(x + 'a' - 1). What this does is take the integer expression x + 'a' - 1 (remember that in C, chars like 'a' can be used as numbers), and converting it from an int to a char. This conversion can cause a loss of precision, since an int is 32-bits and a char is 8-bits, which is partly why an explicit cast is required here. Here is another way to do the same thing, but instead of casting an int to a char, we will just use a char variable to begin with: CODE: #include int main () { char x; int y; for (x = 'a'; x <= 'h'; x++) for (y = 1; y <= 8; y++) printf("%c,%d\n", x, y); return 0; } NOTES: Here, no cast is required since x is already a char, and again we use the 'a' and 'h' characters like numbers. Finally, notice that I have removed the { } brackets in the for-loops. This is because, if a for-loop only contains a single statement inside it's code block, the { and } are not required, instead, the compiler will simply put the proceeding single statement inside the for-loop. Example 3 from lesson 2: Retrieve a number from the user CODE: #include int main () { int x = 10; printf("type a number:\n"); scanf("%d", &x); printf("you typed: %d\n", x); return 0; } NOTES: The scanf family of functions reads data according to a format string (using the same format string as the printf family of functions), and stores the read data in the locations pointed to by the arguments. Note that here we have &x in the scanf call rather than just x. This is because &x is a referencer, which gives the memory location of the variable x, rather than the value of x itself. This is necessary because scanf needs to store it's data somewhere in memory, so you must give it the memory address where it should write the data. If you were instead to use: scanf("%d", x); Then the value of x (10 by default) is treated as the address wherein to store the integer read from the command line. Since the address '10' is most likely to be out-of-range of the program, the program will crash with a write-access violation exception. NOTE ABOUT KEEPING THE OUTPUT WINDOW OPEN AFTER RUNNING A PROGRAM: If you run a program and find that the output window disappears before you are able to view it, then you will want to insert a line of code into your program at the end to keep the output window open. If you add the line: getchar(); before the 'return 0' line of the main function, then your window will remain open until the user presses enter. Example 4 from lesson 3: Retrieve a string from the user CODE: #include int main () { char buf[100]; printf("type a line of text...\n"); gets(buf); printf("you typed: %s", buf); return 0; } NOTES: The variable 'buf' is a 100-character string. The function gets() reads a line of input from the standard input and stores it in the variable buf. Note that the size of buf is only 100 chars, which means that if you type more than 99 characters when running the program, the program will crash (if you're lucky). NEVER USE gets() In C, all strings must be allocated before they are written to. Since this program only allocates 100 bytes to the variable 'buf', that means that buf can contain no more than 99 characters (and a NULL-terminator). The function gets() has no concept of the amount of memory that has been allocated for input. Thus it is possible to overflow the input string and write data to memory where data should not be written, possibly causing execution exploits or crashes. The proper way to write this program would be: CODE: #include #include #define LEN 100 int main () { char buf[LEN]; printf("type a line of text...\n"); fgets(buf, sizeof(buf), stdin); printf("you typed: %s", buf); getchar(); return 0; } NOTES: Here we have used fgets() to read a line of input from the console. fgets() takes 3 arguments: fgets(destination_buffer, size_of_destination_bufer, source_file) In this case, the destination buffer is the variable called 'buf', and the size can be calculated with the built-in C function called 'sizeof'. sizeof() returns the number of bytes required to hold it's argument, so, for example, sizeof(buf) in this case would return 100, since buf is a 100-char string. sizeof can take specific variables, or variable types as arguments. For example, sizeof(long) would return 4, since a long integer requires 4 bytes (32-bits) to hold. Finally, the source_file we use is one of three built-in "files" that the C standard library provides: stdin, stdout and stderr. stdin is a read-only file that gets its data from standard input stdout is a write-only file that directs to standard output stderr is a write-only file that directs to standard error (which by default redirects to standard output) So, for example, if you wanted to invoke a program from the command line, and you wanted to discard standard output and only view errors, you would type: yourprogram > /dev/null If you want to discard all output including errors, you would type: yourprogram > /dev/null 2> /dev/null A shorthand way to write this would be: yourprogram > /dev/null 2>&1 If you wanted only to view standard output but not standard error: yourprogram 2> /dev/null If you wanted your program to receive it's input from a specific file: yourprogram < inputfile So, to give your program input from a specific file, discard output, and send error output to the end of an error file (appending to it instead of overwriting the file), you would run: yourprogram < inputfile > /dev/null 2>> errorfile Also, note that we have #define LEN 100 in this program. In general, if you are dealing with "magic numbers", that is, arbitrary numbers that could change for nearly any reason, then it is preferable to use constants instead of literal numbers. This is so that changing their values can be done in one simple place instead of having to change every occurence of the magic number throughout the source files. Finally, in our program you may notice we added getchar() right before return 0. This is so that the output screen will remain active until the user hits enter after the program is done running. Example 5 from lesson 2: Finally, we will put all we have learned together and write a program which takes a line of input from the user, reverses the text in that line, and sends it back to the user. CODE: #include #include #define LEN 100; int main () { int i, len; char input[LEN], output[LEN]; printf("enter a line of text:\n"); fgets(input, sizeof(input), stdin); len = strlen(input); for (i = 0; i < len; i++) { output[len - i - 1] = input[i]; } output[len] = '\0'; printf("backwards that is: %s\n", output); return 0; } NOTES: This program has another #include, for . string.h contains numerous standard library string functions, and must be included in order to use the function 'strlen' which occurs within main(). Here again we #define LEN 100 and use char input[LEN], output[LEN] instead of char input[100], output[100]. This is so that we can change this 'magic number' in a single location and have it update everywhere in the program. The line len = strlen(input); sets the variable len to the length of the string stored in the variable input (that is, how many relevant characters the string contains before the NULL-terminator; as opposed to how much memory is allocated to it, which would simply be the constant LEN). The for-loop iterates through every character in the input string. The line output[len - i - 1] = input[i]; is what actually reverses the string. For every character in the input string, it writes that character to the opposite side of the output string. The -1 offset (len - i - 1) is required because in C, arrays always start at index 0, as opposed to 1. This means that a string with a length of 5 characters would have characters at string[0] through string[4]. After the for-loop, the line output[len] = '\0'; sets the character after the last text character in the string to a NULL-terminator ('\0' is the same as the NULL character). THIS IS EXTREMELY IMPORTANT. All strings in C must be NULL- terminated. If this line were not in the program, the string 'output' would contain a bunch of random data after the reversed string, and may cause the program to crash on the final printf() line. Note that fgets() stores the newline in the target string, so when you hit ENTER after typing text, the ENTER (newline) character is stored in the string as well. That is why, in the final printf() line, the format string is: "backwards that is: %s\n" And yet the output will be: backwards that is: elpmaxe Although there is no newline before the '%s' in the format string, a newline will already exist in output string since it came from fgets(). That is all for lesson 2. Notes on lesson 3 to come shortly.