C言語
Memo
ラインエディタedのメイン関数
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/bin/ed/main.c#L113
main(int ac, char *av[])
シグナル
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/games/arithmetic/arithmetic.c#L156-L162
* Handle interrupt character. Print score and exit. * static void intr(int dummy __unused) { showstats(1); exit(0); }
shellのノード定義
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/bin/sh/nodetypes#L56-L61
NCMD ncmd # a simple command type int backgnd int # set to run command in background args nodeptr # the arguments redirect nodeptr # list of file redirections lineno int
バイナリツリー
バイナリツリーの中にはleft, rightを持つものがあり、leftは小さい、rightは大きいとしてノードの順番を決定する。
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/external/bsd/libbind/dist/include/isc/tree.h#L44-L49
typedef struct tree_s { tree_t data; struct tree_s *left, *right; short bal; } tree;
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/external/bsd/libbind/dist/isc/tree.c#L105-L130
tree_t tree_srch(tree **ppr_tree, int (*pfi_compare)(tree_t, tree_t), tree_t p_user) { ENTER(“tree_srch”)
if (*ppr_tree) { int i_comp = (*pfi_compare)(p_user, (**ppr_tree).data);
if (i_comp > 0) RET(tree_srch(&(**ppr_tree).right, pfi_compare, p_user))
if (i_comp < 0) RET(tree_srch(&(**ppr_tree).left, pfi_compare, p_user))
* not higher, not lower… this must be the one. * RET((**ppr_tree).data) }
* grounded. NOT found. * RET(NULL) }
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/external/bsd/libbind/dist/isc/tree.c#L155-L169
int tree_trav(tree **ppr_tree, int (*pfi_uar)(tree_t)) { ENTER(“tree_trav”)
if (!*ppr_tree) RET(TRUE)
if (!tree_trav(&(**ppr_tree).left, pfi_uar)) RET(FALSE) if (!(*pfi_uar)((**ppr_tree).data)) RET(FALSE) if (!tree_trav(&(**ppr_tree).right, pfi_uar)) RET(FALSE) RET(TRUE) }
- ツリーは言語構造の表現ができる
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/usr.bin/xlint/lint1/lint1.h#L284-L305
typedef struct tnode { op_t tn_op; * operator * type_t tn_type; / type / bool tn_lvalue:1; / node is lvalue / bool tn_cast:1; / if tn_op == CVT, it’s an explicit cast / bool tn_parenthesized:1; bool tn_sys:1; / in strict bool mode, allow mixture between
- bool and scalar, for code from system
- headers that may be a mixture between
- scalar types and bool
/
bool tn_system_dependent:1; / depends on sizeof or offsetof /
union {
struct {
struct tnode *_tn_left; / (left) operand /
struct tnode *_tn_right; / right operand /
} tn_s;
sym_t *_tn_sym; / symbol if op = NAME */
val_t *_tn_val; /* value if op =
CON /
strg_t *_tn_string; / string if op == STRING */
} tn_u;
} tnode_t;
ハッシュテーブルエントリに複数の要素を格納する
シンボルテーブルの構築によく使われる。
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/games/battlestar/parse.c#L75-L84
static struct wlist * lookup(const char *s) { struct wlist *wp;
for (wp = hashtab[hash(s)]; wp != NULL; wp = wp->next)
if (*s = *wp->string && strcmp(s, wp->string) =
0)
return wp;
return NULL;
}
循環リスト実装
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/bin/csh/lex.c#L176-L189
prlex(FILE *fp, struct wordent *sp0) { struct wordent *sp;
sp = sp0->next;
for (;;) {
(void)fprintf(fp, “%s”, vis_str(sp->word));
sp = sp->next;
if (sp = sp0)
break;
if (sp->word[0] !
’\n’)
(void) fputc(’ ’, fp);
}
}
リスト処理
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/usr.bin/rup/rup.c#L63-L70
static struct host_list { struct host_list *next; int family; union { struct in6_addr _addr6; struct in_addr _addr4; } addr; } *hosts;
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/usr.bin/rup/rup.c#L79-L105
search_host(struct sockaddr *sa) { struct host_list *hp;
if (!hosts) return 0;
for (hp = hosts; hp != NULL; hp = hp->next) { switch (hp->family) { case AF_INET6: if (!memcmp(&hp->addr6, &((struct sockaddr_in6 *)(void *)sa)->sin6_addr, sizeof (struct in6_addr))) return 1; break; case AF_INET: if (!memcmp(&hp->addr4, &((struct sockaddr_in *)(void *)sa)->sin_addr, sizeof (struct in_addr))) return 1; break; default: break; } } return 0; }
nextという要素を持つ構造体は1方向リスト連結リストのノードを定義する。
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/usr.bin/rup/rup.c#L108-L134
remember_host(struct sockaddr *sa) { struct host_list *hp;
if ((hp = malloc(sizeof(struct host_list))) == NULL) { err(1, “malloc”); * NOTREACHED * } hp->family = sa->sa_family; hp->next = hosts; switch (sa->sa_family) { case AF_INET6: (void)memcpy(&hp->addr6, &((struct sockaddr_in6 )(void *)sa)->sin6_addr, sizeof (struct in6_addr)); break; case AF_INET: (void)memcpy(&hp->addr4, &((struct sockaddr_in *)(void *)sa)->sin_addr, sizeof (struct in_addr)); break; default: errx(1, “unknown address family”); / NOTREACHED */ } hosts = hp; }
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/usr.bin/telnet/commands.c#L1700-L1716
struct env_lst * env_undefine(const char *var, char *d) { struct env_lst *ep;
if ((ep = env_find(var)) != NULL) { ep->prev->next = ep->next; if (ep->next) ep->next->prev = ep->prev; if (ep->var) free(ep->var); if (ep->value) free(ep->value); free(ep); } return NULL; }
配列定義
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/lib/libc/time/localtime.c#L869-L871
static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR };
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/lib/libc/time/localtime.c#L864-L867
static const int mon_lengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };
データの内部構造を表現する
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/external/bsd/file/dist/src/tar.h#L53-L71
union record { unsigned char charptr[RECORDSIZE]; struct header { char name[NAMSIZ]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char linkflag; char linkname[NAMSIZ]; char magic[8]; char uname[TUNMLEN]; char gname[TGNMLEN]; char devmajor[8]; char devminor[8]; } header; };
多態の実装
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/include/rpc/rpc_msg.h#L54-L57
enum msg_type { CALL=0, REPLY=1 };
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/include/rpc/rpc_msg.h#L149-L155
struct rpc_msg { uint32_t rm_xid; enum msg_type rm_direction; union { struct call_body RM_cmb; struct reply_body RM_rmb; } ru;
共用体の使用例
共用体はメモリを共用し、節約するために用いる。
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/lib/libbsdmalloc/malloc.c#L75-L89
union overhead { union overhead ov_next; / when free / struct { u_char ovu_magic; / magic number / u_char ovu_index; / bucket # / #ifdef RCHECK u_short ovu_rmagic; / range magic number / u_long ovu_size; / actual block size */ #endif } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index #define ov_rmagic ovu.ovu_rmagic #define ov_size ovu.ovu_size };
空き状態と専有状態を同時にとることはないので、同じメモリ空間を共用できる。
構造体の使用例
外部媒体のデータ構造を表現するために構造体が用いられる。
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/sys/dev/ic/i82557reg.h#L147-L151
struct fxp_cb_nop { volatile uint16_t cb_status; volatile uint16_t cb_command; volatile uint32_t link_addr; };
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/external/bsd/tcpdump/dist/tcp.h#L37-L47
struct tcphdr { uint16_t th_sport; * source port * uint16_t th_dport; * destination port * tcp_seq th_seq; * sequence number * tcp_seq th_ack; * acknowledgement number * uint8_t th_offx2; * data offset, rsvd * uint8_t th_flags; uint16_t th_win; * window * uint16_t th_sum; * checksum * uint16_t th_urp; * urgent pointer * } UNALIGNED;
strlenの実装
https://github.com/kd-collective/NetBSD/blob/89341ae2e1875e7f91cefa9b1dcc0e4549edcde0/common/lib/libc/string/strlen.c#L49-L56
strlen(const char *str) { const char *s;
for (s = str; *s; ++s) continue; return(s - str); }
ポインタを文字列の終端に達するまでインクリメントして、先頭のアドレスを差し引く。
#include <stdio.h> #include <string.h> int test_strlen(char *str) // strは先頭のアドレス { char *s; for (s = str; *s; ++s) continue; // 要素の数だけインクリメント return(s - str); // 進んだ分を求める }
無限ループのイディオム
無限ループの書き方。条件を指定しない。
https://github.com/kd-collective/emacs/blob/d983e080e027bd7b680b1e40ccfa0c71d6a3cd94/lib-src/emacsclient.c#L275-L286
for (;;)
{
char *buf = malloc (buf_size);
if (!buf)
return NULL;
if (getcwd (buf, buf_size) = buf)
return buf;
free (buf);
if (errno !
ERANGE || buf_size = bufsize_max)
return NULL;
buf_size = buf_size <
bufsize_max / 2 ? 2 * buf_size : bufsize_max;
}
配列変数は先頭の要素へのポインタ
配列変数には先頭の要素へのポインタが入っていて、インデックスをその分ずらすことで要素を取得できる。配列が0から始まるのはそのため。
- 最初の要素は、*doses もしくは doses[0] で取得できる。
doses[3] == *(doses + 3) == *(3 + doses) == 3[doses]
void skip(char *msg) { puts(msg + 6); } char *msg_from_amy = "Dont call me"; skip(msg_from_amy);
all me
引数の渡し方
関数呼び出しのとき、デフォルトは値渡しで、コピーされた値が使用される。コピーされるので、呼び出し元の引数の値は変化しない。変化させたいときは、参照を渡す必要がある。
void move(int *lat, int *lon) { *lat = *lat + 1; // 引数で渡されたlatにはメモリアドレスが入っているので、格納している値を読み込むために*を使う。 *lon = *lon + 1; } int main() { int latitude = 32; int longitude = 64; move(&latitude, &longitude); // 参照を渡す。参照でない場合、単なる値のコピーとなって、move()内で全く関係ないローカル変数の値が変わるだけになる。main()内の値は変わらない。 printf("停止...現在位置:[%i, %i]\n", latitude, longitude); return 0; }
停止…現在位置:[33, 65]
渡したメモリ位置を更新する関数といえる。
Tasks
TODO Amazon.co.jp: CプログラミングFAQ: Cプログラミングのよく尋ねられる質問 (新紀元社情報工学シリーズ) : スティーブ サミット, Summit,Steve, 欽一, 北野: 本
Cの詳細な解説本。
TODO C++の設計と進化 | Bjarne Stroustrup 岩谷 宏 | 本 | 通販 | Amazon
元のコンセプトはシンプルとのこと。
TODO 総合目次 - 苦しんで覚えるC言語
WEB版の入門書。
TODO O’Reilly Japan - Head First C
- 41, 59, 67, 103, 105
楽しい入門書。
Reference
weakシンボル - Shohei Yoshida’s Diary
C言語のweakシンボルの使い方。
徹底解剖「G1GC」実装編
GCの実装の解説。
6さいからのプログラミング
C言語のチュートリアル。
Language C FAQ
日本語版。
杉浦とソフトウェア開発
なんだかすごい人。
Bjarne Stroustrup インタビュー (?)
C++の開発者へのインタビュー。
Backlinks
- Emacs
- Ruby
- Learning
- OBS
- Haskell
- Go
- System Crafters
- Scala
- Lua
- KDOC 27: Cコンパイラを書く
- KDOC 46: Goの宣言構文がCと異なる理由
- KDOC 63: 『私はどのようにしてLinuxカーネルを学んだか』
- KDOC 115: 負の値が関わる剰余挙動の違い
- KDOC 129: 『ポインタ理解のためのアセンブリ入門』
- KDOC 141: Cのポインタ操作をアセンブリで見る
- KDOC 143: Cの添字記法は単なるシンタックスシュガーである
- KDOC 192: 『Write Great Code Vol.1』
- KDOC 202: 『30日でできる! OS自作入門』
- KDOC 225: mallocをアセンブラで見る
- KDOC 245: パックしたデータ型がどのように保存されているか見る