What You Will Learn
Every process has an environment list — a set of NAME=value string pairs inherited from its parent. Environment variables are a simple, widely-used mechanism for passing configuration from shell to program, and from parent process to child process.
The environment is a NULL-terminated array of strings of the form NAME=value. It is accessible in C through the global variable extern char **environ.
|
environ
(char** global) ↓
|
→ |
String storage (above stack)
|
| Function | Purpose | Returns |
|---|---|---|
| getenv(“NAME”) | Read value of environment variable | char* to value, or NULL if not set |
| setenv(“N”,”V”,1) | Add or update a variable (copies strings) | 0 on success, -1 on error |
| putenv(“N=V”) | Add or update (uses the string directly — no copy) | 0 on success, nonzero on error |
| unsetenv(“NAME”) | Remove a variable from the environment | 0 on success, -1 on error |
| clearenv() | Erase entire environment (sets environ = NULL) | 0 on success, nonzero on error |
Code Examples
#include <stdio.h>
#include <stdlib.h>
extern char **environ; /* global — points to env list */
int main(void)
{
/* Method 1: getenv — read a specific variable */
char *home = getenv("HOME");
char *path = getenv("PATH");
char *missing = getenv("DOES_NOT_EXIST");
printf("HOME = %s\n", home ? home : "(not set)");
printf("PATH = %s\n", path ? path : "(not set)");
printf("MISSING = %s\n", missing ? missing : "(not set)");
/* Method 2: iterate environ — print all variables */
printf("\n--- Full environment ---\n");
for (char **ep = environ; *ep != NULL; ep++) {
printf(" %s\n", *ep);
}
return 0;
}
/* Partial output:
HOME = /home/ravi
PATH = /usr/local/bin:/usr/bin:/bin
MISSING = (not set)
--- Full environment ---
LOGNAME=ravi
SHELL=/bin/bash
HOME=/home/ravi
PATH=/usr/local/bin:/usr/bin:/bin
TERM=xterm-256color
... */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
/* Set a new variable (overwrite=0 means don't replace if exists) */
setenv("APP_MODE", "development", 0);
printf("APP_MODE = %s\n", getenv("APP_MODE"));
/* Set again with overwrite=1 — this time it replaces */
setenv("APP_MODE", "production", 1);
printf("APP_MODE = %s (updated)\n", getenv("APP_MODE"));
/* putenv — pointer is stored directly, no copy */
/* string must remain valid for the lifetime of the env entry */
static char db_url[] = "DB_URL=postgres://localhost/mydb";
putenv(db_url);
printf("DB_URL = %s\n", getenv("DB_URL"));
/* Remove a variable */
unsetenv("DB_URL");
printf("DB_URL after unset = %s\n",
getenv("DB_URL") ? getenv("DB_URL") : "NULL (removed)");
return 0;
}
/* Output:
APP_MODE = development
APP_MODE = production (updated)
DB_URL = postgres://localhost/mydb
DB_URL after unset = NULL (removed) */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
/* Parent sets a custom environment variable */
setenv("MY_CONFIG", "value_from_parent", 1);
printf("[Parent] Set MY_CONFIG = %s\n", getenv("MY_CONFIG"));
pid_t pid = fork();
if (pid == 0) {
/* Child inherits the environment AT TIME OF FORK */
printf("[Child] MY_CONFIG = %s (inherited)\n",
getenv("MY_CONFIG"));
/* Child changes it — parent is NOT affected */
setenv("MY_CONFIG", "value_from_child", 1);
printf("[Child] MY_CONFIG changed to: %s\n",
getenv("MY_CONFIG"));
exit(0);
}
wait(NULL); /* Wait for child */
/* Parent's copy is unchanged */
printf("[Parent] MY_CONFIG still = %s (child change didn't affect us)\n",
getenv("MY_CONFIG"));
return 0;
}
/* Output:
[Parent] Set MY_CONFIG = value_from_parent
[Child] MY_CONFIG = value_from_parent (inherited copy)
[Child] MY_CONFIG changed to: value_from_child
[Parent] MY_CONFIG still = value_from_parent */
Interview Preparation Questions
When fork() is called, the child gets a copy of the parent’s entire environment list. This is a one-time, one-way transfer at the moment of fork. After that, changes in either process’s environment are independent and invisible to the other.
setenv() copies the name and value strings into newly allocated memory — safe to pass stack variables. putenv() stores the exact pointer you provide directly in the environment — you must ensure the string stays valid (use heap or static storage). Never pass a local char[] to putenv().
NULL. Always check the return value of getenv() before using it to avoid a null pointer dereference: `char *val = getenv(“X”); if (val != NULL) { … }`.
No. SUSv3 says you must not modify it — in most implementations it points directly into the environment storage. If you need a modified copy, use strdup() to duplicate it first, then modify the copy.
Use clearenv() or assign NULL to environ directly. Risk: clearenv() does not free memory previously allocated by setenv() — repeated clearenv()+setenv() cycles in a loop will leak memory. In practice it’s usually called once at startup.
The initial environment strings and the argv strings share a single contiguous region just above the initial stack in the process’s virtual address space. They are placed there by the kernel via execve() before the process starts executing.
