Software_Library

☆ライブラリ関数

◆標準Cライブラリ
standard C library

※libc
/libディレクトリにおかれている
Linuxでは libc.so.6
-(シンボリックリンク)->実ファイル

_◇ライブラリバージョン
※glibcのバージョン確認
ls -l /lib/libc-* | more

libc-2.3.*.soならglibc-2.3

※GNU libcの一次情報はinfoにある

_◇標準入出力ライブラリ
stdio : standard I/O library

カーネルレベルのストリームにAPI層を追加し、使いやすくする。

①バッファリング
システムコールでは大きな塊で読み書きし、stdioでいったんバッファリングすることで効率をあげる
⇒書き込む場合は改行が書き込まれた時点でバッファが一杯にならなくても書き込まれる
⇒stdioがアンバッファモードの場合は即座に書き込まれる
<setvbuf()でセット>
⇒stderrはアンバッファモードである

②FILE型
typedefされた型。カーネルのファイルディスクリプタやstdioのバッファなどの内部情報を格納している

③標準入出力 FILE*型変数
stdin
stdout
stderr

_◇atoi(3)
#include

int atoi(const char *nptr);
文字列を整数に変換する。失敗しても0が返る。
⇒返り値でエラーを知ることはできない

_◇errno(3)

#include

最後のエラー番号を保持するマクロ。大域変数として扱える。

※エラーが起きたときのみ参照してよい。
⇒成功しても、以前のエラーを保持している場合がある。

※マニュアルで許可されている場合を除き
・  先にerrnoに0をセット
・  errnoが0ならエラーなしと判断するのは不可
⇒成功してもerrnoにセットされる場合がある

_◇exit(3)
プロセスを終了する。リターンしない。

#include
void exit (int status);

※終了ステータスは、慣習上、0で実行成功、0以外で失敗を意味することが多い
⇒0~255の範囲
⇒マクロ EXIT_SUCCESS と EXIT_FAILURE を使う方がよい。

※main()関数での exit(0);と return 0;は本質的に変わりがない。
⇒Cのプログラムはexit(main(argc, argv))が実行されたように起動されるため

※終了ステータスのシェルからの参照
tcsh, csh
⇒  $status
sh, bash
⇒  $?

_◇calloc(3)
#include

void *calloc(size_t nmemb, size_t size);

size * nmemb バイトのメモリをヒープ領域に割り当てる
メモリはゼロクリアしてある
割り当てに失敗するとNULLが返る

⇒確保したメモリはfree()で解放する

_◇fclose(3)
fopen()で開いたストリームを閉じる
成功すれば0、失敗すればEOFを返す。原因はerrno

#include

int fclose(FILE *stream);

_◇fgetc(3), fputc(3)
バイト単位入出力

#include

int fgetc(FILE *stream)
int fputc(int c, FILE *stream)

fgetc()はストリーム末でEOFを返す。

※getc()、putc()は fgetc(), fputc()相当のマクロ
⇒GNU libcでは速度に差はない

_◇fgets(3)
行単位入力

#include

char *fgets(char *buf, int size, FILE *stream);

ストリームから1行読み込んでbufに格納するが、最大size-1バイトに限られる
⇒末尾に\0を書き込んでsizeバイトとなる
⇒正常に1行読み込むか、size-1バイト読んだ場合はbufを返す
⇒1文字も読まずにEOFの場合はNULLを返す
⇒バッファフルなのかsize-1の長さの1行を読んだのかは判別できない

※fgets()の入力には行末の\nが含まれる

_◇fopen(3)
ファイルにつながるストリームを作り、それを管理するFILE型へのポインタを返す
失敗するとNULLを返す。原因はerrno

#include

FILE *fopen(const char *path, const char *mode);

※mode
“r”    読み込み専用
“w”    書き込み専用
“a”    追加書き込み
“r+”  読み書き両用、ファイルは存在しなければならない
“w+”  読み書き両用、ファイルは存在しなくても良い
“a+”  読み書き両用、ファイルが存在すれば末尾に追加

※バイナリファイルを表す”b”も追加できるがLinuxではバイナリとテキストの区別はない。

_◇fputs(3)
文字列bufをstreamへ出力

#include

int fputs(const char *buf, FILE *stream);

問題なく出力できは0以上の数を返す
⇒ストリームに全てのバイト列を書き終えた場合も、何か問題がおきた場合もEOFを返す。errnoを見ないとエラーなのか終了なのか判断できない
⇒あらかじめerrnoを0クリアしておく

_◇free(3)
ヒープ領域に割り当てたメモリを解放する
(直ぐにexit()するならfree()しなくても実害はないが。。。)

#include

void free(void *ptr);

_◇getchar(3), putchar(3)

#include

int getchar(void);
int putchar(int c);

stdin固定のfgetc()相当, stdout固定のfputc()相当

_◇getopt(3)

#include
int getopt(int argc, char * const argv[], const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

コマンドライン引数argc, argvを与え、getoptを呼び出す毎に、オプション文字(-を除く)を得ることができる。optstringには受け付けるオプション文字を与える。文字の後の:は、オプションに引数があることを示す。引数へのポインタが変数optargに代入される。オプション文字列の先頭に+を置くと、オプション対象の操作は、非オプション文字列が現れた時点で終了する。デフォルトでは、argvの順序を変更し、オプションを前にもってくる。
指定外のオプションが見つかると標準エラーにエラーを出力し、optoptにオプション文字を保存し、’?’を返す。変数opterrに0を代入することでエラーの出力を抑止できる。
オプションがそれ以上ないと-1を返す。そのときoptindは、オプションでない最初のargvを示す。

※オプション解析API. ショートオプションだけを認識する

※呼び出す毎に「次のオプション」を返し、オプションがなくなれば-1を返すので、「!= -1」でチェックしながらwhileループを回す。

※関連するグローバル変数
char* optarg  現在処理中のオプションパラメータ
int optind    現在処理中のargvでのインデックス
int optopt    現在処理中のオプション文字
int opterr    真ならエラー時にgetopt()がメッセージ表示をする

※「-」で終了した場合は、optindは「-」を指して終了する
※「–」で終了した場合は、optindは、「–」の次を指して終了する

※GNU libcのgetopt()では、オプションがオプション以外のファイル名などの後に現れても受け付ける。伝統的実装ではオプションは常に先に書かねばならない
例)

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    int flags, opt;
    int nsecs, tfnd;

    nsecs = 0;
    tfnd = 0;
    flags = 0;
    while ((opt = getopt(argc, argv, "nt:")) != -1) {
        switch (opt) {
        case 'n':
            flags = 1;
            break;
        case 't':
            nsecs = atoi(optarg);
            tfnd = 1;
            break;
        default: /* '?' */
            fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    printf("flags=%d; tfnd=%d; optind=%d\n", flags, tfnd, optind);

    if (optind >= argc) {
        fprintf(stderr, "Expected argument after options\n");
        exit(EXIT_FAILURE);
    }

    printf("name argument = %s\n", argv[optind]);

    /* Other code omitted */

    exit(EXIT_SUCCESS);
}

_◇getopt_long(3)

#define _GNU_SOURCE
#include <getopt.h>

int getopt_long(int argc, char * const argv[],
    const char *optdecl,
    const struct option *longoptdecl,
    int *longindex);

struct option {
    const char *name;
    int has_arg;
    int *flags;
    int val;
};

extern char *optarg;
extern int optind, opterr, optopt;

※ロングオプションも解析できるAPI
※第3引数まではgetopt()と同じ

※第4引数に struct optionという構造体の配列を使ってロングオプションの仕様を与える。配列の最後はすべてのメンバを0にする
│ name  ロングオプション名文字列
│ has_arg  no_argument(0)、パラメータをとらない
│     required_argument(1)、パラメータ必要
│     optional_argument(2)、とるかもしれない
│ flag  NULL、valメンバの値を返す
│     非NULL、0を返し、*flagsにvalメンバの値代入
│ val    falgsで指定される返却値
⇒例えばflagをNULLにして、valを文字’h’としてロングオプション”help”にひもづけておけば、–helpにたいしてショートオプション-hを返すこともできる
⇒flagの非NULLの使い方としては、flagにポインタを与えておき、直接valの値をポインタの指す変数に代入するという方法がある。

例)

#include <stdio.h>
#include <stdlib.h>

#define _GNU_SOURCE
#include <getopt.h>

static void do_head(FILE *f, long nlinse);

#define DEFAULT_N_LINES 10

static struct option longopts[] = {
    {"lines", required_argument, NULL, 'n'},
    {"help",  no_argument,       NULL, 'h'},
    {0,0,0,0}
};

int
main(int argc, char *argv[])
{
    int opt;
    long nlines = DEFAULT_N_LINES;
    
    while ((opt = getopt_long(argc, argv, "n:", longopts, NULL)) != -1) {
        switch (opt) {
        case 'n':
            nlines = atoi(optarg);
            break;
        case 'h':
            fprintf(stdout, "Usage: %s [-n LINES] [file file...]\n", argv[0]);
            exit(1);
        case '?':
            fprintf(stderr, "Usage: %s [-n LINES] [file file...]\n", argv[0]);
            exit(1);
        }
    }
    
    if (optind == argc) {
        do_head(stdin, nlines);
    } else {
        int i;
    
        for (i = optind; i < argc; i++) {
            FILE *f;
    
            f = fopen(argv[i], "r");
            if (!f) {
                perror(argv[i]);
                exit(1);
            }
            do_head(f, nlines);
            fclose(f);
        }
    }
    exit(0);
}

static void
do_head(FILE *f, long nlines)
{
    int c;
    
    if (nlines <= 0) return;
    while ((c = getc(f)) != EOF) {
        if (putchar(c) < 0) exit(1);
        if (c == '\n') {
            nlines--;
            if (nlines == 0) return;
        }
    
}

_◇getpid(2)
プロセスIDを得る

_◇gets(3)
バッファオーバーフローを起こすので使用しない

_◇malloc(3)
#include <stdlib.h>
void *malloc(size_t size);

sizeバイトのメモリをヒープに割り当てる
戻り値はvoid型なので、適切なポインタにキャストする
⇒割り当てに失敗するとNULLが返る
⇒メモリは初期化されない
⇒free()で解放する

_◇perror(3)

#include <stdio.h>

void perror(const char *s);
現在のerrnoの値から、エラーメッセージを出力する。
文字列引数はそのまま出力される。(何を出力してもよい)

_◇printf(3), fprintf(3)

#include <stdio.h>

int printf(const char *fmt, …);
int fprintf(FILE *stream, const char *fmt, …);

※単なる文字列出力として*fmtのところで文字列を出力すると中に%が入っていると引数をとろうとするので注意

_◇puts(3)
標準出力への文字列出力。かならず末尾で\nも出力する

#include <stdio.h>

int puts(const char *buf);

_◇realloc(3)
#include <stdlib.h>

void *realloc(void *ptr, size_t size);

malloc()で割り当てたメモリのサイズをsizeバイトに拡張もしくは縮小する
⇒返されるptrは移動する可能性があるが、内容はコピーされる。
⇒割り当てに失敗するとNULLが返る
⇒割り当てに失敗してもfree()で解放する必要がある
⇒よって、realloc()の戻り値をチェックなしに元のポインタに代入してしまうとfree()もできなくなる。

使用例)
void *tmp = realloc(ptr, ...);
if (!tmp) {
    /* error */
}
ptr = tmp;

_◇scanf(3)
フォーマット付入力

⇒バッファオーバーフローの危険がある

_◇strtol(3)
文字列からlong型への変換

long型の範囲を超える大きな数を与えてもLONG_MAXが返る。
数字の後に数字以外の文字が続く場合、エラーにならず数字部分のみが変換される。

※strtolは成功時にerrnoの値を変更することはない
⇒予めerrnoに0を代入しエラー検出することができる。

※strtolは変換をやめた位置を第二引数にセットする

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    long val;
    char *end;

    errno = 0;
    val = strtol(argv[1], &end, 0);
    if (errno != 0) {
        perro("strtol");
        exit(1);
    }
    if (end[0] != '\0') {
        fprintf(stderr, "Non-digit character: %c\n", end[0]);
        exit(1);
    }
    printf("%ld\n", val);
}

_◇syscall(2)
間接システムコール

_◇system(3)
シェルコマンドを実行する

#include <stdlib.h>

int system(const char *command);

例)
system(“ls foo”);

_◇ungetc(3)
バイト単位でバッファに戻す
⇒連続してungetc()はできない。1バイトだけ

#include <stdio.h>

int ungetc(int c, FILE *stream);

※文字列を読んで行って、「ではない」文字を読んで区切りと認識するような処理で使用する。

◆ライブラリ形式

_◇静的ライブラリ
ライブラリの中のオブジェクトファイル単位にリンク時に実行可能ファイルにコピーされリンクされる。

アーカイブファイル(.a)
複数のオブジェクトファイルを1つのファイルにまとめたもの。

静的ライブラリの作成例)
% cc -c -o foo.o foo.c
% cc -c -o bar.o bar.c
% ar ruv libfoo.a foo.o bar.o

静的ライブラリのリンク例)
libfoo.aがライブラリのディレクトリにインストールされていること。
%cc -o baz baz.o -lfoo

_◇共有ライブラリ
複数のプロセスでメモリを共有して参照する。

共有ライブラリの作成例)
% cc -fPIC -c -o foo.o foo.c
% cc -fPIC -c -o bar.o bar.c
% cc -shared -Wl,-soname,libfoo.so.0 -o libfoo.so foo.o bar.o

※共有ライブラリのリンクは、静的ライブラリと同じ手順だが、処理は異なる。

_◇ELF
Executable and Linking Format

主にGNU/Linuxで使われるバイナリフォーマット
ヘッダ構造は elf.h に記述されている。

※ファイル先頭の4バイトは
7F 45 4C 46
「45 4C 46」=ELF

_◇COFF
Common Object File Format

_◇PE
Portable Executable

Windowsの実行形式
0000番地 MZ(4D 5A)
0080番地 PE(50 45)

◆wrapper

まわりをAPIで覆って機能を追加する層のこと

◆著名なライブラリ、その他の使用記事

_◇Boost C++ Library
http://www.boost.org/

※BoostライブラリをCmakeする場合の練習記事
ソフトな忘却力(5) CMakeLists.txtにリンク・ライブラリ指定、Boost

_◇getopt

コマンドライン引数の処理
※getoptを使った実習記事が以下に。

ソフトな忘却力(1) リモート接続でLinux Cプログラミング getopt

_◇libconfig

ファイルに書き込まれたコンフィギュレーション設定を読み取る
※libconfigを使った実習記事が以下に。

ソフトな忘却力(2) ラズパイ4でCプログラミング、libconfig

☆ABI
Application Binary Interface

オブジェクトファイルやライブラリファイルが問題なくリンクできるように、必要な規約を定めたもの

①変数型のバイト数
②CPUレジスタの役割
③構造体、共用体のデータ構造
④関数への値の渡し方、関数の返却値の取得方法