C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
93: Environment Variables: getenv, setenv
A few years ago, I was reviewing a pull request for a tool that handled database migrations. The developer had hardcoded the database password and the server IP address directly into a header file. My first reaction wasn't just "this is a security nightmare," but "this is a maintenance disaster." Every time they wanted to move the tool from a local test environment to a staging server, they had to change the code and recompile the entire binary. I sat them down and showed them how to pull those values from the environment instead. It changed their workflow from "edit-compile-deploy" to simply changing a line in a shell script.
Reading the Shell's State with getenv
In C, the environment is essentially a collection of strings—key-value pairs—passed to your program by the operating system. To grab one of these, we use getenv() from <stdlib.h>. It takes the name of the variable you're looking for and returns a pointer to the value string.
#include <stdio.h>
#include <stdlib.h>
int main() {
char *api_key = getenv("APP_API_KEY");
if (api_key == NULL) {
printf("Error: APP_API_KEY is not set. The app cannot start.\n");
return 1;
}
printf("API Key loaded successfully: %s\n", api_key);
return 0;
}
One thing you need to be careful about: getenv() returns a pointer to the actual environment string. Do not attempt to free() this pointer, and be wary about modifying the string directly. If you need to manipulate the value, copy it into your own buffer using strcpy or strdup first. I've seen plenty of segmentation faults from developers trying to free() something they didn't malloc().
Manipulating Variables with setenv
While you'll spend most of your time reading environment variables, there are cases where your program needs to set them—perhaps to configure a child process you're about to launch via exec(). For this, we use setenv(). It takes three arguments: the name, the value, and an integer flag that decides whether to overwrite the variable if it already exists.
#include <stdlib.h>
#include <stdio.h>
int main() {
// setenv("NAME", "VALUE", overwrite)
// 1 means overwrite, 0 means leave it alone if it exists
if (setenv("LOG_LEVEL", "DEBUG", 1) == 0) {
printf("Log level set to DEBUG\n");
}
char *current_log = getenv("LOG_LEVEL");
printf("Current environment LOG_LEVEL: %s\n", current_log);
return 0;
}
You might see putenv() in older codebases. Avoid it if you can. putenv() is trickier because it doesn't copy the string you give it; it actually puts the pointer you provide into the environment. If that pointer was to a local variable on the stack, your environment becomes a minefield of dangling pointers the moment the function returns. setenv() is much safer because it manages the memory for you.
📋 Practical Task
Build a Dynamic Configuration Loader
Create a program that simulates a service starting up. The program should check for two environment variables: DB_PORT and DB_HOST.
- If both variables are present, print a message saying "Connecting to [HOST] on port [PORT]...".
- If
DB_PORTis missing, the program should usesetenv()to set a default port of "5432" and notify the user that a default is being used. - If
DB_HOSTis missing, the program should print a critical error and exit with a non-zero status, as the host cannot be guessed.
Test your program by running it in the terminal with different setups (e.g., DB_HOST=localhost ./config_loader).
There are no comments for now.