汎用ポインタ

void *変数名


#include <stdio.h>

typedef struct tagTest {
 char c;
 int i;
} _TEST;

void Disp( void *t )
{
 printf( "%d\n", sizeof( *(_TEST*)t ) );
}

void main()
{
 _TEST test;
 Disp( &test );
}


汎用ポインタは任意のポインタを代入する事が可能であり、
同様に型キャストによって型を復元させる事ができます。


#include <stdio.h>

void Disp( void *t )
{
 char *pstr = (char*)t;
 puts( pstr );
 putchar( (int)*pstr );

 int *pd = (int*)t;
 putchar( *pd );
}

void main()
{
 char *str = "abcdefg";
 Disp( str );
}


あらゆる型のポインタをキャストできます。
C > ポインタ | comments (0) | trackbacks (0)

rand/srand

int rand( void )
void srand( unsigned int seed )

rand関数は0〜32767までの整数乱数値を発生させる。

単純にrand関数を使うと毎回同じ乱数列になる為
毎回異なる乱数列を得たい場合は
時間関数timeと乱数系列初期値設定関数srandを使い
次のように記述する。

srand( ( unsigned )time( NULL ) );


サンプル

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

void main()
{
 int i;
 srand( ( unsigned )time( NULL ) );

 // 0〜32767の整数乱数
 for ( i=0; i<=10; i++ ) printf( "%d\n", rand() );
 // 0〜1の実数乱数
 for ( i=0; i<=10; i++ ) printf( "%f\n", rand() / 32767.0 );
 // 0〜1未満の実数乱数
 for ( i=0; i<=10; i++ ) printf( "%f\n", rand() / 32767.1 );
 // このように書いているのもある
 for ( i=0; i<=10; i++ ) printf( "%f\n", rand() / 32768.0 );
 // 1〜6の整数乱数
 for ( i=0; i<=10; i++ ) printf( "%d\n", ( int )( ( rand() / 32767.1 * 6 ) + 1 ) );
 // これだと商が1.0の場合に7がでてしまう
 for ( i=0; i<=10; i++ ) printf( "%d\n", ( int )( ( rand() / 32767.0 * 6 ) + 1 ) );
}


以下のような式もあったりします。
( rand() % ( 上限 - ( 下限 ) + 1 ) ) + 下限

・0〜9の値
rand() % 10

・1〜6の値
( rand() % 6 ) + 1

・-3〜3の値
( rand() % 7 ) - 3
C > 標準関数 > 一般ユーティリティ | comments (0) | trackbacks (0)

strcpy

char *strcpy( char *dest, const char *source )

文字列destに文字列sourceをコピーする

strcpy( s, "strcpy test" );

文字配列sに"strcpy test"をコピーする
C > 標準関数 > 文字列処理関数 | comments (0) | trackbacks (0)

符号あり右シフト

符号あり整数の右シフト動作は一定していません。
まず正の数の右シフトは左から0がシフトインされます。
負の数の場合は、ANSI Cでは規定していません。
処理系に依存するという事になっています。
その動作には2種類あります。


(1)符号ビットのコピーをシフトインする(算術シフト)
(2)0をシフトインする(論理シフト)


算術シフトは、算術計算に耐えるシフトを意味しています。
例えば-1000を右1ビットシフトしたら-500になるべきだというシフトです。
これに対して論理シフトは、
とにかく指定数だけシフトすればいいというシフトです。
負の数を右にシフトして、結果が正の数になっても構わないというものです。

このように右シフトには2つの方法がありますが、
実際には算術シフトをとっている処理系が大部分です。
それはシフト演算と算術演算とに一貫性がでてくるからです。
このようにシフト演算には処理系依存という要素がありますから
実際の運用にあたっては、
シフト処理は符号無し整数に対して行う
という姿勢が望ましいと言えます。
C > 演算 | comments (0) | trackbacks (0)

シフト演算


#include <stdio.h>
void main()
{
 int b = 1;
 printf( "%d %d\n", b * 2, b << 1 );
 printf( "%d %d\n", b * 4, b << 2 );
 printf( "%d %d\n", b * 8, b << 3 );
 printf( "%d %d\n", b * 16, b << 4 );

 int a = 16;
 printf( "%d %d\n", a / 2, a >> 1 );
 printf( "%d %d\n", a / 4, a >> 2 );
 printf( "%d %d\n", a / 8, a >> 3 );
 printf( "%d %d\n", a / 16, a >> 4 );
}


実行結果

2 2
4 4
8 8
16 16

8 8
4 4
2 2
1 1


シフト対象整数が正の時、
i * 2n と i << n は同じ結果になる。
i / 2n と i >> n は同じ結果になる。

左に1ビットシフトするごとに値は2倍になる
右に1ビットシフトするごとに値は1/2倍になる
C > 演算 | comments (0) | trackbacks (0)

プレフィックス

BOOLb-,f-
HANDLEh-
LPSTRlpsz-
DWORDdw-
WORDw-
UINTu-
BYTEn-,cb-
intn-
shortn-
charcb-,sz-
ポインタlp-
C > 基礎 | comments (0) | trackbacks (0)

算術変換と整数の昇格

算術変換
2項演算子に異なる型のオペランドを指定した場合、「精度の高い」ほうに型を揃えてから演算が行われる。
制度の高い順に
long double>double>float>unsigned long>long>unsigned int>int
となる。
※longとunsigned intの場合はunsigned longになる。

整数の昇格
int型よりもサイズの小さい整数は、演算の前に無条件に符号付int型に変換される。

原則

・ビットフラグなど、ビット単位の情報が重要なものは
符号無し整数にする。
・配列の要素数が多いなど、サイズが重要な場合には
できるだけ小さいデータ型にする。
・さもなければint


何かの個数や人の年齢といった値は負になる事が無い為、符号無し整数にしたくなるかも知れません。
しかし、こうした変数はintにしておく事で、余計な型変換を防ぐ事ができます。
また、charやshortを使用すると、整数の昇格によって余計な変換が発生する分だけ遅くなります。
C > 演算 | comments (0) | trackbacks (0)

and演算での剰余演算


#include <stdio.h>
void main()
{
 int i;
 for ( i=0; i<16; i++ ) printf( "%d ", i % 2 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i & 1 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i % 4 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i & 3 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i % 8 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i & 7 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i % 16 );
 printf( "\n" );
 for ( i=0; i<16; i++ ) printf( "%d ", i & 15 );
 printf( "\n" );
}


実行結果

0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15


i % (n2) は i & (n2-1) と同じである。
C > 演算 | comments (0) | trackbacks (0)

関数の記憶クラス

・関数に指定できるクラスはstaticとexternのいずれかである。
・static記憶クラスを持つ関数は、その関数が定義されているファイル内の関数からのみ呼び出す事ができる。
 他のファイルの関数からstatic関数を呼び出すことはできない。
・extern記憶クラスを持つ関数は、他のファイルの関数から呼び出す事ができる。
 関数の記憶クラスを省略した場合はexternが暗黙的に採用される。
・特定のファイル内の、特定の処理に特化した独立性の低い関数の場合は、名前の競合を避ける為にstatic記憶クラスを指定する事が推奨される。
C > 関数 | comments (0) | trackbacks (0)

typedef

構文
typedef 既存の型名 新しい型名


typedef struct { int x, int u } Point;


・構造体の宣言でタグ名は省略できるため、
 必要でなければ無名構造体に別名を与えた方が効率的である。
・構造体に別名を与えておくと、structキーワードを指定する必要がなくなり、
 構造体変数の宣言が簡単になる。
C > 基礎 | comments (0) | trackbacks (0)

enum

構文
enum タグ名 {
 列挙子1 = 定数, 列挙子2 = 定数 ...
} 列挙変数;



enum { MSG_OK, MSG_YESNO };
enum { ID_OK = 1, ID_YES, ID_NO };


タグ名と末尾の列挙変数の宣言は、構造体や共用体と同じように
省略することができる。


#include <stdio.h>

void main()
{
 enum { Sun,Mon,Tue };

 int x = Sun;
 switch ( x ) {
 case Sun:
  printf( "日曜日の処理" );
  break;
 case Mon:
  printf( "月曜日の処理" );
  break;
 case Tue:
  printf( "火曜日の処理" );
  break;
 }
}

C > 基礎 | comments (0) | trackbacks (0)

論理演算子

論理和
A||B

論理積
A&&B

否定
!A

排他的論理和
(A||B)&&!(A&&B)


・優先順位は!&&||となります。
&&||は左から右に評価されることが保証されています。
 そして条件が一致すれば、そこで条件判定を中止します。
 ですから合致する確率の高い条件を最初に記述した方が処理効率が良くなります。
C > 演算 | comments (0) | trackbacks (0)

ctime

char *ctime( time_t *time )

システムクロック時間を人間が理解可能な時間文字列に変換する。
ctimeとはconvert timeの略かと思われる。
意味合い的には「localtime+asctime」という働きをする。


#include <stdio.h>
#include <time.h>

void main()
{
 time_t nowtime;
 time( &nowtime );
 printf( "%s", ctime( &nowtime ) );
}


実行結果

Tue Oct 24 12:12:37 2006


'\n'と'\0'が文字列の最後に付加されます。
C > 標準関数 > 時間処理 | comments (0) | trackbacks (0)

条件付きコンパイル


#include <stdio.h>

#define DEBUG

void main()
{
 printf( "処理開始\n" );

#ifdef DEBUG
 printf( "%s\n", __TIME__ );
 printf( "%s\n", __DATE__ );
 printf( "%s\n", __FILE__ );
 printf( "%d\n", __LINE__ );
#endif

 printf( "処理終了\n" );
}


出力結果

処理開始
11:56:29
Oct 19 2006
C:\xxx\xxx\xxx\tekito.cpp
13
処理終了




#include <stdio.h>

//#define DEBUG

void main()
{
 printf( "処理開始\n" );

#ifdef DEBUG
 printf( "%s\n", __TIME__ );
 printf( "%s\n", __DATE__ );
 printf( "%s\n", __FILE__ );
 printf( "%d\n", __LINE__ );
#endif

 printf( "処理終了\n" );
}

出力結果

処理開始
処理終了

C > プリプロセッサ | comments (0) | trackbacks (0)

処理系が定義しているマクロ

C言語の処理系は次の4種類のマクロを標準マクロとして定義しています。
__TIME__コンパイル時刻を「時:分:秒」の書式で表す文字列
__DATE__コンパイル日付を「月 日 年」の書式で表す文字列
__FILE__処理中のソースプログラムの名前を表す文字列
__LINE__処理中のソースプログラムの行番号を表す10進数(先頭行は1)
C > プリプロセッサ | comments (0) | trackbacks (0)