C language

概要

C言語は汎用のProgramming Language。OS、プログラミング言語、ハードウェアとの接続といった基盤的な部分で使われる。

たとえば。

現在基盤として使われている多くのプログラムがCで書かれていて、OSSとして公開されている。別の言語で書くにしても、既存の巨大なコード群を参考にできるのは大きな利点。

Memo

ラインエディタedのメイン関数

main(int ac, char *av[])

シグナル

* Handle interrupt character. Print score and exit. * static void intr(int dummy __unused) { showstats(1); exit(0); }

shellのノード定義

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は大きいとしてノードの順番を決定する。

typedef struct tree_s { tree_t data; struct tree_s *left, *right; short bal; } tree;

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) }

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) }

  • ツリーは言語構造の表現ができる

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;

ハッシュテーブルエントリに複数の要素を格納する

シンボルテーブルの構築によく使われる。

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; }

循環リスト実装

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); } }

リスト処理

static struct host_list { struct host_list *next; int family; union { struct in6_addr _addr6; struct in_addr _addr4; } addr; } *hosts;

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方向リスト連結リストのノードを定義する。

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; }

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; }

配列定義

static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR };

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 } };

データの内部構造を表現する

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; };

多態の実装

enum msg_type { CALL=0, REPLY=1 };

struct rpc_msg { uint32_t rm_xid; enum msg_type rm_direction; union { struct call_body RM_cmb; struct reply_body RM_rmb; } ru;

共用体の使用例

共用体はメモリを共用し、節約するために用いる。

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 };

空き状態と専有状態を同時にとることはないので、同じメモリ空間を共用できる。

構造体の使用例

外部媒体のデータ構造を表現するために構造体が用いられる。

struct fxp_cb_nop { volatile uint16_t cb_status; volatile uint16_t cb_command; volatile uint32_t link_addr; };

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の実装

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); // 進んだ分を求める
}

無限ループのイディオム

無限ループの書き方。条件を指定しない。

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 C++の設計と進化 | Bjarne Stroustrup 岩谷 宏 | 本 | 通販 | Amazon

元のコンセプトはシンプルとのこと。

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++の開発者へのインタビュー。

Archives