It's not about the parent / child relationship.
When a process forks a child, the child is an almost exact copy of the parent, so it inherits everything.
$ var=1; (echo "$var"; var=2); echo "$var"
Where most shells implement the
(...) subshell environment by forking a child process, that child process has access to the same data as the parent.
The environment variables are about preserving data (in that case
key=value strings) across execution of a new command within a same process.
When you do:
$ export VAR=value
$ printenv VAR
The shell forks a child process (which inherits everything), but then in that child process executes the
printenv command (and with
exec printenv VAR, the forking is skipped).
It's only at that point that the memory of that child process is completely wiped before loading the new executable, so everything is lost. The environment variables are passed in the third argument of the
execve("/usr/bin/printenv", ["printenv", "VAR"], environ) system call.
The shell places the
VAR=value inside that
environ array of strings for
printenv to retrieve it, and in Bourne-like shells, only the variables that have been marked with the
export attributes are put there (with some variation in behaviour when those variables are not scalar variables).
Some shells like
rc and derivatives put all their variables in the environment even the array ones (which they need to encode some specific way which is only understood by other instances of themselves).
The Bourne shell had env vars even more separated from shell vars in that shell variables were created from env var on startup, but you had to export those shell variables even if they were initially imported from the environment for any modification made to them to be propagated to executed commands. It's similar in the C-shell, where you have to use
setenv instead of
set to set values of variables if you want the modification to be exported to executed commands.
ksh also exports the attributes of exported variable via the special
A__z env var.
bash can also export its options to other executed
bash instances via the
BASHOPTS environment variables.
perl, the environment variables are mapped to the
%ENV associative array variable.
Note that env var names can contain any sequence of bytes other than
= and NUL, and the third argument of
execve() can contain more than one definition for a same variable and even strings that don't contain
= characters, while shells have much greater restrictions as to what their variable names may contain. You'll find some variation in behaviour as to what shells do with environment strings that can't be mapped to shell variables.