There are several important differences between C and C++, and we will spend the first part of the class learning how to do things in C, rather than in C++.
So, simple examples:
// Here is a typical C++ comment. int main() { return(0); } |
/* Here is a typical C++ comment. */ int main() { return(0); } |
Some C compilers will accept C++ comments, but some won't. Do not get into the habit of using C++ comments in C programs.
#include <iostream> using namespace std; |
In C, our programs start with different headers:
#include <stdio.h> #include <stdlib.h> |
I'm going to walk you through two procedures in C: putchar() and printf().
First, take a look at putchar(). This is a procedure which takes an integer (or char) and prints out its ASCII value on standard output. So, take a look at the putchar() version of "Hello World:" phw.c.
#include <stdio.h> #include <stdlib.h> int main() { putchar('H'); putchar('e'); putchar('l'); putchar('l'); putchar('o'); putchar(' '); putchar('W'); putchar('o'); putchar('r'); putchar('l'); putchar('d'); putchar('!'); putchar('\n'); return 0; } |
It works, but it's yucky:
UNIX> gcc -o phw phw.c UNIX> ./phw Hello World! UNIX>In phw2.c, I have a better version, which puts "Hello World" into a character array (which we called a "C-Style string" in CS102), and then we traverse it with a for loop and use putchar() to print out each element:
#include <stdio.h> #include <stdlib.h> int main() { char *hw = "Hello World!\n"; int i; for (i = 0; hw[i] != '\0'; i++) putchar(hw[i]); return 0; } |
You'll find putchar() to be useful in a variety of situations; however, 99% of the time, when you want to print output, you will use printf(). Printf() is a bizarre procedure in that it can take a variety of parameters. The first parameter is called a format string, which is a C-style (char *). In its simplest form, printf() takes use that one parameter and prints it out.
So, for example, the printf() version of "Hello World" (in pfhw.c) is even simpler than our previous putchar() example:
#include <stdio.h> #include <stdlib.h> int main() { printf("Hello World!\n"); } |
UNIX> gcc -o pfhw pfhw.c UNIX> pfhw Hello World! UNIX>
#include <stdio.h> #include <stdlib.h> int main() { int i; /* Print out numbers from 1 to 10 */ for (i = 1; i <= 10; i++) printf("%d\n", i); /* Print out all the capital letters */ for (i = 0; i < 26; i++) printf("%c", 'A'+i); printf("\n"); /* Print out all the capital & lowercase letters: */ for (i = 0; i < 26; i++) printf("%c%c", 'A'+i, 'a'+i); printf("\n"); return 0; } |
Note, in that last printf() statement, there are two conversion specifications, and therefore there need to be two parameters following the format string.
UNIX> gcc -o p10 p10.c UNIX> ./p10 1 2 3 4 5 6 7 8 9 10 ABCDEFGHIJKLMNOPQRSTUVWXYZ AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz UNIX>If you want to print a percent sign, simply use two percent signs in the string. Similarly, if you want a double-quote, prepend it with a backslash.
#include <stdio.h> #include <stdlib.h> int main() { int i; double d; /* Forgetting a parameter */ printf("1. %d\n"); /* Trying to print an integer as a double without casting it */ i = 5; printf("2. %lf\n", i); /* Trying to print a double as integer without casting it */ d = 5; printf("3. %d\n", d); /* Trying to print a string as an integer, even though it's a string of an integer */ printf("4. %d\n", "5"); /* Trying to print an integer as a string */ printf("5. %s\n", i); return 0; } |
Here's the program running on my MacBook:
UNIX> gcc -o pfmistake pfmistake.c UNIX> ./pfmistake 1. -1881117246 2. 0.000000 3. 0 4. 8152 Bus error UNIX>And here's it running on hydra3:
UNIX> gcc -o pfmistake pfmistake.c UNIX> ./pfmistake 1. 1075338880 2. 0.000000 3. 0 4. 134513994 Segmentation fault UNIX>On the first four mistakes, you're likely to get a random answer, depending on the kind of machine on which you are running. On the last mistake, you are going to get a bus error or segmenetation violation in almost all cases, because you are treating an integer as a pointer.
The big difference between printf() and scanf() is that you have to provide a pointer to the argument that you want to be read from standard input. So, for example, the following program reads in an integer. Study it carefully (scex):
#include <stdio.h> #include <stdlib.h> int main() { int i; printf("Enter an integer: "); scanf("%d", &i); printf("You entered: %d\n", i); return 0; } |
Note, we passed &i rather than i. This is because scanf() needs the pointer to "fill in" the value of i. If you put i instead of &i, you'll probably get a segmentation violation, which, believe it or not is a good thing since it will alert you to your problem.
Here is a quick example of the program running:
UNIX> gcc -o scex scex.c UNIX> ./scex Enter an integer: 5 You entered: 5 UNIX>That's nice. Here are some weirder examples:
UNIX> ./scex Enter an integer: 55.99 You entered: 55 UNIX> ./scex Enter an integer: 45Fred You entered: 45 UNIX> ./scex Enter an integer: 0000000000000005 You entered: 5 UNIX> ./scex Enter an integer: Fred You entered: -1073746852 UNIX> ./scex Enter an integer: <CNTL-D> You entered: -1073746852 UNIX>If you specify for scanf() to read an integer, it will read the next word and try to convert it to an integer as long as it starts like one. When it hits some characters that don't make sense, it ignores them and returns what it had read so far. This is why it returns 55 and 45 in the first two examples. Obviously, the next example shows that it doesn't care about leading zeroes. The last two exampmles show what happens when you don't give it an integer -- it returns and does not modify i. Since you never set i it has a random value.
So how do you deal with getting bad input or EOF? You use scanf()'s return value. Scanf() returns how many matches it made. If you only ever call it with one conversion specification (as I do), then if it returns 1, you had a match. If it doesn't, you don't. The following program (scex2.c) calls scanf() repeatedly to read integers until it fails:
#include <stdio.h> #include <stdlib.h> int main() { int i; int n; n = 1; while (1) { if (scanf("%d", &i) != 1) return 0; printf("Integer %d. %d\n", n, i); n++; } } |
When we run it, we see a few things. First, scanf() does not care about line breaks -- it simply keeps reading integers until it gets another, whether there are multiple integers on one line or not. It also does not care about blank lines.
UNIX> ./scex2 44 33 22 Integer 1. 44 Integer 2. 33 Integer 3. 22 -5 Integer 4. -5 Fred UNIX>
The following program (scex3.c) gives an example:
#include <stdio.h> #include <stdlib.h> int main() { char s[16]; int i; i = 261303714; while (1) { if (scanf("%s", s) != 1) return 0; printf("s = %s. i = %d\n", s, i); } } |
This program works fine as long as we enter words with 15 characters or fewer. That happens in the first few examples. However, when we give it 1234567890123456, which has 16 characters plus the null character, scanf() writes past the end of the array, and starts overwriting i. That happens in the last example too.
UNIX> gcc -o scex3 scex3.c UNIX> ./scex3 Jim s = Jim. i = 261303714 Jim Plank s = Jim. i = 261303714 s = Plank. i = 261303714 123456789012345 s = 123456789012345. i = 261303714 1234567890123456 s = 1234567890123456. i = 261303552 12345678901234578 s = 12345678901234578. i = 261292088 <CNTL-D> UNIX>Bugs of this sort are disastrous, and if you have ever heard of a buffer overflow attack, this is one of the sources. It is why you should be quite circumspect about using scanf() with strings.