naobe @ ウィキ

C

最終更新:

Bot(ページ名リンク)

- view
管理者のみ編集可
言語に戻る

ライブラリ関数

インタフェース 引数 説明
char *strchr(const char *s, int c) s: 対象文字列、c: char sの先頭から最初にcが見つかったポインタを返す。なければNULLを返す。strrchrは逆方向の検索
char *strdup(const char *s); s:コピーする文字列 文字列 sの複製である新しい文字列へのポインタを返す。freeで返却する。
void *memmove(void *dest, const void *src, size_t n) dest:移動先、src:移動元、n:サイズ 移動元からサイズ分のメモリを移動先にコピーする。領域が重なっていても良い。
char *fgets(char *s, int size, FILE *stream) s:読み込んだデータを保管するポインタ、size:読み込みサイズ、stream: 入力ストリーム 1行またはファイルの終わりまで読み込む。sizeは1行の読み込むバイト数。ファイルの終了でNULLを返す。
void perror(const char *s) s:追加する文字列 システムライブラリを実行してエラーが発生した時に、標準エラー出力にシステムライブラリが用意したメッセージを出力する。printf("%s:%s", s, systemmsg)といった形式で出力する。
void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset) start:開始アドレス、length:確保するページサイズ(バイト)、prot:ページの読み書きモード、flags:ページの使い方を指定、fd:ファイルディスクリプタ(open結果)、offset:fdのオフセット fdからoffset離れた位置からlenghtまでメモリにマップする。ファイルに/dev/zeroを指定すると、メモリ確保のみ。mallocは内部でmmapを使用している。
void va_start(va_list ap, last) ap: とにかく必要 , last: 可変引数の直前の引数 可変引数を扱うために関数の最初に実行する
type va_arg(va_list ap, type) ap: va_startで使用したva_list, type: 次の可変引数の型(char *, int, longなど)。戻り値:次の可変引数 次の可変引数を取得する。
void va_end(va_list ap) ap: va_startで使用したva_list 可変引数処理の最後で実行する。

書式制御文字

項目 説明
\n LF。行送り。改行
\r CR。復帰。行頭に戻る
\f FF。フォームフィード。ページ送り。


typedef

関数のtypedef

typedef void (*ptr_to_func)(int);
ptr_to_func signal(int s, ptr_to_func func);
ptr_to_funcは、intを引数に持ち、voidポインタを返す関数。

const char *、char * const、const char * const

参考URL http://stackoverflow.com/questions/890535/what-is-the-difference-between-char-const-and-const-char
constは型指定子(int, charなど)の前に指定すると、型を修飾する。それ以外は直前のポインタを修飾する。
const char *は、const charへのポインタ。char * constは、charへのconstポインタ。const char * constは、const charへのconstポインタ
const char *を指定すると、charを変更できない。
【例】
 const char *p;
 char a[] = "abcd";
 
 p = a;
 p[3] = 'e';
p[3] = 'e'の部分でコンパイルエラーになる。

char * constを指定すると、ポインタを変更できない。
const char * constを指定すると、charもポインタも変更できない。

apacheのソースに登場する、const char * const * *argvの意味は、(const char * const)なるポインタへのポインタのポインタ(何のこっちゃ)。

文字列リテラル(定数)

参考URL http://mkubara.com/index.php/%E6%96%87%E5%AD%97%E5%88%97%E3%83%AA%E3%83%86%E3%83%A9%E3%83%AB
以下の場合は、"abcd"のメモリ割り当ては、スタックでもなく、ヒープでもない。書き換え不可。書き換えた場合の動作は保証されない。
 char *a = "abcd";
以下なら書き換え可能
 char a[] = "abcd";

配列の初期化

 int c[] = {1,2,3,4,5};
 int d[5] = {1,2,3};

下の場合は、d[3],d[4]には、0が設定される。

言語仕様

項目 説明
enum enum {定数1,定数2,...} 変数 という形式で定義。式内では、変数 = 定数2と使うと、変数に1が設定される。変数はint。


型とサイズ

CentOS 5.2 32bit
サイズ(byte) 定数表記(符号あり、符号なし)
char 1
void * 4
int 4 ,U
long 4 L,LU
long int 4 L,LU
float 4
long long int 8 LL,LLU
double 8

マクロ

項目 説明
トークン1##トークン2 トークン1とトークン2を結合する。どちらかのトークンがマクロの引数のときに必要。
# "で囲まれた定数文字列に変換する。
__FILE__ ファイル名
__extention__ コンパイラへの指示。拡張機能(C標準になし)を使っていることを示す。これをつけないと、コンパイラによってはエラーを出力する。

以下の記述では、EPOLLINを書くと、EPOLL_EVENTS.EPOLLINに書き換えられる。
enum EPOLL_EVENTS
 {
   EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN  // 右のEPOLLINは、enumのEPOLLIN

演算子の優先順位


結合規則は、同じ優先順位の演算子が複数あったときの実行順序

優先順位 演算子 用法 名称 結合規則
1 [] a[b] 添字演算子 ---->
() a(b) 関数呼出し演算子
. a.b ドット演算子
-> a->b ポインタ演算子
++ a++ 後置増分演算子
-- a-- 後置減分演算子
2 ++ ++a 前置増分演算子 <----
-- --a 前置減分演算子
& &a 単項&演算子、アドレス演算子
* *a 単項*演算子、間接演算子
+ +a 単項+演算子
- -a 単項-演算子
~ ~a 補数演算子
! !a 論理否定演算子
sizeof sizeof a sizeof演算子
3 () (a)b キャスト演算子 <----
4 * a * b 2項*演算子、乗算演算子 ---->
/ a / b 除算演算子
% a % b 剰余演算子
5 + a + b 2項+演算子、加算演算子 ---->
- a - b 2項-演算子、減算演算子
6 << a << b 左シフト演算子 ---->
>> a >> b 右シフト演算子
7 < a < b <演算子 ---->
<= a <= b <=演算子
a > b >演算子
>= a >= b >=演算子
8 == a == b 等価演算子 ---->
!= a != b 非等価演算子
9 & a & b ビット単位のAND演算子 左から右
10 ^ a ^ b ビット単位の排他OR演算子 左から右
11 | a | b ビット単位のOR演算子 左から右
12 && a && b 論理AND演算子 左から右
13 || a || b 論理OR演算子 左から右
14 ? : a ? b : c 条件演算子 右から左
15 = a = b 単純代入演算子 右から左
+= a += b 加算代入演算子 右から左
-= a -= b 減算代入演算子 右から左
*= a *= b 乗算代入演算子 右から左
/= a /= b 除算代入演算子 右から左
%= a %= b 剰余代入演算子 右から左
<<= a <<= b 左シフト代入演算子 右から左
>>= a >>= b 右シフト代入演算子 右から左
&= a &= b ビット単位のAND代入演算子 右から左
^= a ^= b ビット単位の排他OR代入演算子 右から左
|= a |= b ビット単位のOR代入演算子 右から左
16 , a , b コンマ演算子 左から右


可変引数

ライブラリ関数と以下のサンプルコード参照。
関数fooは、foo("sdc", "aaa", 10, 'c')のように使う。

 #include <stdio.h>
 #include <stdarg.h>
 
 void foo(char *fmt, ...) {
 	va_list ap;
 	int d;
 	char c, *s;
 
 	va_start(ap, fmt);
 	while (*fmt)
 		switch (*fmt++) {
 		case 's': /* string */
 			s = va_arg(ap, char *);
 			printf("string %s\n", s);
 			break;
 		case 'd': /* int */
 			d = va_arg(ap, int);
 			printf("int %d\n", d);
 			break;
 		case 'c': /* char */
 			/* need a cast here since va_arg only
 			 takes fully promoted types */
 			c = (char) va_arg(ap, int);
 			printf("char %c\n", c);
 			break;
 		}
 	va_end(ap);
 }

errorno

http://itref.fc2web.com/unix/system_call.html#eintr 参照
システムコールを実行した時にOSが設定するエラー番号

項目 説明 備考
EINTR(4) システムコールを実行したときに、割り込みが入り処理が終了していないことを示す。http://itref.fc2web.com/unix/system_call.html#eintr 参照


構造体

ビットフィールド

   struct aaa {
       int a:1;
       int b:2;
   } ;
上記では、aは1ビット、bは2ビットの領域を持つ。ビットフィールドで使える宣言は。int, unsigned int,signed int

アラインメント


  • 32bitOSの場合、アラインは最大4バイト(正確にはCPU、コンパイラ依存。Athlon4, CentOS 32bit gccでは4バイト)。
  • 32bitCPUの場合、データバスは32bit。よってメモリの先頭から(??)、4バイトごとにアクセスする。つまり、アドレスは、0,3,7,・・・からしかアクセスできない(RISCプロセッサの場合。違反するとBUSエラーになる)。Intel系のプロセッサは下位互換となっているため、エラーにはならないが2回のアクセスが必要になる。
  • 構造体の要素の最大アラインが、その構造体のアラインになる。要素のアラインは、型のサイズ。
【例】
 struct _aaa {
     char a;
 }

sizeof(struct _aaa)は1(バイト)

 struct _aaa {
     char a;
     char b;
 }

sizeof(struct _aaa)は2(バイト)

 struct _aaa {
     char a;
     int i;
 }

sizeof(struct _aaa)は8(バイト)。1 +4 = 5。5は4で割り切れないので、割り切れる8がサイズになる。
offsetof(struct_aaa, i)は4。つまり、aの後に、3バイトパディングされる。

 struct _aaa {
     char a;
     long long int i;
 }

sizeof(struct _aaa)は12(バイト)。1 + 8 = 9。9は4で割り切れないので、割り切れる12がサイズになる。


mmap

  • ファイルをメモリのように扱える。seekしてread,writeする必要がない。
  • 複数のプロセス間でメモリを共有できる(プロセス間通信)。

配列とポインタ

以下の違いを説明する。上の式は、変数aに"abcd"を格納する(変数aが格納されるアドレスに"abcd"がある)。下の式は、"abcd"が格納されるアドレスと、変数aが格納されるアドレスは異なる。変数aには"abcd"のアドレスが格納される。
char a[] = "abcd";
char *a = "abcd";
宣言文では、配列とポインタは異なったものとして扱われる。以下の例では正常な動作は保証されない。
 【例】
 a.c
   int a[10]:
 
   int main(int argc, char **argv) {
 	・・・・
   }
 
 b.c
   extern int *a:
 
   int main(int argc, char **argv) {
 	・・・・
   }

ただし関数の引数として配列が渡されるときと式の中では配列はポインタとして扱われる。


時刻

以下参照
 #include <time.h>
 /*
   YYYY/MM/DD hh:mm:ss形式の時刻を得る
 
   引数
     full_time  YYYY/MM/DD hh:mm:ss形式の時刻
 */
 void get_full_time( char *full_time ) {
     time_t tnum;
     struct tm *ts;
 
     time(&tnum);
     ts = localtime(&tnum);
 
     sprintf( full_time, "%d/%d/%d %d:%d:%d", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->  tm_hour, ts->tm_min, ts->tm_sec);
 }

ミリ秒まで求めるには以下を使用
 #include <time.h>
 #include <sys/time.h>
 /*
  YYYY/MM/DD hh:mm:ss.sss形式の時刻を得る
 
  引数
  full_time  IN/OUT  YYYY/MM/DD hh:mm:ss.sss形式の時刻
  */
 void util_get_time(char *full_time) {
         struct timeval tim;
         struct tm *ts;
         time_t sec;
         const char *funcname = "util_get_time";
 
         gettimeofday(&tim, NULL);
         sec = tim.tv_sec;
         ts = localtime(&sec);
 
         int msec = tim.tv_usec % 1000000 / 1000;
 
         sprintf(full_time, "%04d/%02d/%02d %02d:%02d:%02d.%03d",
                         ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour,
                         ts->tm_min, ts->tm_sec, msec);
 }

CentOSでは、gettimeofdayを使うためにtime.hとsys/time.hをincludeする。sys/time.hの中で、time.hをincludeしているが何故か両方のincludeが必要。sys/time.hだけincludeすると、localtimeの定義がincludeされない。

writeとfwrite

http://www.oreilly.co.jp/community/blog/2010/08/buffer-cache-and-aio-part1.html 参照
fwriteは、書き込みデータを内部バッファに一時保管し、内部バッファがいっぱいになったら、writeを発行してディスクに書き込む。強制的にディスクに書き込むにはfflushを用いる。
内部バッファのサイズはディスクブロックサイズの倍数になる。

littleエンディアンの確認

以下のプログラムを実行。構造体aaaは、int,long,ポインタの順番で要素を持つ。構造体aaaをファイルbbbにwriteしている。
 #include <stdio.h>
 
 int main(int argc, char **argv) {
     struct aaa {
         int i;
         long l;
         char *c;
     };
     typedef struct aaa aaa;
 
     aaa a;
     a.i = 10;
     a.l = 10000L;
     a.c = "abcdef";
 
     printf("int size:%d\n", sizeof(int));
     printf("long size:%d\n", sizeof(long));
 
     FILE *stream = fopen("bbb", "w");
     fwrite( &a, sizeof(aaa), 1, stream);
     fclose(stream);
 
 }

odでファイルbbbをダンプ
 000000 0a 00 00 00 10 27 00 00 80 86 04 08              >.....'......<
 00000c

10、10000ともに、下から順番に格納されている。ポインタはメモリのアドレスで4バイト。0x08048680が同様に下から順番に格納されている。
char *cを、char c[16]として、"abcdef"をstrcpyした場合は以下。charのサイズは1バイトなので、順番に格納される。
 000000 0a 00 00 00 10 27 00 00 61 62 63 64 65 66 00 bf  >.....'..abcdef..<
 000010 c8 5b ce bf f4 8f de 00                          >.[......<


extern

参照URL http://blog.bitmeister.jp/?p=614
externとexternなしを共存する記述は問題ない。externを記述する目的は、グローバル変数であることを意識するため。
【例】
int i;
extern int i;

関数へのポインタ

書式

 戻り値の型 (*変数名) ();
引数は特に指定しなくてよい。

使用例

 void func(const char* str);
 int main(int argc, char** argv)
 {
   void (*pfunc)();
   pfunc = func;
   (*pfunc)("関数へのポインタを使って呼びました");
 }
 
 void
 func(const char* str)
 {
   printf ("%s\n", str);
   return;
 }

関数へのポインタ配列

書式

 戻り値の型 (*変数名[要素])();

使用例

 #include <stdio.h>
 
 int plus(unsigned int a, unsigned int b);
 int minus(unsigned int a, unsigned int b);
 
 int main(int argc, char** argv)
 {
   int (*pfunc[2])() = {plus, minus};
   printf ("3 + 5 = %d\n", (*pfunc[0])(3, 5));
   printf ("3 - 5 = %d\n", (*pfunc[1])(3, 5));
   return(0);
 }
 
 
 int plus(unsigned int a, unsigned int b)
 {
   return((int)(a + b));
 }
 
 int minus(unsigned int a, unsigned int b)
 {
   return((int)(a - b));
 }

その他

項目 説明
gets(char *s) getsは使わない。代わりにfgetsを使う。改行かEOFが来るまで標準入力から読み込むため、バッファオーバフローが起きる。
select(0, NULL, NULL, NULL, &tv); tvで指定したμsecだけsleep
初期値 指定しなければ外部変数、静的変数の初期値は0
freopen("/dev/null", "r", stdin) 標準入力を捨てる。
構造体への一括代入 同じ構造体動詞は、a = b;という形で一括代入可能。
人気記事ランキング
ウィキ募集バナー