C Input/Output Before we discuss I/O in C, let's review how C++ I/O works. int i; double x; cin >> i; cin >> x; cout << "i = " << i << "\n"; cout << "x = " << x << "\n"; Using the I/O stream cin and cout, you can easily do I/O in C++. The << and >> stream operators are actually bitwise left and right shift operators. e.g., char c = 3; c = c << 2; This code shifts c left 2 bits, so instead of 00000011, we now have 00001100, or 12 as an integer value. In the I/O streams for the C++ standard library, the shift operators have been overridden as follows: ostream cout; ostream& operator<<(ostream &s, int i); ostream& operator<<(ostream &s, double x); Where cout is actually an ostream (output stream), and operator<< is a member function of the osteam class. When the compiler sees a statement such as cout << i; cout << x; the statements are converted into a function calls operator<<(cout, i); operator<<(cout, x); and the references are resolved to the correct ostream member function by matching parameter types. Incidentally, the operator<< member functions returns a stream operator so you can cascade assignments: cout << i << x; Since the shift operators are left associative, cout << i is processed first and then the resulting stream is applied to output x. Streams are passed by reference since moving data to the stream changes the stream's state.
Example of C I/O There are no classes in C. The C language supports structures with data members, but does not support the concept of member functions. All I/O in C is done with function calls. Here's a simple example. #include <stdio.h> int main() { int i, double x; printf("enter integer: "); scanf("%d", &i); printf("enter floating point: "); scanf("%lf", &x); } printf("product = %f\n", i * x); return 0; In this example scanf reads data from stream stdin, and printf prints results on stream stdout. The stdin and stdout streams correspond to cin and cout in C++. Unlike C++, where there's a separate member function for each datatype, there's only one printf function, and one scanf function. This means that we must pass, as a parameter, a format that indicates the datatype. This is indicated as "%d", "%lf", and "%f" in this example. Consider what would happen if we coded the following: scanf("%lf", &i); Here we've specified that we're reading into a double, but have passed a pointer to an int variable. This will compile okay, but at execution time scanf will attempt to copy an 8-byte floating point value into a 4-byte integer! Later in the course, when we cover procedure calls and name mangling, you'll learn why C cannot overload functions based on parameter types and define separate printf functions for each datatype.
Character I/O Prototypes int getchar(void); int putchar(int c); Example // kr/ch01/p017.c #include <stdio.h> // copy input to output int main() { int c; while ((c = getchar())!= EOF) putchar(c); } return 0; Comments Function getchar returns the next single character, and EOF at end of file. EOF is typically defined as -1. To make the EOF marker unique from all other 256 possible characters, getchar returns an int. You can signal EOF to getchar by entering Ctrl-Z (Windows) or Ctrl-D (Unix). printf See K&R, page 243, for a complete description of printf. Prototype int printf(const char *fmt,...); Examples kr/ch01/p006.c kr/ch01/p009.c kr/ch01/p012.c kr/ch01/p015.c kr/ch01/p020.c Function printf returns an integer indicating the number of characters output. Format specifiers include %c (char), %d (int), %f (float/double), and %s (string). The float type, when passed as an argument to printf, is promoted to double by the compiler. This is done because printf has a variable-length argument list (...) in its prototype, and is why %f works for both float and double types. We'll cover variable-length argument lists later in the course. Various attributes may be specified in the specifier, including (in this order): flags: - (left justify) + (sign) 0 (zero fill) minimum field width floating point precision
For example: printf("%d", 25) // output: "25" printf("%-+8d", 25) // output: "+25 " printf("%08.2f", 25.299) // output: "00025.30" scanf See K&R, page 244, for a complete description of scanf. Prototype int scanf(const char *fmt,...); Examples kr/ch07/p158 Function scanf returns an integer indicating the number of fields converted. Fields are specified with format specifiers and include %c (char), %d (int), %f (float), %lf(double), and %s (string). Note that float and double have separate formats. In this case you're passing the address of the variable so scanf will be able to place the scanned value into the variable's location. Consequently, scanf must know whether you are pointing to a float or double. Formats for int, float, and double will skip whitespace until a legal character is found. Whitespace includes spaces, tabs, and newlines. After finding a legal character it will keep scanning until an illegal character, or EOF, is found. int i; float x; scanf("%d%f", &i, &x); 1 2 3. 1 4 1 2. 3 4 1 2, 3. 5 i = 12, x = 3.14 i = 12, x =.34 i = 12, x =??? In the first two cases, scanf will return 2, indicating 2 fields have been successfully converted. A one is returned for the last case, as only the first field was successfully scanned. The character specifier, %c, does not skip whitespace and simply returns the next character.
This can cause problems in programs that fetch single-character prompts from users: int i; char c; printf("enter number: "); scanf("%d", &i); printf("continue? "); scanf("%c", &c); if (c!= 'y') exit(0); // fix: scanf(" %c", &c); This code will fail because the second scanf will read the newline that the user pressed after responsing to the first scanf. To remedy the problem, enter a single space in the scanf specifier. This will cause scanf to match zero or more whitespace characters before matching the response. Line I/O Prototypes char *gets(char *s); int puts(const char *s); Examples char name[80]; printf("enter first and last name: "); if (gets(name) == NULL) exit(0); puts("your name is: "); puts(name); puts("\n"); Function gets fetches the next line (up to a newline), replaces the newline with a null terminator '\0', and returns a pointer to the buf, or NULL if EOF is encountered. Function puts writes the string without appending an extra newline. The last three lines could also be written as printf("your name is: %s\n", name);
File I/O Prototypes FILE *fopen(const char *filename, const char *mode); int fclose(file *fp); // stdin int getchar(void); int scanf(const char *fmt,...); char *gets(char *s); // file input int fgetc(file *fp); int fscanf(file *fp, const char *fmt,...); char *fgets(char *s, int n, FILE *fp); // stdout int putchar(int c); int printf(const char *fmt,...); int puts(const char *s); // file output int fputc(int c, FILE *fp); int fprintf(file *fp, const char *fmt,...); int fputs(const char *s, FILE *fp); // macros int getc(file *fp); int putc(int c, FILE *fp); // same as fgetc // same as fputc Example kr/ch07/p162 Function fopen returns a pointer to structure FILE that tracks the location of the file and includes a buffer for fast I/O. FILE *fopen(const char *filename, const char *mode); Specify access mode as follows: "r" open existing text file for reading "w" create text file for writing; discard previous contents if any "a" append; open or create text file for writing at end of file "r+" open existing text file for update (read/write) "w+" create text file for update (read/write); discard previous contents if any "a+" append; open or create text file for update (read/write), writing at end To do I/O on binary files, include a b after the initial letter. For example, "rb" opens a binary file for reading. The stream, or file pointer, returned by fopen is used to access the file by the remaining file I/O routines. Streams stdin, stdout, and stderr are open by default. Thus, the following are equivalent:
printf("hello world"); fprintf(stdout, "hello world"); c = getchar(); c = fgetc(stdin); putchar(c); fputc(c, stdout); In fact, it might make sense to implement getchar and putchar as follows: #define getchar() #define putchar(c) fgetc(stdin) fputc(c, stdout) The file version of line I/O includes extra checks for buffer overflow. #define N 80 char buf[n]; gets(buf); fgets(buf, N, stdin); Although the two versions both fetch strings, the fgets call is guaranteed not to overflow the buffer.