mmapのflags引数にMAP_FIXEDフラグを指定することで、OSの実装によってはゼロ番地を先頭とするページにマップできます
たとえば、i386以降のCPU, Vine Linux 4.0, gcc-4.1.1を使った環境で以下のコードをコンパイルして実行するとゼロ番地にアクセスできます。
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int* p = (int*)mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
*p = 1;
printf("*(int*)%d = %d\n", (int)p, *(int*)0);
return 0;
}
実行すると以下のようになります。
$ gcc main.c
$ ./a.out
*(int*)0 = 1
$
以下、注意点。
Cの仕様上では、start引数に0を指定しても数値の0を指すとは限りません。なぜなら、start引数の型はvoid*であり、Cの仕様ではポインタを書くべきところに書かれた0は、コンパイル時にヌル・ポインタに変換されることが保証されているからです(コンパイラがポインタ型の式だと判断できる場合のみ)。
なお、C++でもほとんど同様のようです。自分には、CとC++の違いは、C言語の場合、#define NULL (void*)0 か #define NULL 0 どちらでもありうるが、C++だと #define NULL (void*)0 がありえないということしかわかりませんでした。
よって、ハードウェア, OS, C/C++のコンパイラによっては、start引数で渡す値がコンパイル時に0ではない値に変換されているかもしれないので、ソースコードでstart引数に0を指定したからといって、実行時にmmapでマップされたページが必ずしもゼロ番地ではないかも知れないことに注意です。
以上、何の役に立つかわからないけど面白かったのでメモっときます。
ちなみにWindowsの場合、ゼロ番地から64KB分のアドレスをユーザプログラムのプログラマがVirtualAlloc APIで確保(予約)することはできません。これは、ヌル・ポインタが指すアドレス先に値を代入しようとしたときに、必ずメモリアクセス違反が発生するようにするために、意図的にそう決まっています
ということは、Windowsが動くような環境において、ヌル・ポインタが0と一致する(させる)のは当然だとMicrosoftは考えているのでしょうね。