LoginSignup
18
21

More than 5 years have passed since last update.

[C言語] ライブラリの関数などメモ

Last updated at Posted at 2015-03-24

よく忘れるのでメモ。随時アップデート中。


I/O編

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

open()は指定したパスのファイルをオープンし、そのファイルディスクリプタを返します。

戻り値

オープンに成功した場合はそのファイルディスクリプタを返し、エラーが発生した場合は-1を返します。
その場合、errnoに適切な値が設定されます。


close

#include <unistd.h>

int close(int fd);

戻り値

成功した場合は0を、エラーの場合は-1を返します。
エラーの場合はerrnoに適切な値が設定されます。


creat

#include ...

int creat(const char* pathname, mode_t mode);

write

ファイル(ソケット)へのデータ書き込み

#include <unistd.h>

int write(int handle, void *buf, unsigned n);
パラメータ 説明
int handle ファイルハンドル
void *buf データアドレス(ポインタ)
unsigned n データの大きさ

戻り値は書き込みに成功した場合は書き込んだバイト数を、失敗した時は-1を返します。


read

ファイル(ソケット)からのデータ読み込み

#include <unistd.h>

int read(int handle, void *buf, unsigned n);
パラメータ 説明
int handle ファイルハンドル
void *buf データを格納するアドレス(ポインタ)
unsigned n データの大きさ

戻り値は読み込みに成功した場合は読み込んだバイト数を、失敗した時は-1を返します。


sscanf

#include <stdio.h>

int sscanf(const char *str, const char *format, ...);

strから、書式(formatにしたがってscanf関数と同様の変換を続く変数のアドレスに格納する。

example

char *text = "Name: edo Age: 20";
char output1[256];
int  output2;
char *format = "Name: %s Age: %d";

sscanf(text, format, output1, &output2);

printf("名前: %s、歳: %d\n", output1, output2); // => "名前: edo、歳: 20"

sprintf

書式formatにしたがって、printf()関数と同様の変換を行った出力を、変数strに格納します。

#include <stdio.h>

int sprintf(char *str, const char *format, ...);

戻り値

成功時:strに格納した文字数(最後の`\0'は除く)
失敗時:EOF


select

select()システムコールは同期的多重I/Oの仕組みを提供します。

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

select()システムコールは渡されたファイルディスクリプタセットのいずれかがI/O可能になるまで、または指定された時間が経過するまで内部でブロックすします。

タイムアウトに利用されるtimeval構造体の宣言は以下のようになっています。

#include <sys/time.h>

struct timeval {
    long tv_sec;  // seconds
    long tv_usec; // microseconds
};

select()に渡すファイルディスクリプタの種類は3種類あり、readfdsには読み取り用の、writefdsには書き込み用の、そしてexceptfdsには例外または帯域外データ(緊急データ、急送データ(out-of-band data))それぞれのイベント発生を待ちます。

渡したファイルディスクリプタのどれかがI/O可能になると、I/O可能になったファイルディスクリプタだけを残すように、渡されたファイルディスクリプタセットを変更します。
つまり、残されたファイルディスクリプタを調べればどのファイルディスクリプタがI/O可能になったかが分かる、というわけです。

ちなみに、最初の引数のnには、渡すファイルディスクリプタの最大値+1の値を渡します。
(内部的にはその値までの間のファイルディスクリプタを捜査するということ?)

ファイルディスクリプタの操作

select()システムコールに渡すファイルディスクリプタセットは直接には操作しません。
操作用のヘルパーマクロが用意されているのでそれを利用します。

fd_set writefds;

// ファイルディスクリプタセットからすべてのファイルディスクリプタを削除します。
FD_ZERO(&writefds);

// 第一引数のファイルディスクリプタをセットに追加します。
FD_SET(fd, &writefds);

// 第一引数のファイルディスクリプタをセットから削除します。
FD_CLR(fd, &writefds);

// 第一引数のファイルディスクリプタがセット内にあるかを調べます。
// セットされている場合は非0を、されていない場合は0を返します。
FD_ISSET(fd, &writefds);

mmap

mmap()システムコールは、ファイルディスクリプタfdに対応するファイルをメモリにマッピングします。
offsetバイト目からlenバイト数分の領域をマッピングします。

これをする大きな利点は、ユーザー空間とカーネル空間でのバッファのコピーなどのオーバーヘッドを省略し、メモリアクセスと同じ方法でファイルへ読み書きできるようになる点です。

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

munmap

munmap()システムコールは、mmap()システムコールでマッピングしたメモリ領域を解放します。

#include <sys/mman.h>

int munmap(void *addr, size_t len);

munmap()システムコールは、プロセスアドレス空間のaddrからlenバイト数分のページを削除します。


stat

stat()システムコールはファイルのメタ情報を取得します。

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

stat()は普通のファイルのパスを、fstat()はファイルディスクリプタを渡します。
*bufには指定したファイルのメタ情報が格納されます。

lstat()はシンボリックリンクの場合に動作が異なり、参照先のファイルではなくシンボリックリンク自身の情報を返します。

3つのシステムコールはどれも、処理が成功すると0を返し、ファイルのメタ情報をstat構造体に格納します。
エラーが発生した場合は-1を返します。

stat構造体

stat構造体は以下のように宣言されています。

#include <sys/stat.h>

struct stat {
    dev_t st_dev;
    ino_t st_ino;
    mode_t st_mode;
    nlink_t st_nlink;
    uid_t st_uid;
    gid_t st_gid;
    dev_t st_rdev;
    off_t st_size;
    blksize_t st_blksize;
    blkcnt_t st_blocks;
    time_t  st_atime;
    time_t st_mtime;
    time_t st_ctime;
};

getenv

ホスト環境が提供する環境リストから、nameにマッチする文字列を検索します。

#include <stdlib.h>

char *getenv(const char *name);

プロセス編

system

#include <stdlib.h>

int system(const char *string);

system関数はコマンドプロセッサへstringの文字列を渡し、実行します。

sample

lsコマンドを実行して/tmpファイル経由で取得する。

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

int main() {
    char filename[80];
    char str[512];
    char *ptr;
    FILE *fp;

    sprintf(filename, "/tmp/ls%d.tmp", getpid());
    sprintf(str, "ls -1 > %s", filename);
    system(str);

    if ((fp = fopen(filename, "r")) == NULL) {
        fprintf(stderr, "Error!\n");
        return 1;
    }
    while(1) {
        fgets(str, 512, fp);
        if (feof(fp)) {
            break;
        }
        ptr = strchr(str, '\n');
        if (ptr != NULL) {
            *ptr = '\0';
        }
        printf("%s\n", str);
    }
    fclose(fp);

    sprintf(str, "rm -f %s", filename);
    system(str);

    return 0;
}

pipe

pipe()はパイプを実行する関数です。

#include <unistd.h>

int pipe(int pipefd[2]);

引数にはint型の配列を渡します。
ここに、パイプの書き込み用と読み込み用のファイルディスクリプタが渡されます。
(ちなみに添字0が読み込み用、添字1が書き込み用)

sample

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

int main() {
    int pipefd[2];
    if (pipe(pipefd) < 0) {
        perror("pipe");
        return 1;
    }

    char *s = "test";
    write(pipefd[1], s, strlen(s));

    char buf[128];
    read(pipefd[0], buf, sizeof(buf));

    printf("buf=[%s]\n", buf);
    close(pipefd[1]);
    close(pipefd[0]);

    return 0;
}

exec

バイナリをメモリにロードし、実行します。

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

第一引数に実行するバイナリのパス、第二引数以降は可変引数となり、実行するプログラムに渡す引数となります。
ただし、Unixの規約に従い、プログラムに渡す第一引数(execl的には第二引数)は該当プログラムパスの最後の要素を渡します。使用例は以下です。

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

int main(void) {
    int ret;

    // 現在起動しているプロセスを`vi`に置き換える。
    ret = execl("/bin/vi", "vi", NULL);
    if (ret == -1) {
        perror("execl");
        return 1;
    }

    return 0;
}

戻り値

起動エラーの場合は-1を返します。
起動成功した場合は関数はリターンしません。


fork

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

戻り値

fork()の戻り値は、親プロセスは生成した子プロセスのpidとなります。
子プロセス側は0が返されます。
生成に失敗した場合は-1が返されます。

実行例

さっきのexeclをベースに、新規プロセスからviを起動してみます。

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

int main(void) {

    pid_t pid;

    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (!pid) {
        int ret;

        ret = execl("/usr/bin/vi", "vi");
        if (ret == -1) {
            perror("execv");
            return 1;
        }
    }

    printf("Success to create a new process.\n");

    return 0;
}

exit

exit()関数はライブラリ関数です。(システムコールではありません)
この関数はatexti()関数やon_exit()関数で登録された関数群を登録したのとは逆順で実行し、プロセスを終了させます。

プロセスを終了するシステムコールは_exit()です。

#include <stdlib.h>

void exit(int status);

_exit

_exit()システムコールはプロセスを終了させます。
ただ、前述のexit()関数を呼び出すべきであり、直接呼んでもあまり意味はありません。

#include <unistd.h>

void _exit(int status);

atexit

#include <stdlib.h>

int atexit(void (*function)(void));

exit()関数実行時に呼び出される関数を登録します。

登録する関数のプロトタイプは以下です。

void my_function(void);

戻り値、引数ともにありません。


on_exit

SunOS4は、atexit()と等価なインターフェースを独自に実装しました。
Linuxでもglibcで実装されています。

#include <stdlib.h>

int on_exit(void (*function)(int, void *), void *arg);

動作はatexit()と同じですが、登録するプロトタイプが少し異なります。

void my_function(int status, void *arg);

on_exit()では引数を取ることができます。


wait

親プロセスが子プロセスの終了を待つケースがあります。
そのときに使うのがwait()です。

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

wait()は終了した子プロセスのプロセスIDを返します。エラーが発生した場合は-1です。

ゾンビプロセス

子プロセスが先に終了した場合、カーネルは親プロセスがそれを確認できるよう、プロセスを構成するのに最低限必要な情報のみを残した特別な状態に遷移させます。
これを「ゾンビ」と呼びます。

この状態のプロセスは、親プロセスから終了確認されるのを待ち、確認された時点でゾンビ状態から抜け、正式に終了します。


waitpid

複数の子プロセスを起動し、特定の子プロセスの終了を待ちたい場合があります。
その場合、wait()では特定の子プロセスのみを監視するには向きません。
特定の子プロセスの終了を待ちたい場合はwaitpid()を使います。

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

引数のpidには1つまたは複数のプロセスを特定する条件を渡します。

  • < -1
    • プロセスグループIDがパラメータの絶対値と一致する子プロセスの終了を待つ。
    • 例えば-500を渡した場合はプロセスグループIDが500である任意の子プロセスを意味する。
  • -1
    • 任意の子プロセスの終了を待つ(つまりwait()と同等)
  • 0
    • waitpid()を発行したプロセスと同じプロセスグループに属する任意の子プロセスの終了を待つ。
  • > 0
    • プロセスIDがパラメータに一致する子プロセスの終了を待つ。例えば500を渡した場合はプロセスIDが500の子プロセスを意味する。

system

プロセスの起動と終了待ちを行います。

#define _XOPEN_SOURCE
#include <stdlib.h>

int system(const char *command);

system()システムコールは、プロセスを同期的に起動します。
system()はパラメータcommandに渡されたコマンド(パラメータ文字列も含む)を起動します。
commandの先頭には/bin/sh -cという文字列が自動的に加えられ、文字列はすべてシェルへ渡されます。


シグナル編

kill()システムコールは、killだけではなく任意のシグナルを送信することができる。

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

pidを持つプロセスに、sigシグナルを送信します。

なお、シグナルを送るには権限が必要です。
通常は、同一ユーザーのプロセスにのみ、シグナルを送信することができます。
root権限を持つプロセスは任意のプロセスにシグナルの送信が可能です。

シグナルハンドラ

シグナルが発生した際に、そのシグナルに対してなにがしかの処理をしたい場合があります。
その際に使用されるのがシグナルハンドラです。

シグナルハンドラに設定できる関数のプロトタイプは以下です。

void my_sig_handler(int signo);

シグナルハンドラを登録するシステムコールは以下です。

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signo, sighandler_t handler);

なお、SIGKILL, SIGSTOPは補足できず、登録しても意味がありません。

シグナルのブロック

シグナルが発生すると、プログラムは現在実行中の場所から、シグナルハンドラの場所へジャンプします。
その際、例えば処理途中でジャンプされてしまうと問題が出る処理があります。
そうした部分を「クリティカルセクション」と呼び、シグナルに割り込まれるのをブロックする仕組みがあります。

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

sigprocmask()の動作はhowによって決定され、以下のいずれかの値を渡します。

  • SIG_SETMASK
    カレントプロセスのシグナルマスクをsetへ変更する。
  • SIG_BLOCK
    set内にセットされたシグナルをカレントプロセスのシグナルマスクへ追加する。
  • SIG_UNBLOCK
    set内にセットされたシグナルをカレントプロセスのシグナルマスクから削除する。

String編

strcmp

#include <string.h>
int strcmp(const char *s1, const char *s2);

文字列s1と文字列s2を比較する。

【戻り値】

戻り値 意味
正の値 s1 > s2
負の値 s1 < s2
0 s1 == s2

※ 大小比較は文字コードによる。


strchr

#include <string.h>
char *strchr(const char *s, int c);

文字列sの先頭から文字cを探し、最初に見つかった位置をポインタで返す。見つからなかった場合はNULLを返す。
(文字列終了コード\0も文字列の一部とみなされる)

※ 一般的に検索する文字はint型にcastする。

example

char test[] = "http://css-eblog.com/";
int find = (int)'o';

char *result = strchr(test, find);
printf("%s\n", result); // => "og.com/"

strstr

#include <string.h>
char *strstr(const char *s1, const char *s2);

文字列s1の先頭から文字列s2を検索し、見つかったらその位置のポインタを返す。見つからなかった場合はNULLを返す。

example

char *s1 = "http://css-eblog.com/";
char *s2 = "css";

char *result = strstr(s1, s2);
printf("%s\n", result); // => "css-eblog.com/"

strcpy

#include <string.h>
char *strcpy(char *s1, const char *s2);

文字型配列s1に、文字列s2\0までコピーする。
\0もコピーするため、それを考慮した配列サイズを確保しておく必要がある。
(s1に、s2の内容をコピーするので引数の順番に注意)

example

char *base = "http://css-eblog.com/";
char result[256];

strcpy(result, base);
printf("%s\n", result); // => "http://css-eblog.com/"

strlen

#include <string.h>
size_t strlen(const char *s);

文字列sの長さを取得する。長さには\0は含まれない。


memcpy

#include <string.h>
void* memcpy(void *buf1, const void *buf2, size_t n);

nバイトメモリブロックのコピー

buf2の先頭からn文字分を、buf1へコピーします。
strcpy()との違いは\0を付加せず、またコピー途中に\0があってもコピーを続けます。


ヘルパー編

bzero

値0のバイトで埋める

※ 現在はmemsetを使った方法が推奨されています。bzeroは廃止予定。

#include <string.h>
void bzero(void *s, size_t n);

memset

引数bufの先頭からnバイト分、chをセットします。

#include <string.h>
void* memset(void *buf, int ch, size_t n);


ch0にすることでbzeroと同じ結果を得ることができます。

memset(&hoge, 0, sizeof(hoge));

atoi

文字列を整数(int)に変換します。
(ASCII to Intの略?)

#include <stdlib.h>
int atoi(const char *str);

余談

ちなみにASCIIは以下の略称のよう。初めて知った・・。

ASCII(アスキー、英: American standard code for information interchange)は、現代英語や西ヨーロッパ言語で使われるラテン文字を中心とした文字コード。これはコンピュータその他の通信機器において最もよく使われているものである。

引用 - Wikipedia

ちなみに、intへの変換からピンと来るかもしれませんが、atolatofなど他の型の数値に変換する関数もあります。


gethostbyname

ホスト名をIPアドレスに変換します。
こちらの記事から引用させてもらうと、

DNS との接続や名前の解決は、全て WinSock が担当してくれます
名前の解決は gethostbyname() 関数を使います

なので、これを使うことで適切にIPアドレスに変換してくれるようです。

struct hostent FAR* gethostbyname(const char FAR *name);

ネットワーク編

socket

TCPまたはUDP通信を行う場合に、OSにソケットの作成を依頼します。

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

domain

プロトコルファミリ。

ドメインタイプ 説明
AF_INET インターネット(INET)ドメインの2ホスト間プロセス通信
AF_UNIX UNIXドメインの1ホスト内プロセス通信。ファイルシステムソケット
AF_ISO IOS標準プロトコル
AF_NS Xerox Network Systemsプロトコル

type

通信の種類を定義。

タイプ 説明
SOCK_STREAM 順次双方向バイトストリーム。コネクション型の信頼性が高い通信
SOCK_DGRAM データグラム。ベストエフォート型の通信
SOCK_ROW 直接IPを用いた通信

protocol

End to Endプロトコルを指定。

プロトコルタイプ 説明
0 自動設定
IPPROTO_TCP TCP/IP(AF_INET & SOCK_STREAMの場合。0も可)
IPPROTO_UDP UDP/IP(AF_INET & SOCK_DGRAMの場合。0も可)
IPPROTO_RAW ICMP

ソケットの破棄

ソケット生成成功時に渡されたソケットディスクリプタの数値をclose関数に渡します。

int sock = socket(...);

// ------

close(sock);

connect

TCP通信において、クライアントからサーバへコネクションの接続確立要求を行います。

#include <sys/socket.h>
int connect(int socket, const struct sockaddr *address, size_t address_len);
  • socket
    socket()で作成したソケットディスクリプタを指定します。
  • address
    sockaddrへのポインタを指定します。
  • address_len
    アドレス構造体のサイズを指定します。

※ 戻り値は、0が成功、-1が失敗。


bind

ソケットに名前をつける。

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

socket()システムコールでソケットが生成されたとき、ソケットは名前空間(アドレスファミリー)に存在するが、まだアドレスは割り当てられていません。
bind()はファイルディスクリプタ(sockfd)で参照させるソケットにaddrで指定されたアドレスを割り当てます。

戻り値

成功した場合は0を、失敗した場合には-1が返されます。


listen

コネクションの確立時にクライアント側からの接続要求を待つため、サーバ側で使う。
保留中の要求を格納するキューをサーバで作り、接続を待つ。

#include <sys/socket.h>

int listen(int socket, int backlog);
  • socket ... ソケットディスクリプタ
  • backlog ... キューの長さ設定。5が一般的らしい。

戻り値

成功した場合は0が、失敗した場合は-1が返されます。


accept

ソケットへの接続を受ける

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

こちらを参考にさせてもらいました。

accept() システムコールは、接続指向のソケット型 (SOCK_STREAM, SOCK_SEQPACKET) で用いられる。 この関数は、接続待ちソケット socket 宛ての保留状態の接続要求が入っているキューから 先頭の接続要求を取り出し、接続済みソケットを新規に生成し、 そのソケットを参照する新しいファイルディスクリプターを返す。 新規に生成されたソケットは、接続待ち (listen) 状態ではない。 もともとのソケット sockfd はこの呼び出しによって影響を受けない。

引き数 sockfd は、 socket(2) によって生成され、 bind(2) によってローカルアドレスにバインドされ、 listen(2) を経て接続を待っているソケットである。

引数

addr引数はsockaddr構造体のポインタを渡し、接続相手のソケットのアドレスが格納されます。

addrlen引数は入出力両用の引数で、呼び出し時はaddrが指す構造体のサイズ(バイト単位)が格納されます。

戻り値

成功した場合は新しいソケットディスクリプタを、失敗時は-1を返します。

18
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
21