Advanced I/O and Command Line
Command-line arguments are like ordering at a restaurant — you tell the program what you want (argv), the program counts how many items you ordered (argc), and then prepares your order.
Command-Line Arguments
argc and argv
The main function can receive two parameters:
int main(int argc, char *argv[])
argc: argument count, at least 1 (the program name itself)argv: argument vector (array of strings),argv[0]is the program name
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Argument count: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Run:
./program hello world 123
Argument count: 4
argv[0] = ./program
argv[1] = hello
argv[2] = world
argv[3] = 123
atoi, atol, or strtol.
Argument Parsing in Practice
In real projects, command-line arguments typically use - prefixes for options:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
int verbose = 0;
int count = 1;
const char *filename = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
verbose = 1;
} else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
count = atoi(argv[++i]);
} else if (argv[i][0] != '-') {
filename = argv[i];
} else {
fprintf(stderr, "Unknown option: %s\n", argv[i]);
return 1;
}
}
if (filename == NULL) {
fprintf(stderr, "Usage: %s [-v] [-n count] filename\n", argv[0]);
return 1;
}
printf("File: %s, Count: %d, Verbose: %s\n",
filename, count, verbose ? "on" : "off");
return 0;
}
getopt function, which handles both short and long option combinations.
Environment Variables
getenv
char *getenv(const char *name);
Returns the value of the environment variable, or NULL if it doesn't exist.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *path = getenv("PATH");
if (path != NULL) {
printf("PATH = %s\n", path);
}
const char *home = getenv("HOME");
if (home != NULL) {
printf("HOME = %s\n", home);
}
return 0;
}
The extern Variable environ
C defines a global variable environ that points to an array of all environment variable strings:
#include <stdio.h>
extern char **environ;
int main(void) {
char **env = environ;
while (*env) {
printf("%s\n", *env);
env++;
}
return 0;
}
errno Error Handling
The errno Mechanism
When C standard library functions encounter errors, they typically don't crash directly. Instead, they set the global variable errno and return an error value:
#include <errno.h>
errno is not automatically cleared to zero after a successful library function call! You must only check it after a function call has failed.
perror / strerror
void perror(const char *s);
char *strerror(int errnum);
perror outputs s: error message to stderr. strerror returns the text description corresponding to the error code.
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
FILE *fp = fopen("nonexistent_file.txt", "r");
if (fp == NULL) {
perror("fopen");
printf("Error code: %d, Error message: %s\n", errno, strerror(errno));
return 1;
}
fclose(fp);
return 0;
}
fopen: No such file or directory
Error code: 2, Error message: No such file or directory
Example
Complete error handling pattern:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open '%s': %s\n", argv[1], strerror(errno));
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
if (ferror(fp)) {
fprintf(stderr, "Read error occurred: %s\n", strerror(errno));
fclose(fp);
return 1;
}
fclose(fp);
return 0;
}
ferror checks whether a stream has experienced a read/write error, which is different from feof (which checks for end of file).
Standard Streams
C automatically opens three streams when a program starts:
| Stream | Name | Default Device | File Descriptor |
|---|---|---|---|
stdin |
Standard input | Keyboard | 0 |
stdout |
Standard output | Screen | 1 |
stderr |
Standard error | Screen | 2 |
The difference between stdout and stderr: stdout is buffered, stderr is unbuffered. When redirecting, they do not affect each other.
#include <stdio.h>
int main(void) {
fprintf(stdout, "This is normal output\n");
fprintf(stderr, "This is error output\n");
return 0;
}
./program > output.txt
After running this way, "normal output" goes to the file, while "error output" still appears on the screen.
Redirection Principles
At the command line:
>redirectsstdoutto a file2>redirectsstderrto a file<replacesstdinwith a file|connects one program'sstdoutto another program'sstdin
./program 2>&1 all.log
This command merges stderr into stdout and writes both to all.log.
Temporary Files
tmpfile
FILE *tmpfile(void);
Creates a temporary file opened in "wb+" mode. The file is automatically deleted when closed or when the program ends.
#include <stdio.h>
int main(void) {
FILE *tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile");
return 1;
}
fprintf(tmp, "Temporary data %d\n", 42);
rewind(tmp);
char buf[64];
while (fgets(buf, sizeof(buf), tmp) != NULL) {
printf("%s", buf);
}
fclose(tmp);
return 0;
}
Temporary data 42
tmpnam
char *tmpnam(char *s);
Generates a temporary file name that doesn't conflict with existing files. However, there's a race condition — between generating the name and creating the file, another program could create a file with the same name.
tmpfile — it atomically creates and opens the file, making it safer. tmpnam is not safe in multi-threaded environments.
Example
Using a temporary file to process intermediate data:
#include <stdio.h>
#include <string.h>
int main(void) {
FILE *tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile");
return 1;
}
char *lines[] = {"banana", "apple", "orange", "grape"};
int n = 4;
for (int i = 0; i < n; i++) {
fprintf(tmp, "%s\n", lines[i]);
}
rewind(tmp);
printf("Before sorting:\n");
char buf[64];
while (fgets(buf, sizeof(buf), tmp) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
printf(" %s\n", buf);
}
rewind(tmp);
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
char a[64], b[64];
long pos = ftell(tmp);
fgets(a, sizeof(a), tmp);
fgets(b, sizeof(b), tmp);
a[strcspn(a, "\n")] = '\0';
b[strcspn(b, "\n")] = '\0';
if (strcmp(a, b) > 0) {
fseek(tmp, pos, SEEK_SET);
fprintf(tmp, "%s\n%s\n", b, a);
} else {
fseek(tmp, pos, SEEK_SET);
fprintf(tmp, "%s\n%s\n", a, b);
}
fseek(tmp, pos + strlen(a) + strlen(b) + 2, SEEK_SET);
}
rewind(tmp);
}
rewind(tmp);
printf("After sorting:\n");
while (fgets(buf, sizeof(buf), tmp) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
printf(" %s\n", buf);
}
fclose(tmp);
return 0;
}
Before sorting:
banana
apple
orange
grape
After sorting:
apple
grape
orange
banana
Advanced Formatted Output
snprintf
int snprintf(char *str, size_t size, const char *format, ...);
Compared to sprintf, snprintf adds a size parameter to limit the write length, preventing buffer overflow.
#include <stdio.h>
int main(void) {
char buf[10];
int n = snprintf(buf, sizeof(buf), "Hello, %s!", "World");
printf("buf = \"%s\", required length = %d\n", buf, n);
return 0;
}
buf = "Hello, Wo", required length = 13
snprintf is the length that the formatted output should have, not the actual number of bytes written. If the return value is greater than size-1, the output was truncated.
❓ FAQ
Q: Where does tmpfile create its file? A: The location is system-dependent, typically a temporary directory (such as /tmp). You don't need to care about the path because tmpfile returns a FILE, and the file disappears automatically when the program ends.*
📖 Summary
argc/argvreceive command-line arguments, withargv[0]being the program namegetenvreads environment variables,environiterates through all environment variableserrno+perror/strerroris the standard approach for standard library error handlingstdoutis buffered,stderris unbuffered, and they are independent when redirectedtmpfilecreates an automatically-deleting temporary file, which is safer thantmpnam
📝 Exercises
- Write a command-line program that accepts
-cto count characters,-lto count lines, and-wto count words in a file - Write a program that uses
tmpfileto reverse the contents of a file (write everything to a temporary file, then read it back in reverse order) - Write a program that demonstrates the difference between
stdoutandstderrredirection, using>and2>to capture each type of output separately



