C言語プログラムの冒頭でincludeすることの多いstdio.hについて書きます。
stdio.hにはprintf関数のプロトタイプ宣言が書いてあるだけで、関数の本体は/usr/lib/libc.aの中にあります。
/usr/lib/libc.aの中に葉C標準ライブラリ関数のプログラムがバイナリコード形式で格納されています。
libc.aは-lmのオプションをつけなくてもコンパイルのときに自動的にリンクされます。
したがって、printfのプロトタイプ宣言を行ってあげれば、stdio.hファイルをインクルードする必要はないのです。
printfのプロトタイプ宣言の書き方を調べるには、UNIXオンラインマニュアルのSYNOPSISを調べます。
$ man 3 printf
SYNOPSIS
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
よって以下のようにすればHelloworldをヘッダファイルのインクロードなしでコンパイルすることが加納です。
/* hello.c */
int printf(const char *, ...);
int main(void)
{
printf("Hello, world!!\n");
return 0;
}
$ gcc -o hello hello.c
$ ./hello
Hello, world
ウィーン大学で生命科学者をしている今鉄男と申します。生命の尊さ、美しさがどのようなメカニズムで生じるのかに興味があります。科学の方法論を用いて、このような問いに応えたい、私はこう思って医学生物学の基礎研究のトレーニングを受けてきました。生命を科学的手法を用いて理解を試みる上で、genomeを始めとした種々の大量データの処理が必要不可欠であることを痛感しました。また、生命科学が物理学、数学、統計学、有機化学などの種々の学問と深い関わりを持つことを実感しました。そのため、このブログは広範囲の学問領域に関しての記事を載せています。日々の学習内容を文書に書き残し、それを読み返すことによって、体系化された知識を身に付けることを目標としています。どうぞよろしくお願いします。
メモリとセグメント
プログラムはメモリをセグメントよばれる領域に分割してデータを格納しています。
セグメントには、
○スタックセグメント
ローカル変数、引数が収める
○データセグメント
グローバル変数を収める
○テキストセグメント(コードセグメント)
プログラムのコード部分を収める
の三つの種類があります。
下のプログラムは三つのセグメントのアドレスの具体例を表示させるものです。
#include<stdio.h>
int A,B;
int main(void)
{
int a,b;
printf("###ローカル変数はスタックセグメント格納###\n");
printf("a = %p\n", &a);
printf("b = %p\n", &b);
printf("###グローバル変数はデータセグメント格納###\n");
printf("A = %p\n", &A);
printf("B = %p\n", &B);
printf("###関数はテキストセグメント(コードセグメントへ格納)###\n");
printf("printf = %p\n", printf);
printf("main = %p\n", main);
return 0;
}
セグメントには、
○スタックセグメント
ローカル変数、引数が収める
○データセグメント
グローバル変数を収める
○テキストセグメント(コードセグメント)
プログラムのコード部分を収める
の三つの種類があります。
下のプログラムは三つのセグメントのアドレスの具体例を表示させるものです。
#include<stdio.h>
int A,B;
int main(void)
{
int a,b;
printf("###ローカル変数はスタックセグメント格納###\n");
printf("a = %p\n", &a);
printf("b = %p\n", &b);
printf("###グローバル変数はデータセグメント格納###\n");
printf("A = %p\n", &A);
printf("B = %p\n", &B);
printf("###関数はテキストセグメント(コードセグメントへ格納)###\n");
printf("printf = %p\n", printf);
printf("main = %p\n", main);
return 0;
}
実行例
###ローカル変数はスタックセグメント格納###
a = 0xbf832c3c
b = 0xbf832c38
###グローバル変数はデータセグメント格納###
A = 0x804a020
B = 0x804a024
###関数はテキストセグメント(コードセグメントへ格納)###
printf = 0x8048318
main = 0x80483f4
スタックオーバーフローを出す
関数を呼び出す度に、メモリ上のスタック領域にはローカル変数とリターンアドレスが格納される。
下のプログラムは、関数functionの中で1000個の要素を持つ配列を確保した後、再度自らを呼び出してまた1000個の要素を持つ配列を確保する関数です。
無限に配列が作成されるためスタック領域は容量オーバーをおこしてしまします。このことを「スタックオーバーフロー」と呼びます。
#include<stdio.h>
void function()
{
char str[1000];
printf("%p\n", str);
function();
}
int main(void)
{
function();
return 0;
}
実行例
0xbff405b4
0xbff401a4
0xbff3fd94
0xbff3f984
0xbff3f574
0xbff3f164
0xbff3ed54
0xbff3e944
0xbff3e534
0xbff3e124
0xbff3dd14
0xbff3d904
・・・・以下続く・・・・
グローバル変数とローカル変数
グローバル変数とローカル変数のメモリ領域の違い
グローバル変数(var1_global, var2_global)とローカル変数(var_local)を宣言して、各々の
#include<stdio.h>
int var_global;
void funcA(void)
{
int var_local2;
printf("var_local2 : %p\n\n", &var_local2);
return;
}
int main(void)
{
int var_local1;
printf("var_global : %p\n\n", &var_global);
printf("var_local1 : %p\n\n", &var_local1);
funcA();
return 0;
}
実行結果
var_global : 0x804a01c
var_local1 : 0xbfc48efc
var_local2 : 0xbfc48ecc
ローカル変数とスタック
C言語において、ローカル変数はスタックと呼ばれるメモリの領域に記憶されるようです。
スタックといえば情報科学でよくでてくるLIFO(Last In First Out)の形式で要素の出入りが管理される処理形態ですね。
下の例では、main関数から呼び出されたfuncAの中でvar2=100が宣言しています。
funcBでは、初期化をせずにvar3を宣言しています。そのため、var2の内容がスタックから開放されずに、var3の内容になってしまいました。
#include<stdio.h>
void funcA(void)
{
int var2=100;
printf("var2 = %d in funcA\n\n",var2);
return;
}
void funcB(void)
{
int var3;
printf("var3 = %d in funcB\n\n",var3);
return;
}
int main(void)
{
int var1=777;
printf("var1 = %d in main function\n\n",var1);
funcA();
funcB();
return 0;
}
実行結果
var1 = 777 in main function
var2 = 100 in funcA
var3 = 100 in funcB
登録:
投稿 (Atom)