\n
[ トップページ ]
[ ___ _CommandPrompt ]
[ ___ _JScript ]
[ ___ _MySQL ]
[ ___ _Cygwin ]
[ ___ _Java ]
[ ___ _Emacs ]
[ ___ _Make ]
[ ___ _Perl ]
[ ___ _Python ]
[ ___ _OpenGL ]
[ ___ _C# ]
[ ___ _StyleSheet ]
[ ___ _C++ ]
[ ___ _Winsock ]
[ ___ _Thread ]
[ ___ _VisualStudio ]
[ ___ _C ]
[ ___ _Win32API ]
[ ___ _Lua ]
[ ___ _PhotoShop ]
ヘッダ検索
■ マクロ(Macro)
■ C.標準マクロ
DESC
ANSI C の規格では標準マクロとして以下のものを用意している
__FILE__
__LINE__
__DATE__
__TIME__
__STDC__
定数 1. ANSI C 標準準拠 であることの証明
VisualStudio 固有のもの
POINT
TimeStamp として GUIツールのラベルなどに設定しておくとビルドの時刻がわかり管理しやすい。
sprintf( buf, "%s %s" , __DATE__, __TIME__ );
SetWindowText( hWnd, buf );
■ 制御演算子
■ for
POINT
元々 while() 原型だったことを考えれば当然
for()の処理手順
初期化式( i=0; ),
継続条件式( i< N; ),
文
再初期化式( i++ )の順で行われる
■ for(int i = 0; i < 3; i++)
ここのint i はこのループ内のみで有効かつ存在
[init]
|
[test] // 1 回目も test される
|
[statement]
|
[increment]
for文は縦に並んでいると考えるべき
逆に同じようなことが縦に並ぶ場合は、forで制御できる
■ priority
priority
1. () [] -> .
2. !, *, &
3. == !=
4. &&
5. ||
以下の記述は問題ない。
if ( ptr == NULL || fs == NULL )
if ( !ptr || !fs )
&p->nr < -> &( p->nr )
&p->pro[ p->nr ]
■ ビット演算(BitOperation)
DESC
bit を立てる
i | 0x00000001
bit を調べる
if ( i & 0x00000001 )
mask する( 下位4bit )
if ( i & 0x0000000f )
TIP
flag に複数の値( 状態 )を設定したい際に効果的
stat = STAT_SEL | STAT_CPT;
bit 演算子も 演算中は 4 byte 整数であつかうようだ( 32bit CPU ならば当然 )
unsigned short si = 1;
unsigned int ret = si < < 16; // ret = 65xxx;
WARNING
bit 反転( ~ ), 論理否定( ! )を混乱しないこと
■ char のデータを左シフト
//extern float angle[3] = {0.0, 0.0, 0.0};
EXtern float angle[3];//これはOK
■ typedef
DESC
既存のデータ型( 基本型 )に別名をつける。
POINT
typedef YYY XXX;
[ YYY を XXX と呼ぶ ]と Compiler に知らせる
使いどころは、意味があってわかりやすく短い名前をつける。
typedef map< string, number > MemberTable;
MemberTable tbl;
または、環境ごとの型定義の違いを吸収するために使う。
マシンに依存する型を定義することで移植性が高まる。
// 64bit サイズの整数型を i64 という名前で定義する
#ifdef __MSC_VER
// VisualStudio 環境
typedef __int64 i64
#else
typedef int64_t i64
#endif
i64 i;
namespace に収まる
public , private の影響うける
さらに Code 内の記述も可能. Block の影響をうける。
// Scope 内で有効
typedef Foo Delegator;
Delegator d;
// int 型の vector を List と呼ぶ
typedef std::vector< int> List;
List v;
// T 型の Pointer を TPtr と呼ぶ
typedef T * TPtr;
typedef char *string // char * を string 型として定義
typedef struct treenode *tree_ptr
treeptr tnode;
class bcls{
typedef unsigned int UINT; // bcls name空間で有効
}
// ふたつ 定義することも可能.
typedef typedef char TCHAR, *PTCHAR;
// int 型のポインタを intptr と別名をつける
typedef int *intptr;
// 別名なので型チェックは元の型と同じ
void func ( int *);
intptr ip;
fucn( ip );
少し厳密に.
記憶クラス指定子が [ typedef ] である宣言は Object の宣言ではなく,
型に対しての名前( 識別子 )をつける
識別子は [ typedef 名]と言う.
POINT
新しい型の定義ではなく
[ 既存型の同義語 ]を生成するにすぎない
"基本型" は言語ごとに決まっている
そのため 同一名で 2 回定義すると怒られる
( コンパイラはどれを参照すべきかわからない )
error C2371: 'T' : 再定義されています. 異なる基本型です
typedef struct {
float f;
} T;
typedef struct {
int i;
} T;
typedef した オリジナル型とは互換可能
別名にすぎないので元の型が int ならば, 比較演算子を利用できる。
#include< windows2.h>
SOCKET s = 2;
int i = s + 10;
// 12
printf( "%d\n" , i );
Tag( 名札 ) のない Enum, Struct, Union はすべて Uniq な型を指定
typedef Tset::Bar DEPRECATED Bar;
■ 無名共有体.union
DESC
cpp 固有
名前をもたない共有体
[ object をもたず ] に変数にアクセス可能
内部の変数は 共有体として働く( メモリを共有 )
global 変数 無名共有体は static を宣言する static union { int foo, int bar };
union は cmp に {} の 変数は メモリを共有してねと通知する
union を宣言することで, 同一の mem 領域に mapping される.
union 変数の Address 位置から順番に set されるようす.
union {
{
float x;
float y;
float z;
}
vector3 v;
};
POINT
union は最もサイズの大きい obj に合わせられる
typedef union uTEST
{
long num;
int d;
char s[4];
char c;
}test;
test u;
memset( &u, 0, sizeof(test) );
u.s[0] = 'a';
u.c = 64; // 残りの bit は操作されないことになる.
memDump( (int *)&u, sizeof(test) );
■ 前方宣言(ForwardDeclare)
DESC
Compiler に class の symbol の存在を知らせる
Class 型だけを記述する header は前方宣言のみでよい
POINT
Compiler の視点でみると case で ForwardDeclare すればいいかわかる
Source を見て, Compile できれば問題なし
// OK ( ptr (adr) を 代入するので, Foo の構造は知らなくて OK )
void func( Foo &f );
void func( Foo *f );
// ERROR ( CopyCtor が走るので, Foo の構造を知る必要がある. )
void func( Foo f );
まとめ
Object の構造( mem )を知りたいとき以外は, ForwardDeclare を使おう.
TIP
header 内では 積極的に利用することで, 相互依存を避ける
WARNING
cls mem は 定義後でないと使えない
-> あたりまえ ( struct 型の定義は 相対 Address を指定するもの )
2. cls 継承も 先に bcls が定義されている必要あり
-> これ理由わからん
WARNING
定義が必要な場合は ERROR
// ERROR
// incomplete type is not allowed
std::vector< struct foo > v;
// OK
std::vector< struct foo *> v;
// ERROR
// object size がわからないとメモリを確保できない
struct foo f;
// A という class があるよ
class A;
class B
{
A objA;
const A &getA();
}
// A 定義
class A
{
}
■ Pointer(ポインタ)
DESC
アドレスを格納する変数 [ 可変 ]
POINT
配列は わりあてられた領域を指す定数 [ 固定 ]
// 以下は処理系依存らしい.
char **tt = {"boo" , "foo" , "woo" };
■ void.ポインタ
void * とは どこかのアドレスを指すポインタ を意味する
func(void *) とあれば、何かしらのポインタがくれば
それで十分ということになる
■ ポインタの算術演算
ポインタは 加算, 減算ができる。
int a[] = {1,2,3};
int *i = a;
// 1 要素すすめる
i += 1;
// 1 要素もどる
i -= 1;
■ [ any ptr -> int ]への妥当性
( 表現できる十分大きな 整数型にいれること )
■ PreProcessor
■ undef
#define している変数を解除するマクロ展開されなくなる
二重定義の警告もでなくなる
■ ## : token 連結演算子
DESC
token を連結する演算子 [ ## ]
macro のみで有効
A ## B -> AB
#define FOO( x ) FOO_## x
■ Define
DESC
C PreProcessor により 文字列を置換する
置換するだけなので, 書き方は色々
sensitive ( MAX != max )
namespace 関係なし( -> 置換なので当たり前 )
つかいみち
// data の "意味つけ" 名前をつける
#define PI 3.14f
// 一般化した別名( 置換えを想定 )
// 変数としての役割
#define NEW new
#if _DEBUG
# define NEW new( __FILE__, __LINE__) // Debug 情報を仕込む
#else
# define NEW new
#endif
# 引数つきマクロ
# 条件, fall_back 処理
#define myassert( cnd, fall_back ) if ( !(cnd) ) { fall_back; }
myassert( i == 0, return );
myassert( strcmp("foo" == s ) == 0 , return 0 );
// 環境ごとの違いを吸収する。
#define FASTCALL __fastcall
#define ALIGN(n) __declspec(align(n))
#define の連結記号は \ が行単位ゆえに空白の行でも \ が必要
#define FUNC(cnd, fall_back) \
if ( cnd ) { \
fall_back; \
}
// 再定義 は WARNING
#define PI 3.14
#define PI 3.1
// typedef の重複はエラー
typedef unsigned char foo;
typedef unsigned short foo;
POINT
define の NEST OK
PreProcessor の立場はこれ
置換対象があれば, 置換し続ける
define の中で #define symbol の使用は OK
#define TEST( d ) (int)( (d + d + d) / 3.0f )
# define pi( exp ) printf( #exp" = %d FILE: %s\n" , exp, __FILE__ )
// TEST(100) = 100 FILE: main.cpp
// printf( "TEST(100)" " = %d FILE: %s\n" , (int)( (100 + 100 + 100) / 3.0f ), "main.cpp" );
pi( TEST(100) );
#define NEW(heap) NEW_IMPL(heap, 0, 0, 0)
typedef は namespace 有効
{} block 内でも OK
POINT
// if()文の中でマクロを使用した際にエラーを起こさない為
#define MY_MESS(mess) do{ fprintf(stderr, mess);}while(0)
■ 定数(Literal)
DESC
定数 ( < -> 変数 )
左辺になれない値( Object )
意味としては [ 文字で表現された ]などの意味がある
Comuter では 定数 という意味になるらしい
"foo" : 文字列リテラル
2222 : 数値リテラル
TIP
// 文字列 Literal は Compiler 時に連結できるので
// 長い文字列を複数行にかくときに便利
// SPC, 改行を SKIP してくれる
{
const char buf[] =
"TM Revolution "
"Evolution "
"The End Of Genesis "
"Turbo TypeD" ;
printf( buf );
}
// これも OK
strcmp(str, "foo" "bar" )
定数を LL ( long long ) にする
xxxf と同じ
// Long 型の領域を確保して 10 をセット
10L
■ 変数
POINT
プログラム全体で定義はちょうど一つでなければならない
しかしそれを参照する宣言はいくつあってもOK
同じ変数の定義が複数あるのもおかしいし
一つもないのもエラー
定義 : mem 領域を割り当てること
宣言 : 型の指定 -> cmp にこの名前のラベルに対して, サイズを教える
POINT
変数とは 名前の付けられた メモリ領域のこと。
外部定義 (external definition)
int x=5;のように 初期設定付きで記述されたもの.
(紛らわしいですがこの 「外部(external)」はexternとは異なり
「すべての関数の外部で定義されたもの」という意味です)
メモリが割り当てられて初期値がセットされます.
Extern 宣言 (extern declaration)
extern int x;
のように extern付きで記述されたもの
メモリは割り当てられません.
関数の外部にも内部にも使える
extern int gFoo;
int func()
{
// File 名 Scope になる
// 定義ではなく参照
extern int gBar;
}
仮定義 (tentative definition)
int x;
のように初期設定も externもなしで記述されたもの.
これはプログラム全体のどこかに外部定義があればextern宣言として振る舞い,
なければ外部定義になる
また,同じ大域変数の仮定義が 複数あっても良いが
それらは同じメモリ領域を参照する
■ global 変数
static領域 に確保される
TIP
stack の大きさは有限
大きな配列を使用すると、stack が不足してプログラムが誤動作する
その場合はstatic 領域におく
■ 構造体(Struct)
■ struct foo
{
int a;
// 宣言にすぎない -> ERR: fooObj.bar.b
struct test{
int b;
int c;
};
// 定義
struct test{
int b;
int c;
}obj;
// 無名でも良い
struct {
int b;
int c;
}obj;
POINT
後に変数の並びがない構造体宣言は, メモリ割り当てはされない。
struct ivec2{
int x;
int y;
};
struct {
int x;
int y;
}x; // x 構造体変数 mem 割り当て
■ 構造体の代入
構造体は代入ができる。
そのため、引数の値渡し、関数の戻り値として利用できる。
// vec3 構造体の定義
struct vec3 {
float x, y, z;
};
struct vec3 v1, v2;
v2 = v1;
struct vec3 v3 = add( v1, v2 );
■ 引数
■ main関数の引数
argc に引数の数、argv に引数文字列のポインタの配列がセットされる。
int main( int argc, char *argv[] ) {
for( int i=0; i< argc; i++ ){
print ( "index %d %s\n" , i, argv[i] );
}
}
WARNING
引数にはプログラム名も含まれるので、意味のある引数は index 1 からになる。
// 0 main.exe
// 1 1
// 2 2
// 3 3
// 4 d:/tmp/test.txt
main.exe 1 2 3 "d:/tmp/test.txt"
■ 可変引数
void log(const char *sect, const char *format, ...);
void print( const char *fmt, ... );
■ C++からの仕様の取り込み
■ const
型に対する修飾子のひとつ。
REFERENCE const
■ キャスト(Cast)
DESC
整数のかくあげ
整数を扱う式で, int として表現できる型は int に変換
char, short, bf, enum -> [ int | uint ]
■ 算術変換
unsigned int n = 10;
int i = 0;
for( i=0; i< n; i++ ){
}
■ 代入時の自動的型変換
POINT
キャストとは malloc などでデータ領域を確保し、
その領域をある型の配列などとして
使用するために使うのが主たる使用法
■ 暗黙の型変換
[ == ] は演算の一種暗黙の型変換が起こる
「int型→float型→int型の場合」はbug の可能性あり
有効桁数が少ないfloat型に変数aの「123,456,789」大きなint型の値を代入すると、
有効桁数不足から誤差が発生結果変数b「1.23456792×10の8乗」という値
WARNING
int型とfloat型間の代入では誤差の発生が付き物
コンパイルエラーにはならない
INVALID : 不定
■ 関数型( 関数ptr )の考察
DESC
関数 ptr はキャストすれば
■ ptr < -> integer
POINT
int -> any ptr への変換は OK
DESC
integer 型式は ptr と +- 可能
2. ptr -> int( ptr 表現可能な大きさ )
-> ∵ ( ptr も 整数値 )
3. int -> ptr
4. aPtr < -> bPtr
4. 同一型 obj への ptr の相互変換
{
fooObj *a, *b;
a = b;
}
5. func ptr 同士の変換
int *p;
p + 5;
// ptr を int 型に格納( cast いるかも )
{
int a = 7;
int p = (int)&a;
printf("%d\n" , *((int *)p));
}
// 異なる obj 間の ptr 変換 ( mem 配置の整合性があえば OK )
{
typedef struct
{
int a;
int b;
}base;
typedef struct
{
int a;
int b;
int c;
}derive;
{
base b = { 4, 5 };
derive *d = (derive *)&b;
printf( d->a );
printf( d->b );
}
// funcPtr 間の変換
{
int (*fPtr)();
void (*gPtr)(int, int);
// できない ?
fPtr = gPtr;
}
■ int < -> float
DESC
float -> int : 少数部分むし. INVALID( int で表現不能 | -float -> uint )
int -> float : 表現可能な近似値
■ float < -> double
DESC
float -> double : NOCNG
double -> float : 表現可能な近似値. INVALID( float の範囲外 からの変換 )
■ Array
// 初期化方法.
int a = 7;
int array[] = {a, a, a};
// コンパイル時にサイズが指定できればよい
const int nr = 8;
int temp[ nr ];
配列 == 1次元Table
DESC
Table とはあるObject をID(数字)で表現したもの
char *name_t[] = {"foo" , "bar" , "hoge" };
■ 2次元Array
DESC
data[2][4] は [4] の塊が連続しているのと同義
matrix44[4][4] == data16[16] は行優先で読むのと同じ
■ 演算子(operator)
error:
ptr[k];
OK :
(*ptr)[k]
■ 3項演算子
a = a > b? a : b; 返り値をうけとるべき
■ 真偽.否定演算子
DESC
0 : 偽
1 : どれか bit がたっている
TIP
sizeof( bool ) = 1, sizeof( int ) = 4, // ( VC, gcc )
WARNING
!= 1 でない点に注意
■ 逆参照演算子
DESC
[*]
typedef void (*func)();
void foo()
{
...
}
func f = foo;
(*f)(); // 逆参照
■ 桁あふれ(overflow)
DESC
桁あふれとは扱える数値範囲を超えること。
unsigned char 型は 1byte で 0 - 255 の範囲を扱うので
+1 をすると 0 になる。
char < -> int
箱にはいらない部分は切り捨てられると考えるとわかりやすい。
Compiler WARNING にもならない
int a=256
a[0x00 00 01 00]
char b=a -> 0
b[0x-- -- -- 00]
EX2.
char a=-1;
a[0x-- -- -- ff]
int b = a;
a[0xff ff ff ff]
Java
// [0x0001 0001]
short s = 257;
// RET :b 1
byte b = s;
TIP
単純に mem cp がされるわけではない
■ 型(Type)
DESC
型とは Address がさすビット列データの意味のこと。
Object がもつことができる[ 値の集合 ]と, それに適用できる演算を決めるもの
Computer で扱う Object は変数と定数のみ
定数は. int 型である
// 整数とみれば, 64
010101010
// char という意味でみれば 'A'
010101010
■ 整数
int が 4byte の場合では, -2億 - 2億 の範囲の値の整数を扱うことができる。
C にある 型は 4 種類
char : 1 byte
int : ハードウェアが扱う整数
float
double
さらに修飾子がついて long short ( 整数型のみ有効 )
int は HW( 計算機によって異なる )
Compiler は HW の仕様にあわせて, int のサイズを決めることができる。
C の仕様では以下の規則しかない。
int のサイズが 4 byte というのも厳密には保障されない。
short < = int < = long
WARNING
int は掛け算が苦手なので、int の範囲を超えるような掛け算をする場合は float 型をつかう
// BAD
int i = 50000;
printf( "%d\n" , i*i );
// OK
float f = 50000;
printf( "%f\n" , f*f );
■ size_t
POINT
符号なし整数 を typedef したもの
そのため厳密には環境ごとで型のサイズがことなる。
32bit 環境ではたいてい unsigned int ( 4byte )
■ float
基数表記
0.5を浮動小数点数で表すと、基数が10の場合は5.0×10-1(5.0e-1)、基数が2の場合は1.0×2-1となる。
WARNING
float に誤差が発生するため(float という表現) a = 0.1; if( a == 0.1 )
WARNING
扱える値の範囲を大きくしたかわりに 精度を犠牲にしているので
大きな値と小さな値を同時に利用しないこと。
float も int も 4byte なら扱える値は同じ数しかということ。
float f = 50000;
float f2 = f*f;
// 2500000000.000000
printf( "%f\n" , f2 );
// +1 は精度が足りないため, 丸められる。
// 2500000000.000000
f2 = f2 + 1;
printf( "%f\n" , f2 );
カウンタとして利用すると 1600 万あたりで増えなくなる。
float f = 0;
int i = 0;
for( i=0; i< 1024*1024 * 24; i++ ){
f += 1;
}
// 16777216.000000 25165824
printf( "%f %d\n" , f, i );
■ boolean とは
定義:
false 0: true 未定義
正しい例
if( status_check() )
理由
zero 比較の結果は簡単に作成できるが、それ以外の値の比較は
operation がひとつ増える
■ uintptr_t
DESC
ptr を 整数に変換する
問題は ptr size が int と同じサイズとは限らないこと
64 bit binaryでは, sizeof(int) = 4, sizeof(void *) = 8
int 型も Compiler により, 64bit 環境では size が異なるらしい
GCC : sizeof ( int ) = 8
VS : sizeof ( int ) = 4
DEP
C99 stdint.h::intptr_t, uintptr_t
TIP
32bit ptr から u64 に変換する際は
上位に 1 bit がはいらないように uintptr_t に変換する
WARNING
ptr を Int 型に cast していると, 64 bit では 上位 bit がなくなる
// ERROR
// 0x80 00 00 00 が負の整数として扱われる
// 64 bit に 拡.張 されるとき 上位 bit に 1 がはいる
void *p = (void *)0x80000000;
uint64_t x = reinterpret_cast< uint64_t >(p);
// OK
// 上位に 1 bit が入るのを避ける
uint64_t x = reinterpret_cast< uintptr_t >( p );
VisualStudio では次のように定義
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef _W64 unsigned int uintptr_t;
#endif
■ __int64
DESC
C、C++における非標準の変数型で、64ビット長の整数を宣言する。
Microsoft C/C++やBorland C++Builder、その他の環境でこの変数型に対応している。
gcc は対応していない。
POINT
非標準の型であるので, Visual C++ 以外の環境では利用できない。
32bit 環境でも利用できる。64bit 環境ではレジスター1本ですむが、32bit環境では2本使う。
内部では32ビット×2で処理される。
__int64 num;
num = 0x1122334455667788i64;
_num$ = -8
mov DWORD PTR _num$[ebp], 1432778632
mov DWORD PTR _num$[ebp+4], 287454020
■ __int32
DESC
__int64 と同じく Visual C++ 固有の 32bit の整数型
■ wchar.wchar_t
ANSI C は標準でワイド文字をサポートしている
■ MultiByteCharacter.とは
英数字や一部の記号を 8bit ひらかな.漢字を 16 bit で表現
MS-DOS の時代から使われている Shift-JIS を使ったモ−ド
■ WideCharacter.とは
1文字表現するのに2バイト用いる文字のこと
なぜ Wide 文字ができたか ?
1 Byte だけでは中国語や日本語のような
漢字や記号を多く持つ言語を表せないから.
WideCharacter は国際的なアプリケーションの開発には必要な知識
UNICODE も WideChara のひとつ( 一例にすぎない )
ワイド文字の型は wchar_t であらわす // WCHAR.H
typedef unsigned short wchar_t; // 実体は unsigned short
ワイド文字型で文字列を表現してみましょう
ワイド文字として文字列を格納するには、文字列の前にL(Long)プレフィックスを指定する
wchar_t wc[] = L"test" ;
ワイド文字列は、必ず 1文字 2バイトの領域を確保する
VC で L"aiueo" とすると utf-16 で Encode される
■ symbol(識別子)
DESC
mem obj( var, func, enum, typedef, ...) の名前
var は mem obj のひとつ
記憶クラス : 記憶場所の 寿命
2. type( 型 ) : mem obj 値の意味 -> [01011011] ->(float | int ....)
3. scope :
4. linkage : link 時の参照 範囲指定
変数型の Symbol は Address に名前をつけたもの
■ リファレンス(Reference)
■ IO
■ fwrite
SYNTAX
size_t fwrite(
const void *buf // 書き込みデータのポインタ
size_t sz // バイト長さ
size_t n // データ数
FILE *fp // FILEポインタ
);
RET
OK : 書き込んだデータ個数( バイトではない )
ERROR : nより小さな値
DESC
ファイルへ size バイトのデータを n 個書き込み
static unsigned char data[ 1024 * 512 * 4 ];
glReadPixels(
0, 0,
1024, 512,
GL_RGBA,
GL_UNSIGNED_BYTE,
data
);
FILE *fp = fopen( "d:/capture.raw" , "wb" );
fwrite( data, sizeof(data), 1, fp );
fclose( fp );
■ fread
SYNTAX
size_t fread(
void *ptr, // 格納先の Buffer
size_t size, // 1 つの data size
size_t nr, // data の個数
FILE *stream // 読みこみ先の stream
);
DESC
stream ポインタで指定されたストリームから nr 個のデータを読み込み
ptr で与えられた場所に格納する
個々のデータは size バイトの長さを持つ
WARNING
文字とおり, 指定した Size しかよまないので, printf(); をするときは '\0' を忘れずに
REF
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/fread.3.html
{
char buf[256];
memset( buf, 0, sizeof(buf) );
FILE *fp = fopen( "d:/test.txt" , "r" );
// 日本語だと どうなるか TEST
// ERROR
// sizeof(char) == 1 なので [い] しか読まれない
// そもそも fgets() のがいいかも
fread( buf, sizeof(char), 2, fp );
printf( buf );
fclose( fp );
}
■ fprintf
SYNTAX
fprintf( FILE *, const char *format, ... )
fprintf( fp, "%32s%4d" , n->str, isLeaf );
■ string
■ strlen
SYNTAX
int strlen( const char * );
DESC
文字数をかえす ( Buffer Size ではない )
"\0" はカウントしない
{
char buf[256];
sprintf( buf, "%s" , "abc" );
// RET : 3
strlen( buf );
}
WARNING
strlen の \0 忘れ
ERROR
char *p = malloc( strlen(str) );
// error;
strcpy(p, str);
■ strdup
SYNTAX
char *strdup(char *DST, const char *SRC);
■ strcpy
SYNTAX
char *strcpy(char *DST, const char *SRC);
DESC
src ( \0 含む )を dst へコピー
RET
dst の先頭ポインタ
const char *src = "test" ;
int nr = strlen(src);
char *dst = (char *)malloc( nr+1 );
strcpy( dst, src );
■ strlcpy
SYNTAX
size_t strlcpy(
char *dst,
const char *src,
size_t size
);
DESC
セキュアな strcpy
src の指すアドレスから最大で size - 1 バイトの文字列を dst にコピーする。
また dst の指す文字列が必ずヌル文字で終わるようにする。
dst が char 配列の場合に sizeof(dst) を size として指定すればバッファオーバーランしないことが保証される。
char path[ NR ];
strlcpy( path, src, NR );
■ strchr
SYNTAX
char *strchr(const char *str, int c );
DESC
文字列 str から 文字 c を検索してそのアドレスを返す。
c は最初に見つかったアドレスを返す。
後ろから検索するには strrchr を使う。
RET
NULL : search が c に含まれない
!NULL : str の中の c のアドレス
if ( strchr( "test.bmp" , '.' ) != NULL ) {
}
■ strrchr
SYNTAX
char *strrchr(const char *str, int c );
if ( strrchr( "d:/foo/test.bmp" , '/' ) != NULL ) {
}
■ strstr
SYNTAX
char *strstr( const char *str, const char *search );
DESC
文字列 str から search を検索してマッチした場合はその先頭アドレスを返す
search は \0 を含めずに検索する
RET
NULL : search が str に含まれない
str : search が長さ 0 の場合は str がかえる
char *s = strstr( "test.bmp" , ".bmp" );
// .bmp
ptintf( "%s" , s );
■ strncpy
SYNTAX
char *strncpy(
char *dst,
const char *src,
size_t n
);
DESC
文字型配列 dst に文字列 src を先頭から n 文字コピー
dst と src が重なっている場合には動作は未定義
strlen( src ) >= n なら
n 文字をコピーするが '\0' の自動付加はしない
src : [a][b][c][d][e]
dst : [ ][ ][ ][ ]
strncpy( dst, src, sizeof(dst)-1 );
dst : [a][b][c][ ]
// strlen( src ) < n なら、残りの文字を '\0' で埋める
src : [a][b]
dst : [ ][ ][ ][ ]
strncpy( dst, src, sizeof(dst)-1 );
dst : [a][b][\0][ ]
// まとめ
// 0 で padding して
// sizeof(dst) - 1 文字のみ Copy すること
char dst[4];
memset( dst, 0, sizeof(dst) );
strncpy( dst, "abcedf" , sizeof(dst)-1 );
// BAD
// BufferOverRun
//::strncpy( dst, "abcedf" , sizeof(dst) );
printf( "%s" , dst );
■ strtod
SYNTAX
double strtod(const char *STR, char **TAIL);
float strtof(const char *STR, char **TAIL);
RET
0 : Fail
!0 : 数値
正しい値が表現可能な値の範囲外であるとき
値の符号に従って
正または負のHUGE_VALを返し
errnoにはマクロERANGEの値を格納する。
正しい値がアンダーフローを起こすときには0を返し
errnoにはマクロERANGEの値を格納する
DESC
文字列を double | float 型に変換して返す。
{
char *e;
char s[32] = "124.5" ;
double x = strtod(s, &e);
if (errno != ERANGE) {
printf("変換数値=%f\n" , x);
// 間違っていたら何かをさす
if (*e != '\0') {
printf("変換不可能部分=%s\n" , e);
}
}
else if (x == HUGE_VAL) {
printf("double値で表現できる値を超えました\n" );
}
return 0;
}
sprintf_s と sprintf の違い
sprintf_s は書式指定文字列の有効な書式指定文字をチェックしますが
sprintf は書式指定文字列またはバッファが NULL ポインタかどうかのみをチェックをする。
いずれかのチェックが失敗した場合
「パラメータの検証」に説明されているように
無効なパラメータ ハンドラが呼び出されます
実行の継続が許可された場合
関数は -1 を返し
errno を EINVAL に設定します
sprintf_s は
出力バッファのサイズを文字数で指定する長さパラメータを受け取ります
バッファが小さすぎて出力テキストを格納できないなら
バッファは
空の文字列に設定され
無効なパラメータ ハンドラが呼び出されます
snprintf とは異なり
sprintf_s では
必ずバッファは null で終わります (バッファ サイズがゼロでない限り)
}
EX
{
// Buffer Size = 8
char buf[] = "1234567" ;
int ret = _snprintf_s( buf, sizeof(buf), sizeof(buf)-1, "foo" );
int ret = _snprintf_s( buf, sizeof(buf), _TRUNCATE, "foo" );
myas( ret != -1, "切り捨てられたけど, ダイジョーブ ?" );
if ( ret == -1 ) {
printf( "切り捨てられた\n" );
}
else {
printf( "%d + 1 文字かきこみました\n" , ret );
}
}
■ jump
通常の関数 の return をさけるために使う
// longjmp の戻り先を指定する
int setjmp( jum_buf env )
int longjmp( jum_buf env, int val )
■ time
日付, 時刻 をあつかう
■ clock
SYNTAX
clock_t clock();
DESC
プログラム開始時からの経過時間( msec )をかえす。
秒単位にするには macro `CLOCKS_PER_SEC'. でわる
clock_t, time_t は時間をあらわす算術型
RET
単位
machine-dependent macro `CLOCKS_PER_SEC'.
未定義なら
(clock_t)`-1'
clock_t s = clock();
system( "timeout 3" );
clock_t dt = clock() - s;
printf("%d sec\n" , dt / CLOCKS_PER_SEC );
printf("%d msec\n" , dt );
■ difftime
SYNTAX
double difftime( time_t t2, time_t t1 )
DESC
経過時間( 秒 )をかえす
時間を表す構造体 struct tm
struct tm {
int tm_sec; // 秒
int tm_min; // 分
int tm_hour; // 時
int tm_mday; // 日
int tm_mon; // 月( 1月=0 )
int tm_year; // 西暦年 - 1900
int tm_wday; // 曜日( 日=0 )
int tm_yday; // 日(年を通して)
int tm_isdst; // サマータイムフラグ
};
■ time
SYNTAX
time_t time( time_t * )
DESC
現在の時間をかえす
■ asctime
SYNTAX
char *asctime( const struct tm *tp )
DESC
文字列表現としてかえす
time_t t = time( NULL );
// Sat Jun 23 15:39:46 2012
printf( "%s\n" , asctime( localtime(&t) ) );
■ localtime
SYNTAX
struct tm *localtime(time_t *CLOCK);
DESC
convert time to local representation
mktime' is the inverse of `localtime'.
■ mktime
SYNTAX
time_t mktime(struct tm *);
DESC
convert time to arithmetic representation
■ memory
■ memcpy
SYNTAX
void* memcpy(
void *dst,
const void *src,
size_t N);
DESC
N バイトを src から dst へコピーする。
src と dst の指す領域が重複する場合の処理は未定義
RET
dst のアドレス
■ memmove
SYNTAX
void *memmove(
void *dst, // Copy 先
const void *src, // Copy 元
size_t n // コピーバイト数
);
DESC
dst の先頭から n 文字分 src をコピーする
strcpy()と異なり空文字('\0')を付加しない
src を単なるメモリブロックとして扱うため
途中に空文字('\0')も含めてコピーを続ける
dst と src が重なっているときの動作は memcpy() では未定義ですが、
このmemmove() では正しくコピー(つまり移動)がされる
■ memcmp
SYNTAX
int memcmp(
const void *buf1,
const void *buf2,
size_t n
);
DESC
buf1 と buf2 を先頭から n バイト分比較する
比較は unsigned char としてされる
buf1 と buf2 をメモリブロックとして扱うため
空文字 \0 を含んでいても比較を続ける
RET
> 0 buf1 > buf2
< 0 buf1 < buf2
==0 buf1 = buf2
POINT
型ごとの内部表現の違いを調べるときにも使える。
// int < -> float の mem layout の違いがわかる. -> 0 は同じ 1 は異なる.
{
int i = 1;
// 型変換. ( 01 の並びが変わる ( 表現方法がかわる ) )
float f = i;
// 中身を見てみる.
memDump( &i );
memDump( &f );
// ここでも型変換がおきるらしい.
if ( f == i ) {
printf( "same\n" );
}
}
■ memset
SYNTAX
void memset( void *adr, int c, size_t n );
DESC
buf の先頭から n バイト分 c をセット
adr: unsigned char *型にキャストされ、
c: unsigned char型にキャストされる
■ malloc
SYNTAX
void *malloc( size_t sz );
DESC
sz 分のメモリをヒープ領域から確保する。
POINT
デバッグ用に malloc をラップする。
#ifdef _DEBUG
# define MALLOC( s ) malloc( s, __FILE__, __LINE__ )
#else
# define MALLOC( s ) malloc( s )
#endif
void *p = MALLOC( 128 );
■ IO
■ printf
SYNTAX
%[フラグ][最小フィールド幅][.精度][長さ修飾子]変換指定子
RET
出力した文字数
符号指定.
+ : [ %+f ] : 常に符号表示
長さ修飾子
l : [ %lb ], :
変換修飾子
d : [ %d ] : 10 進整数
u : [ %u ] : 10 進符号なし整数
x : [ %x ] : 16 進符号なし整数
s : [ %s ] : 文字列
p : [ %p ] : Pointer の値
f : [ %f ] : 小数点形式 浮動小数
e : [ %e ] : 指数形式 浮動小数
指定子 対応する型
%c char 1文字を出力 "%c"
%s char 文字列を出力 "%8s" , "%-10s"
%d int, short 整数を10進で出力 "%-2d" ,"%03d"
%u unsigned int,
unsigned short 符号なし整数を10進で出力 "%2u" ,"%02u"
%o int, short,
unsigned int,
unsigned short 整数を8進で出力 "%06o" ,"%03o"
%x int, short,
unsigned int,
unsigned short 整数を16進で出力 "%04x"
%f float 実数を出力 "%5.2f"
%e float 実数を指数表示で出力 "%5.3e"
%g float 実数を最適な形式で出力 "%g"
%ld long 倍精度整数を10進で出力 "%-10ld"
%lu unsigned long 符号なし倍精度整数を10進で出力 "%10lu"
%lo long, unsigned long 倍精度整数を8進で出力 "%12lo"
%lx long, unsigned long 倍精度整数を16進で出力 "%08lx"
%lf double 倍精度実数を出力 "%8.3lf"
精度
浮動小数の小数の桁数を指定する。
char *p
// 0xaabbccdd;
printf( "%#02x " , p[i] );
// AABBCCDD;
printf( "%#02X " , p[i] );
int i = 10;
[ %04d ] : 桁数表示
WARNING
最小フィールド幅は最小の幅を指定するだけ。
幅よりも大きい値がくると幅数がふえる
int i = 2;
// " 2 2"
printf( "%2d%2d" , i );
i = 123;
// "123123"
printf( "%2d%2d" , i );
std::cout を利用すれば format 演算子は必要はない。
#include< iostream>
std::cout < < i < " " < < f < < " " p < < endl;
assert( !printf("name: %s" , s.c_str()) && i != N );
"r" , "rb" を指定するのは [\n] がきたときに MS 用に
\r\n と変換するため ( これだけの違い )
■ sprintf
SYNTAX
int sprintf(
char *str,
const char *format,
...
);
RET
OK : strに格納した文字数(最後の'\0'は除く)
DESC
書式 format にしたがい
printf 関数と同様の変換をした出力を文字列 str にセットする。
WARNING
%X の場合は
整数を引数とするため
char -> int
-1 -> 0xFFFF FFFF
// 4 Byte 整数に CAST される
char c;
sprintf( buf, "%X" , c );
WARNING
format によって簡単にバッファーオーバーランを起こすため
書き込み文字列指定 sprintf を使うこと。
REFERENCE _snprintf_s
char buf[256];
void log( const char *msg ) {
sprintf( buf, "%s" , msg );
}
■ vsnprintf_s(_vsnprintf_s)
SYNTAX
int vsnprintf_s(
char *buf,
size_t sz, // バッファサイズ
size_t count, // 書き込む文字数 ( 最後の \0 を含めない )
const char *format,
va_list argptr
);
RET
N : 書かれた文字数 ( \0 を含めない )
-1 : エラー または count 以上の文字が切り捨てられた。
DESC
vsprintf のセキュア版。
C4996 が出る場合は これを利用する。
POINT
vsnprintf_s と _vsnprintf_s は同じ。 ( _vsnprintf_s は下位互換のため残している )
char buf[ 256 ];
_vsnprintf_s( buf, sizeof(buf), sizeof(buf)-1, "test %s" , __FILE__ );
■ vsnprintf
SYNTAX
int vsnprintf(
char * restrict s,
size_t n,
const char * restrict format,
va_list arg
);
■ vsprintf(vswprintf)
DESC
可変引数リストをもつ sprintf
printf のカスタマイズ版をつくる。
void print( const char *form, ... )
{
char buf[1024];
va_list arg;
va_start( arg, form );
vsprintf( buf, form, arg );
va_end(arg);
printf ( "INFO: %s\n" , buf );
}
void main()
{
print( "result = %d" , 777 );
print( "msg = %s" , "test" );
}
■ vprintf
SYNTAX
vprintf(
const char *format, // 書式文字列
... // 可変引数
);
DESC
可変引数と書式文字列従って標準出力へ書き出す
RET
N : 出力した byte 数
< 0: 出力した byte 数
void p(const char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
char buf[256];
gets(buf);
}
■ fflush
SYNTAX
fflush
DESC
バッファリングされた出力を強制的に出力する
■ fopen
SYNTAX
FILE *fopen(const char *FILE, const char *MODE);
DESC
■ fseek
SYNTAX
int fseek(
FILE *FP, // File Pointer
long OFFSET, // 検索位置からの offset
int WHENCE // 検索開始位置
)
DESC
FILE Pointer の読み込み位置を移動する
FILE *fp = fopen( "test.txt" , "r" );
// 先頭 から offset 0
fseek( fp, 0, SEEK_SET );
long s = ftell( fp );
// 末尾 から offset 0
fseek( fp, 0, SEEK_END );
long e = ftell( fp );
// file size を取得
int sz = e - s;
printf( "file size %d\n" , sz );
// データを読む場合は先頭にもどすこと
fseek( fp, 0, SEEK_SET );
char *buf = (char*)malloc( sz );
fread( buf, sz, 1, fp );
■ ftell
SYNTAX
long ftell(FILE *FP);
DESC
File Pointer の位置を返す
// FileSize をとる
fseek(fp, 0, SEEK_END);
uint32_t e = ftell(fp);
printf("file pos = %ld\n" , e );
fseek(fp, 0, SEEK_SET);
int s = ftell(fp);
printf("file pos = %ld\n" , s );
■ rand
SYNTAX
int rand()
DESC
0 - RAND_MAX の値を返す。
WARNING
あまり random でないので利用しないこと。
include< stdlib.h>
int r = rand();
■ scanf
SYNTAX
int scanf( const char *fmt, ... );
// space, tab などの 空白文字は 無視される
// shell> 25 DEC 1111
scanf( "%d %s %d" , &day, month, %year );
WARNING
// 値を格納できるように Pointer でないといけない
BAD
// 領域外の Memory にアクセスされてしまう
int n;
scanf( "%d" , n );
OK
scanf( "%d" , &n );
■ getchar
SYNTAX
int getchar()
DESC
STDIN から 1文字の入力をうける
WARNING
int 型だが, 入力ストリームからうけるサイズは 1 byte
#define getchar( ) getc(stdin)
■ gets
SYNTAX
char *gets(
char *buf
);
DESC
標準入力から 改行コード \n があるまで buf へ読み取る。
■ fgets
SYNTAX
char *fgets(
char *buf,
int n,
FILE *fp
);
DESC
行単位で file から n-1 サイズ分の文字列を buf へ読む。
読み込む文字は \n も含まれる。
最後に '\0' が追加される。
\n が途中できた場合, その時点で終了。
RET
!0: 読み込んだ領域の先頭アドレス
vector< string> readText( const char *path )
{
vector< string> a;
char buf[1024];
FILE *fp = fopen( path, "r" );
while( fgets( buf, sizeof(buf), fp ) ) {
// [\n] 削除
buf[ strlen(buf) - 1 ] = '\0';
a.push_back( buf );
}
fclose(fp);
return a;
}
標準入力も FILE *なので fgets から読むことができる。
{
char buf[1024];
while( fgets( buf, sizeof(buf), stdin ) ) {
// [\n] 削除
buf[ strlen(buf) - 1 ] = '\0';
printf( "stdin input [%s]\n" , buf );
}
}
指定したディレクトリ内のファイルをアーカイブする。
find -name "*.bmp" | xargs cygpath -am | ./archive.exe
■ putchar
SYNTAX
int putchar(int c);
RET
DESC
stdout へ 文字 c を 出力する
#define putchar(c) __sputc(c, stdout)
nm /lib/libc.a
./main
# Pipe
echo abcdef | ./main
int main() {
int c;
// stdin から 1 文字取得 -> keyboard なら block される
c = getchar();
// stdout への Buffer を 0 にすると, すぐに出力される
#if 0
setbuf( stdout, NULL );
#endif
for (c = 'A'; c < = 'Z'; c++) {
// Buffering されるため, すぐには画面には表示されない
putchar(c);
}
sleep( 3 );
// ここで出力
printf("finish\n" );
}
■ putc
SYNTAX
int putc(int CH, FILE *fp);
DESC
fp が指すストリームへ1文字を出力するマクロ。
CH が指すデータは int 型から unsigned char 型へ変換後に出力される。
■ puts
SYNTAX
void puts();
DESC
stdout へ指定文字列を出力するマクロ。
fputs( str, stdout); // == printf( str );
WARNING
実装によっては文字数制限あり. 360 ( 256 しか入力できない. )
■ math
#include< math.h>
#include< stdlib.h>
■ abs
SYNTAX
int abs(int i);
DESC
i の絶対値をかえす
■ fabs
SYNTAX
double fabs(double d);
DESC
d の絶対値をかえす
■ fabsf
SYNTAX
float fabs(float f);
DESC
f の絶対値をかえす
ANSI C 準拠ではないが、 VisualC++, gcc コンパイラでは使えそう。
float f = -0.1f;
f = fabsf( f );
printf( "ret %f\n" , f );
■ stdlib
■ atoi
SYNTAX
int atoi( const char *)
DESC
文字列を 数値に変換
int i;
// 10
i = atoi( "10" );
// 10
i = atoi( "10.1" );
// 数値に変換できない場合は 0
// RET
// 0
i = atoi( "aaa" );
■ abort
SYNTAX
void abort()
DESC
プログラムを不正終了する。
C++ 例外の terminate() からも呼ばれる。
■ getenv
DESC
C 標準関数. 環境変数の値を取得
SYNTAX
// const char * でない点に注意
char *p = getenv( ENV );
char *p = getenv( env ); // 大文字小文字 同じ結果 ?
RET
OK : ptr
FAIL: NULL
ENV は大文字で統一
■ qsort
SYNTAX
void qsort(
void *data, // 並び替えるデータのアドレス
size_t nr, // 要素数
size_t size, // 各要素の size
int(*compar)(const void *a, const void *b) // 比較関数
);
DESC
nr 個の大きさ size の要素をもつ配列を並べ変える。
昇順に並べるには比較関数は次の規約に従うようにする。
比較関数
int(*compar)(const void *a, const void *b)
a > b : return positive
a < b : return negative
a == b : return 0
WARNING
二つの要素の比較結果が等しいとき
並べ変えた後の二つの順序は規定されていない
int cmp( const void *_a, const void *_b ) {
int *a = (int *)_a;
int *b = (int *)_b;
if ( *a > *b ) return 1;
else if ( *a < *b ) return -1;
return 0;
}
int a[] = { 1, 4, 9, 2, 5 };
size_t nr = sizeof(a)/sizeof(int);
// 1 2 4 5 9
qsort( a, nr, sizeof(int), cmp );
■ bsearch
SYNTAX
void *bsearch(
const void *key, // 検索する値
const *void // 配列の先頭アドレス
size_t nr, // 要素数
size_t size, // 要素ごとのサイズ
int (*COMPAR)(const void *, const void *)
);
DESC
ソートすみの配列から、key と同じ値をもつ要素を検索してそのアドレスを返す。
比較関数
int(*compar)(const void *a, const void *b)
a > b : return positive
a < b : return negative
a == b : return 0
int cmp( const void *_a, const void *_b ) {
int *a = (int *)_a;
int *b = (int *)_b;
if ( *a > *b ) return 1;
else if ( *a < *b ) return -1;
return 0;
}
int a[] = { 1, 4, 9, 2, 5 };
size_t nr = sizeof(a)/sizeof(int);
// 1 2 4 5 9
bsearch( 1, a, nr, sizeof(int), cmp );
■ ctype
■ isspace
SYNTAX
int isspace(
int c
);
DESC
c が空白文字の場合、0 以外の値を返す
RET
!0 : c が空白文字 (0x09 〜 0x0D || 0x20)
POINT
結果は現在のロケールの LC_CTYPE カテゴリの設定によって異なる
POINT
CRT デバッグ ライブラリを使用すると
CRT アサートを表示する
CRT デバッグ ライブラリを使用すると
isspace はパラメータを配列のインデックスとして使用し
■ ispunct
SYNTAX
int ispunct(
int c
);
// Locale を指定する Version
int _ispunct_l(
int c,
_locale_t locale
);
DESC
文字が区切り文字かどうかを判定
RET
!0 : c が特定の区切り文字の表現
POINT
区切り文字 とは 印字可能な文字 から 次の範囲ではないもの
印字可能な文字とは
( 空白文字 || isalnum(c) != 0 ) でない文字のこと
■ isalpha
SYNTAX
int isalpha(
int c
);
DESC
c が英字の表現の場合
これらの各ルーチンは 0 以外の値を返す
RET
!0 : c が A 〜 Z、|| a 〜 z
■ isalnum
SYNTAX
int isalnum(
int c
);
DESC
c が英数字のいずれかである場合、0 以外の値を返す
c が isalpha || isdigit の値が 0 以外になる場合
( c が A 〜 Z、a 〜 z、|| 0 〜 9 )
POINT
isalnum の結果は
現在のロケールの LC_CTYPE カテゴリの設定によって異なる
iswalnum はテスト条件の結果はロケールとは無関係
POINT
UNICODE は Locale を統一しているということ
■ isleadbyte
SYNTAX
int isleadbyte(
int c
);
RET
!0 : テスト合格
0 : テスト不合格
"C" ロケール, シングルバイト文字セット (SBCS: single-byte character set) ロケールでは
isleadbyte は常に 0 を返す
■ setlocale
SYNTAX
char *setlocale(
int category, // 設定する locale のカテゴリ
const char *locale // Locale 名
);
wchar_t *_wsetlocale(
int category,
const wchar_t *locale
);
DEP
< locale.h>
REF
v=VS.71 .aspx
DESC
RET
NULL : 設定が無効
!NULL : 設定された Locale 名への Pointer
// すべての Category を "english" に
setlocale( LC_ALL, "English" );
POINT
Category の種類
LC_CTYPE
文字処理関数 (isdigit、isxdigit、mbstowcs、mbtowc の各関数は除く)。
POINT
Localeとは
国や言語にあわせた環境設定のこと
この地域性に合わせてプログラムのさまざまな側面をカスタマイズできる
ロケールのカテゴリは
日付,通貨値の表示形式など
1 つの言語で複数の形式がサポートされている場合
その言語の既定文字列を locale に設定したときに
setlocale 関数が戻す値をチェックし
どの形式が有効になっているのか確認する必要がある
たとえば、
"chinese" を指定すると
chinese-simplified || chinese-traditional のいずれかの値が返る
POINT
引数 locale は、ロケール名を指定する文字列へのポインタです
locale で空の文字列を指すと
そのロケールは実装で定義されているネイティブ環境になる
setlocale( LC_CTYPE, "" ); // 自分の環境では 日本語 ( SJIS )の isleadbyte() が機能した
値 C は、C に関する ANSI 規格に最低限準拠した環境を指定します
C ロケールでは
char 型のデータがすべて 1 バイトで表現され
値が常に 256 未満と想定される
C ロケールは、Microsoft Visual C++ Version 1.00、Microsoft C &&
C++ の旧バージョンでサポートされている唯一のロケールです
Microsoft Visual C++ では、「言語および国/地域識別文字列」で示す
すべてのロケールをサポートしています
プログラムの起動時に
次のステートメントと同等の処理が実行される
setlocale( LC_ALL, "C" );
■ math
■ atan
SYNTAX
float atanf( float )
double atan ( double )
DESC
tangent の値から [ -PI/2 : PI /2 ] の範囲の radian を返す
float rad = atanf( v.z / v.x )
■ system
■ exit
SYNTAX
void exit( int )
DESC
プログラムを終了する。
■ atexit
DESC
exit() の前にコールされるコールバックを指定する。
int atexit( void (*fn)() );
■ abort
SYNTAX
void abort()
DESC
プログラムをを異常終了させる。
SIGABRT 例外を発生させる。
■ atof
SYNTAX
double atof( const char *s )
DESC
文字列表現の数値を double 型に変換する。
POINT
strtod( s, (char**)NULL )と同じ
■ system
SYNTAX
int system( char *cmd );
RET
シェルが返す終了コード。
DEP
< stdlib.h>
DESC
Execute command string
別のコマンド(Program)を実行する
OS の Shell( Windows なら cmd.exe )に引数の文字列をわたす
こうしているのと同じ
:: こういう呼び出しをすると Block される
cmd> xxx.bat
:: これは Block されない
cmd> start xxx.bat
起動した Program が終了するまでまつ
( 正しくは cmd.exe が終了するまでまつ )
WARNING
Windows Application から実行すると, ConsoleWindow が表示される。
こうすると とりあえず できる
system( "start /min xxx.bat" );
ProcessTree 的にはこうなる
// ie. system() は cmd.exe を起動する < -> cmd /c "xxx" と同じ
xxx.exe
cmd.exe // system()
start command を使うとこうなる
■ 非.ANSI C
■ sprintf_s
SYNTAX
int sprintf_s(
char *buffer,
size_t sz, // バッファサイズ
const char *format
[, argument] ...
);
RET
-1 : 失敗
DESC
VS2005 から 文字列操作関数の使用に警告がでる
文字数を指定するのは 責任はあなたにありますよ ! ということ
sprintf_s は
出力バッファのサイズを受け取り、
バッファが小さすぎて出力テキストを格納できないなら
バッファは空文字列に設定され無効なパラメータハンドラが呼ばれる。
snprintf とは異なり sprintf_s() は必ずバッファは null で終わる。
sprintf_s と sprintf の違い
sprintf_s は書式指定文字列の有効な書式指定文字をチェックするが
sprintf は書式指定文字列またはバッファが NULL ポインタかどうかのみをチェックする。
WARNING
"test" < -> "test\0" と同じ 5 byte かきこまれる
4 文字だと思うと大怪我をする
// 次はおちる
// Debug では : Assert: Buf Too Small となる
// Release では : おちる
sprintf_s( buf, 1, "" );
警告を消すには
#define _CRT_SECURE_NO_DEPRECATE 1
#pragma WARNING (disable:4996)
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
WARNING
null terminate されないので _snprintf() は禁止 !
{
// Buffer Size = 8
char buf[] = "1234567" ;
int ret = _snprintf_s( buf, sizeof(buf), sizeof(buf)-1, "foo" );
int ret = _snprintf_s( buf, sizeof(buf), _TRUNCATE, "foo" );
assert( ret != -1, "fail" );
if ( ret == -1 ) {
printf( "切り捨てられた\n" );
}
else {
printf( "%d + 1 文字かきこみました\n" , ret );
}
}
■ _snprintf_s
SYNTAX
int _snprintf_s(
char *buf, // 書きこむバッファへのポインタ
size_t szBuffer, // バッファサイズ
size_t count, // 書く文字数
const char *format
[, argument] ...
);
RET
-1 : バッファサイズが足りずに切り捨てられた
N : 書いたバイト数 ( null を除く )
DESC
指定したバッファへ, count バイト数の文字 + NULL 文字を書き込む
POINT
Secure といっても 引数の指定を間違えるとおちる
意味は
szBuffer の 大きさの Buffer へ
カウント数 + [\0] バイトを書いてね ! という意味
だから 次のように言うと , 馬鹿正直に実行しておちる
適切な指示をしないと ダメということ
{
// 8 byte の大きさ
char buf[] = "01234567" ;
// + null だけ overrun する
// ERROR
_snprintf_s( buf, sizeof( buf ), sizeof(buf), "01234567" );
_TRUNCATE( イッパイ まで ) を利用すれば, バッファオーバーランをしない
// n = -1, "0123456"
char buf[8];
int n = _snprintf_s( buf, sizeof( buf ), _TRUNCATE, "01234567" );
以下のようにするのが正しい。null terminate してくれるのを忘れずに。
_snprintf_s( buf, sizeof( buf ), sizeof(buf)-1, "abcdefgh" );
■ _popen._pclose
SYNTAX
DESC
System 関数に Pipe 機能を追加した内容
shell に Command を実行させて, Read, Write としての Pipe を取得する
WARNING
ConsoleWindow は表示されてしまう
表示しないためには
int test() {
FILE *in_pipe;
// 読み込み用バッファ
const int SZ = 256;
char buf[SZ + 1];
int nr;
memset(buf,'\0',sizeof(buf));
/*
POINT
stdout から取得するには 読み込み用の PIPE を開く
stdin へ書き込むには 書き込み用パイプを開く
*/
// hostname の結果をうけとる
in_pipe = _popen("hostname" ,"r" );
/*
処理は通常のファイルストリームと同じ
*/
if(in_pipe != NULL){
nr = fread(buf,sizeof(char),SZ,in_pipe);
while ( nr > 0 ) {
puts( buf );
nr = fread(buf,sizeof(char),SZ,in_pipe);
}
/* 閉じる */
_pclose(in_pipe);
return 1;
}
return 0;
}
■ sizeof
DESC
object の memory 占有領域を調べる
object の size なのでポインタを指定するとポインタオブジェクトのサイズを返す
void func( char *buf ) {
// ERROR: sizeof( buf ) == 4
memset( buf, sizeof(buf), 0 );
}
TIP
COUNTOF macro で 宣言が可視の配列( pointer でない )のサイズを取得可能
count of では ptr 配列objsize / 1ptr 配列objsize として計算
WARNING
wptr 配列では sizeof( ptr[0] ) = 4;
int data[10]; // = 40
int *p
p = data;
sizeof( p ) = 4 // pointer object size == 4
sizeof( *p ) = 4 // p がさす obj という解釈にならない
■ unicode
lstrcpy( LPTSTR str1, LPCTSTR lpString2 )
■ 呼び出し規約
DESC
関数コールをするときにスタック上への引数の渡し方と、後方付けの方法をルール化したもの。
複数の言語で呼び出し方が異なるため.
関数呼び出し時の方法のこと。
POINT
よく使うものは次の3つ
呼び出し規約によって関数の修飾名も決まる。
呼び出し規約を変えると、
混合モードで表示されるアセンブラコードが代わることを確認できる。
デバッグ時にアセンブラコードをトレースする際に役にたつ。
call stack( stack ) への格納方法, 復帰方法, 名前修飾が異なる
testcdecl( g_str, 1, 'a' );
// 引数をスタックに push
push 61h
push 1
mov eax, [g_str(0040000)]
push
// 関数をコール
call testcdecl(0040000)
// 後方付けは 呼ぶ側がする ( 12 byte スタックポインタを戻す )
add esp, 0ch
■ cdecl
引数のセット順
右から左
関数名の修飾
_ がつく。( test() -> _test() )
スタック管理
呼び出し側がスタックを片つける
intel86 系で使用される。
sensitive
戻り値は EAX に格納される。
// 明示的に cdecl 規約で呼ぶことを宣言する。
void _cdecl function( param );
■ stdcall
引数のセット順
左から右
関数名の修飾
_ と最後に @ と引数のバイト数をつける。( test() -> _test@4() )
スタック管理
呼ばれた側がスタックを片つける
win32 API 標準. stack の掃除は sub func 自身でする( -> 可変長の引数 list は 未サポート )
POINT
_XXX@N という関数名 N: 引数サイズ( ie. stack clean のサイズを通知 )
引数をクリーンするためにサイズを知る必要があるということ
■ thiscall
引数のセット順
右から左
関数名の修飾
なし。
スタック管理
呼ぶ側がスタックを片つける
_stdcall とほぼ同じ。
this ポインタは ecx に格納される。
■ firtstcall
register を利用して 引数渡しをする
-> ということは, register は早い
戻り値は register, stack にはいる
@XXX@N
POINT
呼び出し規約により, Symbol に変更が加えられる ( 数値は arg size )
arg
cdecl
stdcall : _ + (関数名) + @8
fastcall : @ + (関数名) + @8
■ C99(Cの新機能)
■ stdint
C 言語のプログラムを移植するときの問題点の一つに、
typedef された int 型の大きさがあります。
例えば、
int型 が 32bit と仮定して、int32 という型を typedef して作ったとします。
そしてソースプログラム中で int32 という型を使用します。
しかし
他の環境へそのソースプログラムを持っていった時には
int が 32bit でないかもしれません。
したがって、もしそのままソースプログラムを使用すると
32bit を仮定しているプログラムなのに、実際は 16bit になってしまったということが起こり得ます。
そのようなことから環境が変わった場合、
新たに定義し直す必要があります。stdint.h はこの問題の一つの解決方法を提示します。
整数を、厳密、最小、演算が速い最小、ポインタ格納可能、最も大きな幅、というクラスにわけ、それをみたす型と限界を定義します。だから、例えば、演算が速い最小の整数型を使えば、他の環境へ持っていってもそのままソースプログラムを再利用することができる可能性が高くなるわけです。
intN_t 符号付き整数型の typedef 名
uintN_t 符号なし整数型の typedef 名
厳密にその幅を持つ整数型。
これらの型は任意であるが、もし実装が 8, 16, 32, 64 ビットの整数型を提供しているなら相当する
typedef 名を定義する必要がある。
例:N=32の場合:int32_t, uint32_t
ポインタを格納可能な整数型に関する
定義定義名 説明
intptr_t ポインタを格納するのに適切な符号付き整数型の typedef 名
uintptr_t ポインタを格納するのに適切な符号なし整数型の typedef 名
void* を (u)intptr_t に変換でき、かつそれを void* に戻せ、
その結果が元のポインタと等しくできる整数型。これらの型は任意である。
WARNING
C99 に準拠していない VisualStudio 2008 では存在しない。
VisualStudio 2010 では $VC/VC/include/ 以下に存在する。
複数の環境で利用するには以下のようにする。
// VisualStudio 環境では __int32 の別名として int32_t を定義する
#ifdef __MSC_VER
typedef __int32 int32_t
#else
# include< stdint.h>
#endif
■ 高速化
■ キャッシュメモリ(CacheMemory)
cache とは隠し場所のこと
メインメモリからデータを CPU ( レジスタ )にメモリを転送した際に
そのデータを CPU に近いメモリにためておくこと。
こうすると次に必要になったデータを近い場所からとることができる。
L2, L3 キャッシュという。
メモリから読む時は 64 byte 単位などをまとめて読むので
なるべく連続したアドレスでアクセスすると
キャッシュ( 近い場所 )からデータをとれる。
キャッシュを有効に使うために次のことに気をつける。
const unsigned int n = 1024*1024*16;
const unsigned int N = 10*10;
static int a[n];
static __int64 a[n];
memset( a, 0, sizeof(a) );
for( unsigned int i=0; i< n; i++ ){
for( unsigned int j=0; j< N; j++ ){
++a[i];
}
}
■ Endian(エンディアン)
数値の桁がどういう順番で並んでいるか
ByteData [0x0000000]を上位からか、下位からかどちらかの方法でメモリに格納する順番
MultiByte にならないと問題にならない
1バイトより大きいサイズのオブジェクトの表現方法は環境によって異なる。
LittleEndian :
ちっちゃい順
int i = 0xAABBCCDD;
// メモリ上では 下の桁から格納される
[DD][CC][BB][AA]
ネットワーク経由で送られたデータはビッグエンディアン
// メモリレイアウト
[11][22][33][44]
// BigEndian としてみると
0x11223344
// LittleEndian としてみると
0x44332211
BigEndian : 大きい桁順 ( PowerPC, ネットワークバイトオーダー )
LittleEndian : 小さい桁順 ( インテル系PC )
Big endian( 人間にとってわかりやすい )
上位バイトからそのまま
[11][AB][22][CD]
ファイルに格納する byte 列も当然影響をうける
PC 環境では BE はありえない
Endian が異なる環境では、バイトの並びを並び替える。
uint32_t tmp = 0;
tmp |= 0x000000ff & (ret >> 24);
tmp |= ((0x000000ff & (ret >> 16)) < < 8);
tmp |= ((0x000000ff & (ret >> 8)) < < 16);
tmp |= ((0x000000ff & (ret >> 0)) < < 24); // 下位 を 上位へ
ret = tmp;
uint16_t tmp;
tmp |= 0x00ff & (ret >> 8);
tmp |= ((0x00ff) & (ret >> 0)) < < 8;
環境によらず Endian をそろえるには int 型のオブジェクトがきたら必ず
下位の桁から順番にストリームへ出力する。
char str[4];
str[0] = ( i & 0x000000ff );
str[1] = ( i & 0x0000ff00 ) >> 8;
str[2] = ( i & 0x00ff0000 ) >> 16;
str[3] = ( i & 0xff000000 ) >> 24;
ofstream out;
out.write( str, 4 );
POINT
endian の相違は単一のシステム( 閉じた世界 )で運用する場合は問題なし。
WARNING
異なるシステムでネットワークを通して、[ バイト単位 ]のやりとりをする場合
2. 異なるシステム間でバイナリファイルを交換する場合。
3. 異なるシステムにプログラムを移植する場合。
4. 構成するプロセッサが異なるマルチプロセッサ環境で共有メモリを使用する場合。
{
unsigned long x = 0x11223344UL;
unsigned char *p = (unsigned char)&x;
// Memory 内の表現を見る
for( i=0; i< sizeof(long); i++ ){
printf( "%x" , *p++ );
}
}
// これと同じ
fwrite( &x, sizeof(x), 1, stdout );
■ NetworkByteOrder
TCP/IP network ではendian の差分を統一するため、
多バイトからなるパケットをBig endian で統一。
画像、音声ファイルのバイナリファイルも互換性を保つため、endian を規定。
Unicode は構成単位が2byte。バイトオーダーマーク( BOM )というもので
エンディアン方式を判別する仕組み。
■ 再帰処理
同じ構造がネスト化されている状態では、再帰処理ができることが多い。
たとえばツリー構造は、ツリーのある部分もツリーであるので
再帰処理ができる。
再帰処理は基本的に処理が簡単になる。
■ データ構造
■ 配列
ソートされた配列は 2分探索が使えるようになる。
この場合は計算量が O(logN) にまで減る。
■ リンクリスト
■ バイナリーツリー
2分木は検索を高速をすることができる構造。
データの集合を自分より大きい、小さいで分別してしまうことで
検索対象を半分くらいに減らすことで高速化できる。