解説クラスSupport
解説クラスSupport
2024年2月3初講(初稿)
先に、解説『高速大容量の代表的処理が、画像処理』で紹介
した!画像処理ライブラリ内のサポート的
クラス【class Support{}】の解説を今回は、行います!
1.ヘッダーファイル
比較的、小サイズのヘッダーファイルなので全て
Noteの「code」機能で以下の様に示した後で解説して行
きます!
// Support.h: Support クラスのインターフェイス
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_)
#define AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "ImageFuncDef.h" // 画像処理関数の定義部
#include "TypeArray.h" // TypeArray 記述用
#include "TypeSf.h" // TypeSf 記述用
#include "TypeCursor.h" // TypeCursor 記述用
/***********************************************************************/
/***** 座標組用データ構造 ****/
/***********************************************************************/
struct TypeXY { // 座標組用構造体の型宣言
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
}; // 型名「TypeXY」
struct TypeXYI { // 座標組用構造体の型宣言
int x; // x座標(32ビット)
int y; // y座標(32ビット)
}; // 型名「TypeXYI」
struct TypeXYF { // 座標組用構造体の型宣言
float x; // x座標部(単精度)
float y; // y座標部(単精度)
}; // 型名「TypeXYF」
struct TypeXYD { // 座標組用構造体の型宣言
double x; // x座標部(倍精度)
double y; // y座標部(倍精度)
}; // 型名「TypeXYF」
struct TypeAB { // 始終点X座標の構造体の型宣言
short a; // 終点X座標(16bit)
short b; // 終点X座標(16bit)
}; // 型名”TypeABY”
struct TypeABY { // 始終点X座標+Y座標の構造体の型宣言
short a; // 終点X座標(16bit)
short b; // 終点X座標(16bit)
short y; // Y座標(16bit)
}; // 型名”TypeABY”
struct TypeXYHV { // 座標範囲用データ構造
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
short h; // x座標幅(16ビット)水平幅
short v; // y座標幅(16ビット)垂直幅
}; // 型名「TypeXYHV」
struct TypeXYHVI { // 座標範囲用データ構造
int x; // x座標部(32ビット)
int y; // y座標部(32ビット)
int h; // x座標幅(32ビット)水平幅
int v; // y座標幅(32ビット)垂直幅
}; // 型名「TypeXYHV」
struct TypeXYHVIX { // 座標範囲+Index用データ構造
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
short h; // x座標幅(16ビット)水平幅
short v; // y座標幅(16ビット)垂直幅
int i; // Index(32ビット)
}; // 型名「TypeXYHVIX」
struct TypeXYRR { // 座標+楕円径用データ構造
float x; // x座標(単精度)
float y; // y座標(単精度)
float r1; // 短径(単精度)
float r2; // 長径(単精度)
float a; // 角度(単精度)
}; // 型名「TypeXYRR」
//page
/***********************************************************************/
/***** 個別計測項目用データ構造 ****/
/***** ① 始終点座標組 ****/
/***** ② 同2組み用 ****/
/***** ③ 相関値+座標 ****/
/***********************************************************************/
struct TypeXYXY { // 始終点座標組用データ構造
short x1; // 始点x座標部(16ビット)
short y1; // 始点y座標部(16ビット)
short x2; // 終点x座標部(16ビット)
short y2; // 終点y座標部(16ビット)
}; // 型名「TypeXYXY」
struct TypeXYXYI { // 始終点座標組用データ構造
int x1; // 始点x座標部(32ビット)
int y1; // 始点y座標部(32ビット)
int x2; // 終点x座標部(32ビット)
int y2; // 終点y座標部(32ビット)
}; // 型名「TypeXYXYI」
struct TypeXYXY2 { // 始終点座標2組用データ構造
short xa1; // 始点x座標部(16ビット):a組
short ya1; // 始点y座標部(16ビット):a組
short xa2; // 終点x座標部(16ビット):a組
short ya2; // 終点y座標部(16ビット):a組
short xb1; // 始点x座標部(16ビット):b組
short yb1; // 始点y座標部(16ビット):b組
short xb2; // 終点x座標部(16ビット):b組
short yb2; // 終点y座標部(16ビット):b組
}; // 型名「TypeXYXY2」
struct TypeCorr { // 相関値+座標組用データ構造
double d; // 相関値
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
}; // 型名「TypeCorr」
struct TypeCorrIX { // 相関値+座標組+Index用データ構造
double d; // 相関値
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
int i; // Index(32ビット)
}; // 型名「TypeCorrIX」
//page
/***********************************************************************************/
/***** クラス Support ****/
/***** 画像処理ライブラリの基本的動作サポート用 ****/
/***********************************************************************************/
class Support
{
public:
Support(); // コンストラクタ
virtual ~Support(); // デストラクタ
void Trace0( char* str ); // トレーサー
int ltos( // 整数→文字列
long data, // 32Bitデータ
char *str ); // 変換後文字列
char* ltosx( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod, // 桁制御 1~11: 右詰めの桁数
// -1~-11: 左詰めの桁数
int sw=0 ); // 0(省略時):空白,1:「0」詰め
int ltox( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod ); // 変換モード
// 0→0を含めて8桁変換
// 1→0を消して左詰
// 2→0を消して右詰
void cvtStrHex( // Hex文字列→整数
char *str, // 文字列
char *buf, // 16進数値列
int sw = 0 ); // 0:ABCDEF,1:abcdef
char* stoi( // 文字列→整数
char* ptr, // 数字文字列
int& data ); // 結果整数
char* skipSpace( // 空白文字の読み飛ばし
char* ptr ); // 文字列
char* skipSpaceComma( // 空白文字と「,」の読み飛ばし
char* ptr ); // 文字列
char* getStringLineToInt( // 文字列行から整数値取り出し
char* line, // 文字列行
int& d ); // 整数値
char* getStringLineToReal( // 文字列行から実数値取り出し
char* line, // 文字列行
double& d ); // 実数値
char* append( // 文字列連結
char *str1, // つながられる文字列領域
const char *str2 ); // つなげる文字列
char* strTime( // 時刻データ→文字列
char *str, // 変換された文字列
int h, // 時間データ(0~24)
int mi, // 分データ(0~60)
int s, // 秒データ(0~60)
int sw=0, // 0(省略時):空白,1:「0」詰め
int mode=0 ); // -1 hhmmss
// 0(省略時) hh:mm:ss
char* strDate( // 日付データ→文字列
char *str, // 変換された文字列
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d, // 日データ(1~31)
int sw=0, // 0(省略時):空白,1:「0」詰め
int mode=0 ); // -1 yymmddwww
// 0(省略時) yy-mm-dd www
int calcWeek( // 曜日の計算:0→日曜
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d ); // 日データ(1~31)
int calcLenString( char* str ); // 文字列の有効長さ算出
double makeReal( // 実数値作成
int d1, // 仮数:大数部
int d2, // 仮数:小数部
int exp ); // 指数:-40 ~ +49
void dtos( // double→文字列
double data, // 浮動小数数値データ
char *str ); // 変換された文字列
void up_fill( // メモリ書き込み:BYTE
int s, // 書き込むデータ 0..255
BYTE *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_short( // メモリ書き込み:short
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_long( // メモリ書き込み:long
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_int( // メモリ書き込み:int
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_void( // メモリ書き込み:汎用
int s, // 書き込むデータ 0..255
void *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_inc_byte( // Mem書込:増分:BYTE
int s, // 書き込むデータ
BYTE *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_short( // Mem書込:増分:short
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_long( // Mem書込:増分:long
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_int( // Mem書込:増分:int
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_void( // Mem書込:増分:void
int s, // 書き込むデータ
void *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_movb( // 転送:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs( // 転送:short
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl( // 転送:long
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movi( // 転送:int
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_mova( // 転送:BYTE:大容量時
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movv( // 転送:汎用
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2s( // 転送:BYTE→short
BYTE *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2l( // 転送:BYTE→long
BYTE *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movc2s( // 転送:char→short
char *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movc2l( // 転送:char→long
char *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2b( // 転送:short→BYTE
short *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2c( // 転送:short→char
short *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2l( // 転送:short→long
short *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2b( // 転送:long→BYTE
long *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2c( // 転送:long→char
long *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2s( // 転送:long→short
long *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movu2l( // 転送:UWORD→long
UWORD *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb_inc( // 転送:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs_inc( // 転送:short
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl_inc( // 転送:long
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movi_inc( // 転送:int
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movv_inc( // 転送:汎用
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2s_inc( // 転送:BYTE→short
BYTE *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2l_inc( // 転送:BYTE→long
BYTE *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movc2s_inc( // 転送:char→short
char *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movc2l_inc( // 転送:char→long
char *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2b_inc( // 転送:short→BYTE
short *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2c_inc( // 転送:short→char
short *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2l_inc( // 転送:short→long
short *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2b_inc( // 転送:long→BYTE
long *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2c_inc( // 転送:long→char
long *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2s_inc( // 転送:long→short
long *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
int up_sumb( // 合計:BYTE
BYTE *ps, // データへのポインタ
int l // 合計する数量
);
int up_sums( // 合計:short
short *ps, // データへのポインタ
int l // 合計する数量
);
int up_sumus( // 合計:UWORD
UWORD *ps, // データへのポインタ
int l // 合計する数量
);
int up_suml( // 合計:long
long *ps, // データへのポインタ
int l // 合計する数量
);
int up_sumi( // 合計:int
int *ps, // データへのポインタ
int l // 合計する数量
);
int up_mov_sumus( // 転送+合計:UWORD
UWORD *ps, // Sデータへのポインタ
UWORD *pd, // Dデータへのポインタ
int l // 合計する数量
);
void up_fill_float( // メモリ書き込み:float
double s, // 書き込むデータ 0..255
float *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_double( // メモリ書込み:double
double s, // 書き込むデータ 0..255
double *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_inc_float( // 書込:増分版:float
double s, // 書き込むデータ 0..255
float *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_double( // 書込:増分版:double
double s, // 書き込むデータ 0..255
double *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_movf( // 転送:float
float *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd( // 転送:double
double *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2f( // 転送:BYTE→float
BYTE *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2d( // 転送:BYTE→double
BYTE *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2f( // 転送:short→float
short *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2d( // 転送:short→double
short *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2f( // 転送:long→float
long *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2d( // 転送:long→double
long *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2f( // 転送:double→float
double *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2d( // 転送:float→double
float *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2b( // 転送:float→BYTE
float *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2s( // 転送:float→short
float *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2l( // 転送:float→long
float *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2b( // 転送:double→BYTE
double *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2s( // 転送:double→short
double *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2l( // 転送:double→long
double *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf_inc( // 増分版転送:float
float *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd_inc( // 増分版転送:double
double *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2f_inc( // 増分版転送:BYTE→float
BYTE *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2d_inc( // 増分版転送:BYTE→double
BYTE *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2f_inc( // 増分版転送:short→float
short *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2d_inc( // 増分版転送:short→double
short *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2f_inc( // 増分版転送:long→float
long *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2d_inc( // 増分版転送:long→double
long *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2f_inc( // 増分版転送:double→float
double *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2d_inc( // 増分版転送:float→double
float *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2b_inc( // 増分版転送:float→BYTE
float *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2s_inc( // 増分版転送:float→short
float *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2l_inc( // 増分版転送:float→long
float *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2b_inc( // 増分版転送:double→BYTE
double *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2s_inc( // 増分版転送:double→short
double *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2l_inc( // 増分版転送:double→long
double *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
private:
void itos_1( // 整数→文字列
long data, // 10000未満のデータ
char *str // 変換後文字列
);
void itos_2( // 整数→文字列
long data, // 32Bitデータ
char *str // 変換後文字列
);
void up0movb( // 転送:基本:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
public: // 最小値/最大値
int MinLine( // 最小値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size // ライン長
);
int MinLine( // 最小値:ライン:short
short* line, // ライン情報Ptr
int size // ライン長
);
int MinLine( // 最小値:ライン:int
int* line, // ライン情報Ptr
int size // ライン長
);
DWORD MinLine( // 最小値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size // ライン長
);
DWORD MinLine( // 最小値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size // ライン長
);
double MinLine( // 最小値:ライン:float
float* line, // ライン情報Ptr
int size // ライン長
);
double MinLine( // 最小値:ライン:double
double* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:short
short* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:int
int* line, // ライン情報Ptr
int size // ライン長
);
DWORD MaxLine( // 最大値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size // ライン長
);
DWORD MaxLine( // 最大値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size // ライン長
);
double MaxLine( // 最大値:ライン:float
float* line, // ライン情報Ptr
int size // ライン長
);
double MaxLine( // 最大値:ライン:double
double* line, // ライン情報Ptr
int size // ライン長
);
int MinMaxLine( // 最小値最大値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
int MinMaxLine( // 最小値最大値:ライン:short
short* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
int MinMaxLine( // 最小値最大値:ライン:int
int* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
DWORD MinMaxLine( // 最小値最大値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size, // ライン長
DWORD& maxData // 返値:最大値
);
DWORD MinMaxLine( // 最小値最大値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size, // ライン長
DWORD& maxData // 返値:最大値
);
double MinMaxLine( // 最小値最大値:ライン:float
float* line, // ライン情報Ptr
int size, // ライン長
double& maxData // 返値:最大値
);
double MinMaxLine( // 最小値最大値:ライン:double
double* line, // ライン情報Ptr
int size, // ライン長
double& maxData // 返値:最大値
);
int MaxLineIndex( // 最大値時Index:ライン:BYTE
BYTE line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:short
short line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:int
int line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:UWORD
UWORD line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:DWORD
DWORD line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:単精度
float line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:倍精度
double line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:BYTE
BYTE line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:short
short line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:int
int line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:UWORD
UWORD line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:DWORD
DWORD line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:単精度
float line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:倍精度
double line[], // ライン情報Ptr
int size ); // ライン長
/********************************************************************************************/
/***** inline関数群 *****/
/********************************************************************************************/
public: // inline関数
inline int MaxLine( // 最大値:ライン:long
long* line, // ライン情報Ptr
int size // ライン長
){
return( MaxLine( (int*)line, size ) ); // 左記で実行
}
inline int MinLine( // 最小値:ライン:long
long* line, // ライン情報Ptr
int size // ライン長
){
return( MinLine( (int*)line, size ) ); // 左記で実行
}
inline int MinMaxLine( // 最小値最大値:ライン:long
long* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
){
return( MinMaxLine( (int*)line, size, maxData ) ); // 左記で実行
}
int MaxLineIndex( // 最大値時Index:ライン:long
long line[], // ライン情報Ptr
int size // ライン長
){
return( MaxLineIndex( (int*)line, size ) ); // 左記で実行
}
int MinLineIndex( // 最小値時Index:ライン:long
long line[], // ライン情報Ptr
int size // ライン長
){
return( MinLineIndex( (int*)line, size ) ); // 左記で実行
}
};
#endif // !defined(AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_)
(1)「#include」等、「#」構文を使用した先頭部分
#if !defined(AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_)
#define AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "ImageFuncDef.h" // 画像処理関数の定義部
#include "TypeArray.h" // TypeArray 記述用
#include "TypeSf.h" // TypeSf 記述用
#include "TypeCursor.h" // TypeCursor 記述用
「#if !defined(AFX_SUPPORT_H__・・・」は、WindowsXP
上でVS(ヴィジュアルスタジオ、以下「()」内は省略し
VSと表記)が自動生成した、「#include」を多重に動作
させて無駄に定義をコンパイラシステムに読み込ませ無い
工夫としての「#if !defined()・・・#endif」構文です!
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
と3行もVSが自動生成したものでVSに取っての都合と
思って居るから、
Windows以外のシステムをお使いの人は、無くても良い物と
思います!!
#include "ImageFuncDef.h" // 画像処理関数の定義部
#include "TypeArray.h" // TypeArray 記述用
#include "TypeSf.h" // TypeSf 記述用
#include "TypeCursor.h" // TypeCursor 記述用
この4つの「#include」でクラス【class Support{}】及び
このクラスを継承したクラスで必要な定義が記述された
定義ファイルをインクルードする事を示します!
(2)構造体「struct構文で定義」したデータ構造
この画像処理ライブラリで使用する構造体「struct構文で
定義したユーザー定義のデータ型」を定義して居る所です!
ここでは、TypeXY・TypeXYI・TypeXYF・TypeXYD・TypeAB・
TypeABY・TypeXYHV・TypeXYHVI・TypeXYHVIX・TypeXYRR・
TypeXYXY・TypeXYXYI・TypeXYHVIX・TypeXYRR・TypeXYXY2・
TypeCorr・TypeCorrIXと「Type」と先頭に付いて居るのは、
元々、昭和の時代にC言語で開発した名残で「型名」を
意味する為に区別の為に付けた物です!
主にこのライブラリで二次元座標を扱う系のデータ構造を
説明します!
(2-1)座標組用データ構造
ここではTypeXY・TypeXYI・TypeXYF・TypeXYDに付いて解説
して行きます!
主に直行座標を扱う為の構造体で画像処理ライブラリの
至る所で出て来る物です!
struct TypeXY { // 座標組用構造体の型宣言
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
}; // 型名「TypeXY」
struct TypeXYI { // 座標組用構造体の型宣言
int x; // x座標(32ビット)
int y; // y座標(32ビット)
}; // 型名「TypeXYI」
struct TypeXYF { // 座標組用構造体の型宣言
float x; // x座標部(単精度)
float y; // y座標部(単精度)
}; // 型名「TypeXYF」
struct TypeXYD { // 座標組用構造体の型宣言
double x; // x座標部(倍精度)
double y; // y座標部(倍精度)
}; // 型名「TypeXYF」
2次元直行座標≪X・Y≫を示す構造体で
「画像処理ライブラリ」では、絶対座標や相対座標を示す
為に使用します「C++」以降の進化した文法を御存知の
人には、
struct TypeXY { // 座標組用構造体の型宣言
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
}; // 型名「TypeXY」
struct TypeXY { // 座標組用構造体の型宣言
int x; // x座標(32ビット)
int y; // y座標(32ビット)
}; // 型名「TypeXY」
struct TypeXY { // 座標組用構造体の型宣言
float x; // x座標部(単精度)
float y; // y座標部(単精度)
}; // 型名「TypeXY」
struct TypeXY { // 座標組用構造体の型宣言
double x; // x座標部(倍精度)
double y; // y座標部(倍精度)
}; // 型名「TypeXY」
と構造体名「TypeXY」にして多重定義(オーバーロード)と
して定義した方が、使用する時に便利ではとお思いに成られ
ると思いますが、昭和の時代に「C言語」しか使え無かった
時に開発した名残です!
さらに説明すると、メンバー変数が「short型」の物が
オリジナルな名前にしてあるのは、一番使用頻度が多い⇒
±32767で画像サイズ「class TypeArrayで定義」に
即して居るからです!
他のメンバー変数が「int型・float型・double型」は
グラフィック処理などで大きな座標を扱う時に便利だから
ですが、使用頻度は多分少ない筈です!
そして名称「TypeXY」は2次元直交座標系の座標を扱う事を
意味する「XY」を付けた事は、理解出来ますね?!
更に名称「TypeXYI」で「I」を後置する事で中の
メンバー変数が「int型」を示し、名称「TypeXYF」で「F」
を後置する事で中のメンバー変数が「float型」を示し、
名称「TypeXYD」で「D」を後置する事で中のメンバー変数
が「double型」を示す事も理解して頂けると思います!
基本的な処理は、メンバー変数が「short型」で済むのです
が、精度が要求されるや大きなサイズの画像に対応する為の
特異な処理用に用意しましたが、用意だけして使わなかっ
可能性も有り、使っても使用頻度は少ない筈です!
そしてワザワザ「名称」を話題にするのは、
プログラミング言語は、勿論、コンピュータに指示を与え
る「文字で構成されたコード」ですが、人間が読める様に
分かり易く記載シナイと作成した本人も誤解するから
です!!
この画像処理ライブラリでのXY座標は、図示すると上の様なイメージに
成ります!
(2-2)座標範囲用データ構造
struct TypeAB { // 始終点X座標の構造体の型宣言
short a; // 終点X座標(16bit)
short b; // 終点X座標(16bit)
}; // 型名”TypeABY”
struct TypeABY { // 始終点X座標+Y座標の構造体の型宣言
short a; // 終点X座標(16bit)
short b; // 終点X座標(16bit)
short y; // Y座標(16bit)
}; // 型名”TypeABY”
座標の範囲を表す構造体「TypeAB」と「TypeABY」に付いて説明すると、
何方も2次元直交座標系の座標を扱いますが、主に「X座標」の範囲を操作
する為に用意した構造体です!「X座標」方向の処理の方が高速に処理可能
と考えて敢えて「Y座標」方向の処理用は用意しませんでした!
名称「TypeAB」の「AB」は、「A地点からB地点まで」を意味します!
更に構造体「TypeABY」は、メンバー変数「short y;」を追加した構造体で
2次元直交座標系の特定の「Y座標」での「X座標のA地点からB地点まで
」を扱って居ると理解して下さい!
このAB座標範囲は、図示すると上の様なイメージに成ります!
小文字の「a,b」でx座標上の「a」から「b」と範囲で
小文字の「y」でy座標示します!
(2-3)座標矩形範囲用データ構造
struct TypeXYHV { // 座標範囲用データ構造
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
short h; // x座標幅(16ビット)水平幅
short v; // y座標幅(16ビット)垂直幅
}; // 型名「TypeXYHV」
struct TypeXYHVI { // 座標範囲用データ構造
int x; // x座標部(32ビット)
int y; // y座標部(32ビット)
int h; // x座標幅(32ビット)水平幅
int v; // y座標幅(32ビット)垂直幅
}; // 型名「TypeXYHV」
struct TypeXYHVIX { // 座標範囲+Index用データ構造
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
short h; // x座標幅(16ビット)水平幅
short v; // y座標幅(16ビット)垂直幅
int i; // Index(32ビット)
}; // 型名「TypeXYHVIX」
struct TypeXYRR { // 座標+楕円径用データ構造
float x; // x座標(単精度)
float y; // y座標(単精度)
float r1; // 短径(単精度)
float r2; // 長径(単精度)
float a; // 角度(単精度)
}; // 型名「TypeXYRR」
ヒョットシテ矩形と聞いてピンと来なかった人に解説しますが、「四角形」の事です!
この画像処理ライブラリは、画像メモリ情報【
「解説クラスTypeArray」で解説します!】の矩形範囲として
(x,y)座標「左上」を始点としてサイズ(h、v)「水平サイズh」と「垂直サイズv」と画素単位で現した
「緑色」の部分を処理する機能に付いて記述した物です!
ここで水平・垂直と言うのは、元々開発された時にADS社のアナログカメラから、キャプチャーしたアナログの映像信号を画像メモリに固定した時の用語として当時のアナログ
TV情報の用語≪水平・垂直≫を元にしているからです!
ここで構造体「TypeXYHV」は、中のメンバー変数が
「short型」と16ビット幅の使用頻度の多い、言い換えれば、速度を考慮したデータ構造にしています!
それに対して構造体「TypeXYHVI」は、32ビット幅で
座標の表現出来る幅は増えるが、使用頻度が少ないと考えて「I」を名称に付けて区別して居ます!
更に構造体「TypeXYHVIX」は、メンバー変数に
「int i;// Index(32ビット)」と追加してこの「Index」は、各種図形計測などの座標範囲以外の必要なデータが格納されている場所を示すための配列へのインデックスを示すと考えて下さい!因みにメンバー変数「x,y,h,v」が
short型×4=64ビットデータで切れ目が良いので
多くのCPUチップでは、一括で処理され、次のint型で記載して居るのは、
この記載でもshort型記載でも速度的には同じに成る事が多いし、多くの場合、バウンダリ(メモリーを割り付ける区切り)を考えると半導体メモリーへの割り付けも同じ
サイズに成るからです!更にint型にした方が、型変換が起こらない為に依り早く動作する可能性も有るからです!
ここでXYHVと言う名称は、察しの素早い人には分かったと思いますが、念の為説明するとXYで座標を示し、
HVで水平・垂直のサイズを示します!
基本的に画素単位ですが、構造体「TypeXYHVI」のメンバー変数は「int型」ですのでモット違う用途にも使用出来る様に設計しています!
更に構造体「TypeXYHVIX」に特徴的な名称「IX」が付いて居ますが、このIXは「index」インデックスの省略形で良く使用しますので覚えて置いて下さい!
(2-4)始終点座標組
struct TypeXYXY { // 始終点座標組用データ構造
short x1; // 始点x座標部(16ビット)
short y1; // 始点y座標部(16ビット)
short x2; // 終点x座標部(16ビット)
short y2; // 終点y座標部(16ビット)
}; // 型名「TypeXYXY」
struct TypeXYXYI { // 始終点座標組用データ構造
int x1; // 始点x座標部(32ビット)
int y1; // 始点y座標部(32ビット)
int x2; // 終点x座標部(32ビット)
int y2; // 終点y座標部(32ビット)
}; // 型名「TypeXYXYI」
先ほど「(2-2)座標範囲用データ構造」で座標の範囲を表す構造体「TypeAB」と「TypeABY」を解説しましたが、
両方とも高速化処理の為にx座標方向のみ1次元的使用されます!
ここでの構造体「TypeXYXY」・構造体「TypeXYXYI」は、
2次元直行座標≪X・Y≫を始終点座標を扱う為に使用されます!
通常使用される構造体「TypeXYXY」とメンバー変数で扱う
座標が32ビットの構造体「TypeXYXYI」を用意して居ます!
主にグラフィック処理に使用しますが?!計測処理でも使用出来る様に設計しています!
(2-5)始終点座標組が二組
struct TypeXYXY2 { // 始終点座標2組用データ構造
short xa1; // 始点x座標部(16ビット):a組
short ya1; // 始点y座標部(16ビット):a組
short xa2; // 終点x座標部(16ビット):a組
short ya2; // 終点y座標部(16ビット):a組
short xb1; // 始点x座標部(16ビット):b組
short yb1; // 始点y座標部(16ビット):b組
short xb2; // 終点x座標部(16ビット):b組
short yb2; // 終点y座標部(16ビット):b組
}; // 型名「TypeXYXY2」
構造体「TypeXYXY2」は、座標の組み合わせを2組使用
出来る様に用意したものですが、用意しただけで本体の
画像処理ライブラリで使用して居る形跡は「Grep検索」で
無い事が判りました!で何かの役に立つと考えて用意した
筈です!オリジナルを作った時は昭和なので良く覚えて無い?!残念?!
(2-6)相関値+座標
struct TypeCorr { // 相関値+座標組用データ構造
double d; // 相関値
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
}; // 型名「TypeCorr」
struct TypeCorrIX { // 相関値+座標組+Index用データ構造
double d; // 相関値
short x; // x座標部(16ビット)
short y; // y座標部(16ビット)
int i; // Index(32ビット)
}; // 型名「TypeCorrIX」
構造体「TypeCorr」・構造体「TypeCorrIX」は、
相関値≪画像と画像を比較して近似率「どれだけ近いか」の指標を算出≫とその相関値の座標≪大きな画像内の小さな
矩形範囲始点(左上)≫を格納した構造体で主な用途として
顔認証等の画像比較様とか、物体追跡に使用される構造体です!
尚、構造体「TypeCorr」単に比較結果を格納する物で構造体「TypeCorrIX」は、付帯する情報とリンクする事が
構造体内に入って居る奴です!
(3)クラス「class構文で定義したデータ構造・メンバー関数(メソッド)」
ここで「C言語」は理解しているが「C++言語」は、使用経験の無い方に「クラス」とは、「構造体」にメンバー変数が入って居て型名の様な使い方が出来、ユーザーが構造体として定義したメンバー変数を「.」演算子や、「->」演算子で取り扱える事は存じているとして「クラス」は、簡単過ぎる
説明すれば、構造体のメンバーに関数(メソッド)が加わった物です!
この単純な仕組みで「C言語」では、物理的なファイル単位でローカル関数を扱っていた事「物理的な物としての都合」で無く「コンピュータ言語の文法としての論理的に
【何処其処のクラスに所属する関数】とローカル関数の管理
が出来る様に成った事で依り近代的なコンピュータ言語に成った事で更にオブジェクト指向プログラミングに対応する
エンカプセリング⇒カプセル化【encapsulation】と言う単語で示される機能を完結したカプセルの中に閉じ込める事で
カプセルの外からの悪影響を及ぼす副作用が起きないように
安全な方向に進化した」と考えて下さい!
ココから、一寸、難しく成るので注意⇒
私の勝手な解釈「オブジェクト指向プログラミングでソースコードの再利用と言う事を強調して居る?!
再利用が簡単に出来る為の機能の一つとして
出典は、新VisualC++6.0入門シニア編の
プロパティシートの構成に載って居る図
【CPropertySheet/CPropertyPageのクラス関係】で元の
【CObject】から機能の継承された【CCmdTarget】、
更に継承された【CWnd】と派生したクラスの関係を示して
居る例です!
この図のようにクラス継承関係を辿り、継承されたクラスは
元のクラスの機能が使用出来ると言う事で元のクラスの
ソースコードを弄る事無く既に動作検証が成された安全な
コードは、ソノママで新たな機能を継承された言わば子分の
クラスで実現出来る様にした」と私は解釈し、
この「クラスSupport」が、画像処理ライブラリの
大本のクラスとして、言い方はオカシイが、全体で使用出来
るグローバル大域定義に成って居る事を分かって下さい!
この画像処理ライブラリでのクラス関係は、上図の様に
「Support」の機能と定義をクラス
「CopyClear」が継承する事でCopyClearの中で「Support」で定義した物が使える事を示します!
そして、このクラス継承図で分かる様に、今回説明して居る
クラス「Support」が画像処理ライブラリの一番、
基礎的な機能を記述して居て中で定義している
「パブリックpublic」属性を付けた!
関数(メソッド)は、画像処理ライブラリ全体で使用可能に成るとの事です
//page
/***********************************************************************************/
/***** クラス Support ****/
/***** 画像処理ライブラリの基本的動作サポート用 ****/
/***********************************************************************************/
class Support
{
public:
Support(); // コンストラクタ
virtual ~Support(); // デストラクタ
void Trace0( char* str ); // トレーサー
int ltos( // 整数→文字列
long data, // 32Bitデータ
char *str ); // 変換後文字列
char* ltosx( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod, // 桁制御 1~11: 右詰めの桁数
// -1~-11: 左詰めの桁数
int sw=0 ); // 0(省略時):空白,1:「0」詰め
int ltox( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod ); // 変換モード
// 0→0を含めて8桁変換
// 1→0を消して左詰
// 2→0を消して右詰
void cvtStrHex( // Hex文字列→整数
char *str, // 文字列
char *buf, // 16進数値列
int sw = 0 ); // 0:ABCDEF,1:abcdef
char* stoi( // 文字列→整数
char* ptr, // 数字文字列
int& data ); // 結果整数
char* skipSpace( // 空白文字の読み飛ばし
char* ptr ); // 文字列
char* skipSpaceComma( // 空白文字と「,」の読み飛ばし
char* ptr ); // 文字列
char* getStringLineToInt( // 文字列行から整数値取り出し
char* line, // 文字列行
int& d ); // 整数値
char* getStringLineToReal( // 文字列行から実数値取り出し
char* line, // 文字列行
double& d ); // 実数値
char* append( // 文字列連結
char *str1, // つながられる文字列領域
const char *str2 ); // つなげる文字列
char* strTime( // 時刻データ→文字列
char *str, // 変換された文字列
int h, // 時間データ(0~24)
int mi, // 分データ(0~60)
int s, // 秒データ(0~60)
int sw=0, // 0(省略時):空白,1:「0」詰め
int mode=0 ); // -1 hhmmss
// 0(省略時) hh:mm:ss
char* strDate( // 日付データ→文字列
char *str, // 変換された文字列
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d, // 日データ(1~31)
int sw=0, // 0(省略時):空白,1:「0」詰め
int mode=0 ); // -1 yymmddwww
// 0(省略時) yy-mm-dd www
int calcWeek( // 曜日の計算:0→日曜
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d ); // 日データ(1~31)
int calcLenString( char* str ); // 文字列の有効長さ算出
double makeReal( // 実数値作成
int d1, // 仮数:大数部
int d2, // 仮数:小数部
int exp ); // 指数:-40 ~ +49
void dtos( // double→文字列
double data, // 浮動小数数値データ
char *str ); // 変換された文字列
void up_fill( // メモリ書き込み:BYTE
int s, // 書き込むデータ 0..255
BYTE *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_short( // メモリ書き込み:short
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_long( // メモリ書き込み:long
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_int( // メモリ書き込み:int
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_void( // メモリ書き込み:汎用
int s, // 書き込むデータ 0..255
void *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_inc_byte( // Mem書込:増分:BYTE
int s, // 書き込むデータ
BYTE *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_short( // Mem書込:増分:short
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_long( // Mem書込:増分:long
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_int( // Mem書込:増分:int
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_void( // Mem書込:増分:void
int s, // 書き込むデータ
void *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_movb( // 転送:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs( // 転送:short
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl( // 転送:long
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movi( // 転送:int
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_mova( // 転送:BYTE:大容量時
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movv( // 転送:汎用
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2s( // 転送:BYTE→short
BYTE *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2l( // 転送:BYTE→long
BYTE *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movc2s( // 転送:char→short
char *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movc2l( // 転送:char→long
char *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2b( // 転送:short→BYTE
short *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2c( // 転送:short→char
short *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2l( // 転送:short→long
short *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2b( // 転送:long→BYTE
long *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2c( // 転送:long→char
long *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2s( // 転送:long→short
long *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movu2l( // 転送:UWORD→long
UWORD *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb_inc( // 転送:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs_inc( // 転送:short
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl_inc( // 転送:long
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movi_inc( // 転送:int
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movv_inc( // 転送:汎用
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2s_inc( // 転送:BYTE→short
BYTE *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2l_inc( // 転送:BYTE→long
BYTE *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movc2s_inc( // 転送:char→short
char *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movc2l_inc( // 転送:char→long
char *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2b_inc( // 転送:short→BYTE
short *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2c_inc( // 転送:short→char
short *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2l_inc( // 転送:short→long
short *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2b_inc( // 転送:long→BYTE
long *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2c_inc( // 転送:long→char
long *ps, // 転送元へのポインタ
char *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2s_inc( // 転送:long→short
long *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
int up_sumb( // 合計:BYTE
BYTE *ps, // データへのポインタ
int l // 合計する数量
);
int up_sums( // 合計:short
short *ps, // データへのポインタ
int l // 合計する数量
);
int up_sumus( // 合計:UWORD
UWORD *ps, // データへのポインタ
int l // 合計する数量
);
int up_suml( // 合計:long
long *ps, // データへのポインタ
int l // 合計する数量
);
int up_sumi( // 合計:int
int *ps, // データへのポインタ
int l // 合計する数量
);
int up_mov_sumus( // 転送+合計:UWORD
UWORD *ps, // Sデータへのポインタ
UWORD *pd, // Dデータへのポインタ
int l // 合計する数量
);
void up_fill_float( // メモリ書き込み:float
double s, // 書き込むデータ 0..255
float *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_double( // メモリ書込み:double
double s, // 書き込むデータ 0..255
double *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
);
void up_fill_inc_float( // 書込:増分版:float
double s, // 書き込むデータ 0..255
float *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_fill_inc_double( // 書込:増分版:double
double s, // 書き込むデータ 0..255
double *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
);
void up_movf( // 転送:float
float *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd( // 転送:double
double *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2f( // 転送:BYTE→float
BYTE *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movb2d( // 転送:BYTE→double
BYTE *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2f( // 転送:short→float
short *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movs2d( // 転送:short→double
short *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2f( // 転送:long→float
long *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movl2d( // 転送:long→double
long *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2f( // 転送:double→float
double *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2d( // 転送:float→double
float *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2b( // 転送:float→BYTE
float *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2s( // 転送:float→short
float *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf2l( // 転送:float→long
float *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2b( // 転送:double→BYTE
double *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2s( // 転送:double→short
double *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movd2l( // 転送:double→long
double *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
);
void up_movf_inc( // 増分版転送:float
float *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd_inc( // 増分版転送:double
double *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2f_inc( // 増分版転送:BYTE→float
BYTE *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movb2d_inc( // 増分版転送:BYTE→double
BYTE *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2f_inc( // 増分版転送:short→float
short *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movs2d_inc( // 増分版転送:short→double
short *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2f_inc( // 増分版転送:long→float
long *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movl2d_inc( // 増分版転送:long→double
long *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2f_inc( // 増分版転送:double→float
double *ps, // 転送元へのポインタ
float *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2d_inc( // 増分版転送:float→double
float *ps, // 転送元へのポインタ
double *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2b_inc( // 増分版転送:float→BYTE
float *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2s_inc( // 増分版転送:float→short
float *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movf2l_inc( // 増分版転送:float→long
float *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2b_inc( // 増分版転送:double→BYTE
double *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2s_inc( // 増分版転送:double→short
double *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
void up_movd2l_inc( // 増分版転送:double→long
double *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l, // 書き込む大きさ
int incS, // 増分:転送元
int incD // 増分:転送先
);
private:
void itos_1( // 整数→文字列
long data, // 10000未満のデータ
char *str // 変換後文字列
);
void itos_2( // 整数→文字列
long data, // 32Bitデータ
char *str // 変換後文字列
);
void up0movb( // 転送:基本:BYTE
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
);
public: // 最小値/最大値
int MinLine( // 最小値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size // ライン長
);
int MinLine( // 最小値:ライン:short
short* line, // ライン情報Ptr
int size // ライン長
);
int MinLine( // 最小値:ライン:int
int* line, // ライン情報Ptr
int size // ライン長
);
DWORD MinLine( // 最小値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size // ライン長
);
DWORD MinLine( // 最小値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size // ライン長
);
double MinLine( // 最小値:ライン:float
float* line, // ライン情報Ptr
int size // ライン長
);
double MinLine( // 最小値:ライン:double
double* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:short
short* line, // ライン情報Ptr
int size // ライン長
);
int MaxLine( // 最大値:ライン:int
int* line, // ライン情報Ptr
int size // ライン長
);
DWORD MaxLine( // 最大値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size // ライン長
);
DWORD MaxLine( // 最大値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size // ライン長
);
double MaxLine( // 最大値:ライン:float
float* line, // ライン情報Ptr
int size // ライン長
);
double MaxLine( // 最大値:ライン:double
double* line, // ライン情報Ptr
int size // ライン長
);
int MinMaxLine( // 最小値最大値:ライン:BYTE
BYTE* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
int MinMaxLine( // 最小値最大値:ライン:short
short* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
int MinMaxLine( // 最小値最大値:ライン:int
int* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
);
DWORD MinMaxLine( // 最小値最大値:ライン:UWORD
UWORD* line, // ライン情報Ptr
int size, // ライン長
DWORD& maxData // 返値:最大値
);
DWORD MinMaxLine( // 最小値最大値:ライン:DWORD
DWORD* line, // ライン情報Ptr
int size, // ライン長
DWORD& maxData // 返値:最大値
);
double MinMaxLine( // 最小値最大値:ライン:float
float* line, // ライン情報Ptr
int size, // ライン長
double& maxData // 返値:最大値
);
double MinMaxLine( // 最小値最大値:ライン:double
double* line, // ライン情報Ptr
int size, // ライン長
double& maxData // 返値:最大値
);
int MaxLineIndex( // 最大値時Index:ライン:BYTE
BYTE line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:short
short line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:int
int line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:UWORD
UWORD line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:DWORD
DWORD line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:単精度
float line[], // ライン情報Ptr
int size ); // ライン長
int MaxLineIndex( // 最大値時Index:ライン:倍精度
double line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:BYTE
BYTE line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:short
short line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:int
int line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:UWORD
UWORD line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:DWORD
DWORD line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:単精度
float line[], // ライン情報Ptr
int size ); // ライン長
int MinLineIndex( // 最小値時Index:ライン:倍精度
double line[], // ライン情報Ptr
int size ); // ライン長
/********************************************************************************************/
/***** inline関数群 *****/
/********************************************************************************************/
public: // inline関数
inline int MaxLine( // 最大値:ライン:long
long* line, // ライン情報Ptr
int size // ライン長
){
return( MaxLine( (int*)line, size ) ); // 左記で実行
}
inline int MinLine( // 最小値:ライン:long
long* line, // ライン情報Ptr
int size // ライン長
){
return( MinLine( (int*)line, size ) ); // 左記で実行
}
inline int MinMaxLine( // 最小値最大値:ライン:long
long* line, // ライン情報Ptr
int size, // ライン長
int& maxData // 返値:最大値
){
return( MinMaxLine( (int*)line, size, maxData ) ); // 左記で実行
}
int MaxLineIndex( // 最大値時Index:ライン:long
long line[], // ライン情報Ptr
int size // ライン長
){
return( MaxLineIndex( (int*)line, size ) ); // 左記で実行
}
int MinLineIndex( // 最小値時Index:ライン:long
long line[], // ライン情報Ptr
int size // ライン長
){
return( MinLineIndex( (int*)line, size ) ); // 左記で実行
}
};
#endif // !defined(AFX_SUPPORT_H__50DD6280_161A_4CC2_8F4B_E043810F5F3F__INCLUDED_)
(3-1)クラス「class Support{・・中身・・};と#endif」
クラス「Support」は、上記の様に
「class Support{」から始まり「};」で定義(宣言)を終え
ます!
最後の「#endif」は、ファイルの終わりで「#include」の
多重読み込みを防ぐ仕掛けでWindowsXP上のビジュア
ルスタジオ6.0で自動生成した物です!
C言語のみ使用経験の有る方でも
「struct 名称{メンバー変数};」と酷似した記述と分かる
と思います!今回の「class Support{」には、メンバー変数
は見て頂ければ分かる様に定義して居ませんが、代わりに
関数(メソッド)が、宣言定義されている事が判るでしょ
う?!この画像処理ライブラリの他のクラスでは、
メンバー変数も定義して居ます!
勿論、関数(メソッド)がメンバーとして定義出来るのが、
C++でクラスと言う構造体を強化するアイデアで簡単に
記述能力をアップ&安全性もアップした事ですが、ここで
class Support
{
public:
Support(); // コンストラクタ
virtual ~Support(); // デストラクタ
void Trace0( char* str ); // トレーサー
と「class Support{public:」と
「メンバー関数(メソッド)」を記述する直前に
キーワード「public」+「:」記述が有る事に気付くで
しょう?!
このキーワード「public」は、以降の定義にパブリック属性
≪このクラスの外からも定義を参照(関数の場合は動作させ
る事)出来る≫を付ける事を意味します!同じ様に
キーワード「private」+「:」記述が有る事にも気づいて頂
けると思いますが、この関数の定義がプライベート属性≪
このクラスSupportの中だけで有効≫の関数として
「itos_1();とitos_2();及びup0movb();」が内部で使用する
だけの関数としてクラスの外から使用出来無いようにして
います!
ここでは、余り、グローバル関数として使う意味が無いと
判断したからですが、ソースコードで提供して居ますので
自身の責任で「public」にしても結構ですが、
改造した物に対して私(作者)は責任を持ちません!
(3-2)コンストラクタ/デストラクタ
「Support(); // コンストラクタ」とコメントに
「コンストラクタ」と説明が有る!
クラス名「Support」と同じ名称の関数は、コンストラクタ
と呼ばれ、このクラスが定義された時に実体で動作すると
記述すると?!
多分、分かり難いと思いますので、違う言い回し≪
クラスを使用した直後にそのクラスの初期化等の試用準備を
行う関数です≫、そしてこの「class Support」では
「Support(); 」は存在しますが、中身は空です!
取り敢えずダミーに用意しただけです!
同じく「virtual ~Support();// デストラクタ」と先頭に
「~」とC言語でビット毎の論理否定(1の補数)を意味
する記号を前置したクラス名「Support」と同じ名称の関数
は、デストラクタ=破壊者的な意味でクラスの使用を終えた
時の後始末を行う関数です!
矢張り、この「class Support」では「~Support(); 」は
存在しますが、中身は空です!取り敢えずダミーに用意
しただけです!
(3-3)トレーサー
void Trace0( char* str ); // トレーサー
昭和の時代にC言語で作成した裸(OSの無い)のCPUで
のプログラミングのデバッグ用に作成した関数の名残です!
現在でプログラミングの開発には色々デバッグ機能が、
使用出来るので残して有るだけで本体は、
void Support::Trace0(
char* str
){
FILE* fp;
fp = fopen( "TS.txt", "a" );
fprintf( fp, "%s", str );
fclose( fp );
}
とファイル「TS.txt」に実引数「char* str」の文字列を
書き溜めると言う操作が出来る様にして居ます!
序にC言語でプログラミング経験は有るが、C++には
不慣れと言う方に解説すると、クラスで定義した関数は、
別のファイルに存在して居ても
クラス名::関数名(仮引数){関数本体のソースコード}と
記載出来ます!
ここでは、「void Support::Trace0(char* str){中身}」と
記載して居ますのでC言語でプログラミング経験は有る人は
理解出来るでしょう!
(3-4)「整数→文字列」関数群
int ltos( // 整数→文字列
long data, // 32Bitデータ
char *str ); // 変換後文字列
char* ltosx( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod, // 桁制御 1~11: 右詰めの桁数
// -1~-11: 左詰めの桁数
int sw=0 ); // 0(省略時):空白,1:「0」詰め
int ltox( // 整数→Hex文字列
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod ); // 変換モード
// 0→0を含めて8桁変換
// 1→0を消して左詰
// 2→0を消して右詰
ここでは、関数「 int ltos(long data,char str);」・
関数「char ltosx(long data,char*str,int mod,int sw=0
); 」・
関数「ltox(long data,char *str,int mod );」に関して
説明して行きます
(3-4-1)関数「 int ltos(long data,char *str);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
int Support::ltos(
long data, // 32ビット数値データ
char* str // 変換された文字列
){
char temp_str[20]; // 単純変換用文字列
char* str1; // 上記へのポインタ
int temp; // 元文字列ptr:長算出用
temp = (int)str; // 文字列PtrAdr値を保存
if( data < 0 ){ // 数値が負のデータなら
if( data == 0x80000000 ){ // 負の値の最大値の時
str1 = "-2147483648"; // それを数字列で表し
while( ( *str++ = *str1++ ) != 0 ){ // 文字列としてセット
; //
} //
return( 12 ); // 文字数12(含-記号)
} // を返す
data = - data; // 正のデータにし
*str++ = '-'; // -を文字列先頭に付け
} // る
if( data < 10 ){ // データが10未満なら
data += '0'; // 1桁を10進変換し
*str++ = (char)data; // セットする
*str++ = '\0'; // 終了文字をセット
}else{ // データが10以上なら
str1 = temp_str; // 単純文字列ptrを求める
if( data < 10000 ){ // データが10000未満なら
itos_1( data, str1 ); // 10000未満の変換処理
}else{ // 10000以上なら
itos_2( data, str1 ); // 2147483647以下の変換
} //
while( *str1 == '0' ){ // 文字「0」を
str1++; // スキップする
} //
while( ( *str++ = *str1++ ) != 0 ){ // 変換文字列へ単純文字
; // 列を転送する
} //
} //
return( (int)str - temp ); // 文字列の長さを返す
}
勿論、関数「ltos();」・関数「ltosx(); 」・
関数「ltox();」直接では無くこれのサブルーチンとして
使用されているのでコレの説明から始めます!
「Support::itos_1(」と「Support::」を関数名に前置する
事で別個のファイルとして記述を格納できます!
void itos_1( // 整数→文字列
long data, // 10000未満のデータ
char *str // 変換後文字列
);
「void Support::itos_1(」で関数本体の記述開始を示しま
す!
「long data,」で第1引数として「コメントで 10000未満の
データ」とあるように整数値として実引数が、10000未満と
制約があるので「public」で無く「private」と外から見え
無い様にしました!
void Support::itos_1(
long data, // 10000未満のデータ
char* str // 変換後文字列
){
static const long cmp_tbl[] = { // 比較表(4桁分)
8000, 800, 80, 8 //
}; //
const long* ptr_tbl; // 比較DataTablePtr
const long* pend; // 終点Ptr
long cmp_data; // 比較データ
int d; // 一文字分データ
ptr_tbl = cmp_tbl; // 4~1桁用比較Tableの
pend = &cmp_tbl[3]; // 始終点をセットし
while( ptr_tbl <= pend ){ // 4~1桁まで進行
d = '0'; // 文字0をセット
cmp_data = *ptr_tbl++; // 比較データを取り出し
if( data >= cmp_data ){ // 任意桁の 9,8成分を
data -= cmp_data; // 比較し変換処理
d += 8; //
} //
cmp_data >>= 1; //
if( data >= cmp_data ){ // 任意桁の 7,6,5,
data -= cmp_data; // 4 成分を比較し変換
d += 4; // 処理
} //
cmp_data >>= 1; //
if( data >= cmp_data ){ // 任意桁の 3,2成分を
data -= cmp_data; // 比較し変換処理
d += 2; //
} //
cmp_data >>= 1; //
if( data >= cmp_data ){ // 任意桁の 1,0成分を
data -= cmp_data; // 比較し変換処理
d += 1; //
} //
*str++ = d; // 任意桁を格納
} //
*str = '\0'; // 終了文字をセット
} //
特徴的なのは、
「static const long cmp_tbl[]={8000,800,80,8};」と
「cmp_tbl[]」と「変数」が寧ろ「定数」と
ROM(リード・オンリー・メモリ)的に扱って居る事で
す!
真面なコンパイラシステムならば、ドンなCPUでも
「インストラクションキャッシュに格納される領域」に割り
当てる筈です⇒詰り、関数「itos_1()」をアクセスした時点
の一回だけCPU外への外部メモリー読み出しが行われる
だけでアクセス回数が減ります⇒高速化する事は、理解して
頂けるでしょう!
因みに「const」に関しては、コンパイラシステムに依って
「警告ワーニング」が型変換等に頻発する事が有りますので
使用される処理系に詳しい人に聴いて下さい!
この関数「itos_1()」の動作アルゴリズムは、C言語の
プログラムを作成した事が有る方には、理解出来ると思い
ますが、念の為に解説します!
ptr_tbl=cmp_tbl;pend=&cmp_tbl[3];
while( ptr_tbl <= pend ){・・中身・・・}*str='\0';
がこの関数の動作する本体です!
初期化「ptr_tbl=cmp_tbl;pend=&cmp_tbl[3];」と
ループ「while( ptr_tbl <= pend ){・・中身・・}」及び
後始末「*str='\0';」で出来ている事は理解できるで
しょう!
初期化はポインタをテーブル「cmp_tbl[]」の先頭セットと
終了点目印のセットでループ制御
「while( ptr_tbl <= pend )」の範囲を設定する事は、理解
出来るでしょう!
だから、ループはテーブルの先頭「8000」から終点「8」
迄を仮引数「data」と比較し、「data」の方が以上値(
大きいか同じ値)なれば引く事と一文字分データ「d」に
文字データ変換として【8・4・2・1を
ifブロック「if(data>=cmp_data){data-=cmp_data;
d+=8;} 」】て順次比較して行き、引き算し、文字変換用の
文字にコードに加算して行きます?!
注意して欲しいのは、「cmp_data = *ptr_tbl++;」の構文は
テーブル「cmp_tbl[]」から値を取り出し、
「cmp_data >>= 1;」の構文は、取り出した比較データを
例えば【終点の「8」なら「4」に成ると半分の数値に成る】
事は理解して頂けますね!
理解力の有る方なら、これで数値「8000」から比較するので
変換可能な数値が「10000」以内だと判るでしょう!
直ぐに理解出来ないならば、適当な数値を「data」に設定
したとして机上でプログラムを実行して頂ければ、
分かると思います!
最後の後始末「*str='\0'」は、文字列の最後に終了文字と
してコード「0」を付けると言う「C言語プログラマー」に
は、お馴染みの処理です!
この関数は、数値も十進数文字列に変換する基本部だと
理解して頂けると思います!
この関数は、上記動作の為に変換可能な数値が「10000」
以内なので「private」として直接には使用出来ないように
しています!
この関数を、:ltos()のサブルーチンとして使用して
「ltos()」が「public」と成っています!
int Support::ltos(
long data, // 32ビット数値データ
char* str // 変換された文字列
){
char temp_str[20]; // 単純変換用文字列
char* str1; // 上記へのポインタ
int temp; // 元文字列ptr:長算出用
temp = (int)str; // 文字列PtrAdr値を保存
if( data < 0 ){ // 数値が負のデータなら
if( data == 0x80000000 ){ // 負の値の最大値の時
str1 = "-2147483648"; // それを数字列で表し
while( ( *str++ = *str1++ ) != 0 ){ // 文字列としてセット
; //
} //
return( 12 ); // 文字数12(含-記号)
} // を返す
data = - data; // 正のデータにし
*str++ = '-'; // -を文字列先頭に付け
} // る
if( data < 10 ){ // データが10未満なら
data += '0'; // 1桁を10進変換し
*str++ = (char)data; // セットする
*str++ = '\0'; // 終了文字をセット
}else{ // データが10以上なら
str1 = temp_str; // 単純文字列ptrを求める
if( data < 10000 ){ // データが10000未満なら
itos_1( data, str1 ); // 10000未満の変換処理
}else{ // 10000以上なら
itos_2( data, str1 ); // 2147483647以下の変換
} //
while( *str1 == '0' ){ // 文字「0」を
str1++; // スキップする
} //
while( ( *str++ = *str1++ ) != 0 ){ // 変換文字列へ単純文字
; // 列を転送する
} //
} //
return( (int)str - temp ); // 文字列の長さを返す
}
上記の様に関数「itos_1()」をサブルーチンとして使用して
居ます!
更に関数「itos_2()」もサブルーチンとして使用して居ます
が、基本的には、関数「itos_1()」を理解して居た方なら
同じ様な処理なので理解出来る筈です!
ソースコードを読んでね!!
(3-4-1-A)関数「 int ltos(・・・);」【仮引数】説明
int Support::ltos(
long data, // 32ビット数値データ
char* str // 変換された文字列
){
int Support::ltos(long data,charstr){
C言語系経験者ならば、理解して居る通り「long data」は
右側のコメント
「32ビット数値データ」と記載して居る様に型名「long」
の全データが使える範囲の数値データです!
「charstr」は、コメント「変換された文字列」と有る様に
この関数の結果を意味します!
ポインタに成って居るのは、ポインタで示される実引数が、
文字列を格納する領域「メモリー空間」が存在する事が前提
です!
と記載したと言う事は、必要なサイズ記載する必要が有りま
すが、この関数は、「ltos」と言う名前≪
Long・To・String≫⇒≪32ビット符号付き
数値データを文字列(半角)へ変換≫を意味する通り
文字列「char*str」に必要なサイズは12バイトです
「符号(-)と十進法数字10桁文字」最後に終了文字
「御存知の様にC言語世界では【\0】、数値データとして
0が入る」が必要なので12バイト必要な事は推測出来たと
思います!
因みに、最大桁数の数は「-2147483648」です!
コレが11文字ですから、終了文字を加えると12バイトで
す!
ココまでヒツコイとC言語類を使用されている人には鬱陶し
いと思うがC言語系の初心者向けの心算で記載して居るので
す!
今日の講義はここまで疲れた!!
説明して行こうと思ったが、私の気力が萎えた?!
今日の講義は、ここまで、読者様も若くて新規の他人の
ソースコードにも対応出来る高性能な人が殆んどと思います
が、爺(作者の私)が疲れたのだ!
2024年2月4継続講義
(3-4-1-B)関数「 int ltos(・・・);」【ローカル変数】説明
){
char temp_str[20]; // 単純変換用文字列
char* str1; // 上記へのポインタ
int temp; // 元文字列ptr:長算出用
「char temp_str[20];」は、この関数「ltos()」内部で
作業用に一時的に用意した文字列操作用で具体的には、
内部で使用するサブルーチンとしての「itos_1()」・
「itos_2()」で一旦、部分的に十進数字列を作成する為に
使用します!
「char* str1;」は、文字列の操作用のポインタ変数です!
コレをこの関数では使用して行くのが主な操作です!
「int temp」は、このライブラリの記述が昭和の時代に
C言語で記載を始めた名残として「ポインター」と
「int型」がCPUでの演算は同じと考えて機械アドレスの
演算に使用してアドレスの差分を数値演算で行う為の物です
ので一寸、だけ今(21世紀)の方法とは違う事を理解して
下さい?!
でも
この方が、CPU機械アドレスを素直に利用して居るので
高速に動作します
(3-4-1-C)関数「 int ltos(・・・);」アルゴリズム説明
temp = (int)str; // 文字列PtrAdr値を保存
if( data < 0 ){ // 数値が負のデータなら
if( data == 0x80000000 ){ // 負の値の最大値の時
str1 = "-2147483648"; // それを数字列で表し
while( ( *str++ = *str1++ ) != 0 ){ // 文字列としてセット
; //
} //
return( 12 ); // 文字数12(含-記号)
} // を返す
data = - data; // 正のデータにし
*str++ = '-'; // -を文字列先頭に付け
} // る
if( data < 10 ){ // データが10未満なら
data += '0'; // 1桁を10進変換し
*str++ = (char)data; // セットする
*str++ = '\0'; // 終了文字をセット
}else{ // データが10以上なら
str1 = temp_str; // 単純文字列ptrを求める
if( data < 10000 ){ // データが10000未満なら
itos_1( data, str1 ); // 10000未満の変換処理
}else{ // 10000以上なら
itos_2( data, str1 ); // 2147483647以下の変換
} //
while( *str1 == '0' ){ // 文字「0」を
str1++; // スキップする
} //
while( ( *str++ = *str1++ ) != 0 ){ // 変換文字列へ単純文字
; // 列を転送する
} //
} //
return( (int)str - temp ); // 文字列の長さを返す
}
「 temp=(int)str;」機械アドレスを一時保存、
「if(data<0){・・中身・・}」勿論、仮引数「long data」
が負の数の場合の処理です!
では、その中身「if(data==0x80000000){」は、例外処理と
して仮引数「long data」が「0x80000000」詰り、
32ビット2の補数表現で負の最大値の処理です!
str1 = "-2147483648"; // それを数字列で表し
「str1="-2147483648";」で操作用のポインタ変数に
文字列"-2147483648"先頭をセットし、
「while((*str++=str1++)!= 0){;}」で仮引数「char*str」に
結果として文字列"-2147483648"を格納する事を示しま
す!
「while(条件)」の条件内で一文字ヅツのコピーと終了文字
の判定を行って居る事はC言語を使われた事が有る読者様
なら分かりますね!
「return( 12 );」リターン文で関数の結果として12⇒
文字数(終了文字も含む)を返し関数を終了します!
で、「リターン文」で終わるので「else」が不要な事に
注意して下さい!
だから、例外処理以外は
data = - data; // 正のデータにし
*str++ = '-'; // -を文字列先頭に付け
と一旦、仮引数「long data」を「-」演算子で2の補数表現
で正の数に変換し「*str++ = '-';」結果文字列の先頭に
文字コード「'-'」を付け、正の値として引き続く処理を
続けます!
次は、
if( data < 10 ){ // データが10未満なら
data += '0'; // 1桁を10進変換し
*str++ = (char)data; // セットする
*str++ = '\0'; // 終了文字をセット
}else{ // データが10以上なら
str1 = temp_str; // 単純文字列ptrを求める
if( data < 10000 ){ // データが10000未満なら
itos_1( data, str1 ); // 10000未満の変換処理
}else{ // 10000以上なら
itos_2( data, str1 ); // 2147483647以下の変換
} //
while( *str1 == '0' ){ // 文字「0」を
str1++; // スキップする
} //
while( ( *str++ = *str1++ ) != 0 ){ // 変換文字列へ単純文字
; // 列を転送する
} //
} //
「if(data<10){中身1}else{中身2}」で中身1は 、
{ // データが10未満なら
data += '0'; // 1桁を10進変換し
*str++ = (char)data; // セットする
*str++ = '\0'; // 終了文字をセット
}
10未満、詰り、一桁の変換処理で文字コード「'0'」と
データの値(0~9)までを加算する事で文字'0'から
文字'9'に変換される事は理解出来ますね!
それを「str++ = (char)data;」と文字表現にキャストし
仮引数「charstr」に結果として先頭文字列として格納し
格納ポインタも「++」で格納場所移動、
最後に「*str++ = '\0';」終了文字を格納する事は、
理解出来ますね!
では、中身2は、
}else{ // データが10以上なら
str1 = temp_str; // 単純文字列ptrを求める
if( data < 10000 ){ // データが10000未満なら
itos_1( data, str1 ); // 10000未満の変換処理
}else{ // 10000以上なら
itos_2( data, str1 ); // 2147483647以下の変換
} //
while( *str1 == '0' ){ // 文字「0」を
str1++; // スキップする
} //
while( ( *str++ = *str1++ ) != 0 ){ // 変換文字列へ単純文字
; // 列を転送する
} //
} //
「str1 = temp_str;」ローカルな処理バッファ
「temp_str」の先頭ポインタをセットし、
「if(data<10000){itos_1(data,str1);}else{
itos_2(data,str1);}」と
10000未満の値ならば、「itos_1(data,str1);」
10000以上の値ならば、「itos_2(data,str1);」
で処理して居る事は、理解して居ますね!
既に関数「itos_1()」は解説して居ます!
関数「itos_2()」は、それを元に練習問題として読み解いて
下さい!
無理だったと言われる場合は、コメント欄にエクスキューズ
して下さい何とかします!
「while((*str++=str1++)!=0){;}」で仮引数「charstr」
に結果としてサブルーチンとしての「itos_1()」・
「itos_2()」で得られた十進数字列をコピーします!
ここで何故「itos_1()」・「itos_2()」と2種類用意したか
は少しでも高速化したい為に「itos_1()」を用意した事は
「itos_2()」を解読して頂ければ分かる筈です!
(3-4-2)関数「 int ltosx(long data,char*str,int mod,int sw);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
char* Support::ltosx(
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod, // 桁制御 1~11: 右詰めの桁数
// -1~-11: 左詰めの桁数
int sw // 0(省略時):空白,1:「0」詰め
){
char temp_str[20]; // 単純左詰め用文字列
char *str1; // 上記へのポインタ
char *end_str; // 文字終点へのポインタ
int absmod; // 桁数の絶対値(1~11)
int c; // 文字データ
int i; // インデックス
if( ( mod < -11 && mod > 11 ) || mod == 0 ){ // 桁制御引数が、-11~11の範囲外及び0なら
return( (char*)-1 ); // 不正として-1を返す
} //
absmod = mod > 0 ? mod: -mod; // 桁数を求める
end_str = str; // 最終PtrをSETする前準備
if( sw == 0 ){ // 空白指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分空白を作る
*end_str++ = ' '; //
} //
}else{ // 「0」指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分「0」を作る
*end_str++ = '0'; //
} //
} //
*end_str = '\0'; // 終了文字をセット
str1 = temp_str; // 文字の先頭にセットする
*str1++ = '\0'; // 番兵(終了文字)をセット
ltos( data, str1 ); // 左詰めで数字列を作成する
if( mod >= 1 ){ // 右詰めにするなら
while( *str1 != '\0' ){ // 数字列の最終ポインタを
str1++; // 捜す
} //
str = end_str; // 最終ポインタの値をセット
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *--str1 ) == 0 ){ // 後ろからの文字が終了(番兵)
break; // なら、抜ける
} //
*--str = c; // 後ろから文字をセットする
} //
}else{ // 左詰めにするには
str1 = &temp_str[ 1 ]; // 文字の先頭にセットする
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *str1++ ) == 0 ){ // 前からの文字が終了したら
break; // 抜ける
} //
*str++ = c; // 前から文字をセットする
} //
} //
return( end_str ); // 最終ポインタを返す
}
(3-4-2-A)関数「int ltosx(・・・);」の【仮引数】説明
char* Support::ltosx(
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod, // 桁制御 1~11: 右詰めの桁数
// -1~-11: 左詰めの桁数
int sw // 0(省略時):空白,1:「0」詰め
){
仮引数「long data」・「char *str」は、
関数「int ltos(・・・);」と同じく元の数値データと結果としての文字列データです!
これ以降の仮引数「int mod」・「int sw」で細かい設定が
出来る事が出来ます!
仮引数「int mod」は、コメントで「桁制御 1~11: 右詰め
の桁数-1~-11:左詰めの桁数」と-11・・-1及び
1・・11と桁数を示す値を指定する≪
桁数なので0は無い、そして±で右詰め(+)、
左詰め(-)の桁数を指定できる≫
仮引数「int sw」で指定した桁数の文字列を作成する時、
桁の数に満たない
部分を空白にするか、「0」を入れるか、例えば、
「mod=5,sw=0」で「data=123」⇒「" 123"」に成り、
「mod=5,sw=1」で「"00123"」と成ります!
(3-4-2-B)関数「int ltosx(・・・);」の【ローカル変数】説明
){
char temp_str[20]; // 単純左詰め用文字列
char *str1; // 上記へのポインタ
char *end_str; // 文字終点へのポインタ
int absmod; // 桁数の絶対値(1~11)
int c; // 文字データ
int i; // インデックス
「char temp_str[20];」は、この関数「ltosx()」内部で
作業用に一時的に用意した文字列操作用で具体的には、
内部で使用するサブルーチンとしての「itos()」で
十進法数字文字列に変換した文字列を格納します!
「charstr1;」・「charend_str;」文字列を操作する
ポインタ変数でコレをポインタ操作する事が、
この関数の基本操作です!
「int absmod;」仮引数「int mod」が、±が付いて居るが
作成する文字列の桁数で有る事は、説明しましたが、
ココでは、±で無く絶対値の桁数に変換した数値変数です!
「int c;」文字コードの一時的な置き場です!
RISC系のCPUでは一旦、単純変数にデータを入れる
と単純変数(CPU内レジスターに割り当てる筈)は、
効率良く動作する筈で高速動作する可能性が高い!
「int i;」桁数など数を数えるので用意した変数
(3-4-2-C)関数「int ltosx(・・・);」の【アルゴリズム】説明
if( ( mod < -11 && mod > 11 ) || mod == 0 ){ // 桁制御引数が、-11~11の範囲外及び0なら
return( (char*)-1 ); // 不正として-1を返す
} //
absmod = mod > 0 ? mod: -mod; // 桁数を求める
end_str = str; // 最終PtrをSETする前準備
if( sw == 0 ){ // 空白指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分空白を作る
*end_str++ = ' '; //
} //
}else{ // 「0」指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分「0」を作る
*end_str++ = '0'; //
} //
} //
*end_str = '\0'; // 終了文字をセット
str1 = temp_str; // 文字の先頭にセットする
*str1++ = '\0'; // 番兵(終了文字)をセット
ltos( data, str1 ); // 左詰めで数字列を作成する
if( mod >= 1 ){ // 右詰めにするなら
while( *str1 != '\0' ){ // 数字列の最終ポインタを
str1++; // 捜す
} //
str = end_str; // 最終ポインタの値をセット
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *--str1 ) == 0 ){ // 後ろからの文字が終了(番兵)
break; // なら、抜ける
} //
*--str = c; // 後ろから文字をセットする
} //
}else{ // 左詰めにするには
str1 = &temp_str[ 1 ]; // 文字の先頭にセットする
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *str1++ ) == 0 ){ // 前からの文字が終了したら
break; // 抜ける
} //
*str++ = c; // 前から文字をセットする
} //
} //
return( end_str ); // 最終ポインタを返す
}
「if((mod<・・・return((char*)-1);}」は、
仮引数「int mod」のエラー検査として不正と判断したら
エラーを示す「(char*)-1」を返す!
※注意※普通のOS管理上「-1」の値がポインタとして還
る訳は無いのでエラーコードとして使用したが、
コンパイラシステムに依ってはコンパイル時にワーニングや
酷い場合は文法エラーと見なすかも知れません?!
その場合は、「(char*)0」に置き換えて下さい!
「absmod=mod>0?mod:-mod;」は、C言語ラシイ記法で
変数「absmod」に仮引数「int mod」の絶対値を格納する
事は理解出来ますね?!
余り、この3項演算子を教えないC言語系の学習塾(
専門学校・大学・高専・工業高校及び簡易コンピュータ言語
教習所)もコンピュータアルゴリズムの本筋で無いので教え
ない所も有ると思いますが、
「#define」で定義済みの置換関数で無くても直接使う人も
居るのです!
「end_str = str;」は、右端に記載のコメント
「// 最終PtrをSETする前準備」と有る様に引き続く
if( sw == 0 ){ // 空白指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分空白を作る
*end_str++ = ' '; //
} //
}else{ // 「0」指定なら
for( i = 1; i <= absmod; i++ ){ // 桁数分「0」を作る
*end_str++ = '0'; //
} //
} //
*end_str = '\0'; // 終了文字をセット
「if(sw==0){・・この条件成立時実行部・・}else{・・
条件不成立時実行部・・}*end_str = '\0';」と「sw==0」
で成立、「sw!=0」で不成立、はお判りと思います!先ず、
条件成立時「for(i=1;i<=absmod;i++){*end_str++=' ';}」
で指定した桁数文字コード「' '」をポインタ
変数「end_str」でポインティングする場所『勿論、
「end_str = str;」と仮引数「char *str」で示した場所から始め』ポインタの増加方向に書き込んで行くのは、理解
出来たと思います!
同じ様に条件不成立時「for(i=1;i<=absmod;i++){
*end_str++='0';}」では、
文字コード「'0'」が書き込んで行くのは理解出来たと思い
ます!
そして「*end_str = '\0';」で終了文字が書き加えられた
事も理解出来る筈です!
これは、昭和の私が、このライブラリの前身を記載し始めた
時の事です!今なら、
if( sw == 0 ){
c = ' ';
}else{
c = '0';
}
for( i = 1; i <= absmod; i++ ){
*end_str++ = c;
}
*end_str = '\0';
と記載した方が分かり易いかな!
「str1=temp_str;」は、ローカルな文字列変換領域の先頭を
操作ポインタにセット
str1 = temp_str; // 文字の先頭にセットする
*str1++ = '\0'; // 番兵(終了文字)をセット
「str1++='\0';」は、右端のコメントに「番兵(終了文字
)をセット」と記載した様にコンピュータプログラミングの
世界で番兵と呼ばれる目印を使用したテクニックを使いま
す!
番兵に関しては、画像処理本体で頻パンにテクニックとして
使用して居るので名前だけでも覚えて頂きたい!
「ltos(data,str1);」は、関数「ltos()」で数値データを
ローカルな文字列ポインタ「str1」の位置から十進法数字列
を作成して格納します!
ここで
注意して欲しいのは、既に「str1++='\0';」を実行した
直後の操作で
「番兵、十進法数字列」と言う風にローカル作業用文字列
領域「temp_str」の中の文字列データが成る事です!
if( mod >= 1 ){ // 右詰めにするなら
while( *str1 != '\0' ){ // 数字列の最終ポインタを
str1++; // 捜す
} //
str = end_str; // 最終ポインタの値をセット
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *--str1 ) == 0 ){ // 後ろからの文字が終了(番兵)
break; // なら、抜ける
} //
*--str = c; // 後ろから文字をセットする
} //
}else{ // 左詰めにするには
str1 = &temp_str[ 1 ]; // 文字の先頭にセットする
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *str1++ ) == 0 ){ // 前からの文字が終了したら
break; // 抜ける
} //
*str++ = c; // 前から文字をセットする
} //
} //
「if(mod>=1){・・右詰め・・}else{・・左詰め・・}」
先ず「if(mod>=1)」の条件「1以上、詰り正の値」が成立は
右詰め、不成立は左詰め
そして「右詰め」では、「while(str1!='\0'){str1++;}」
で操作ポインタ「str1」が作成された十進法数字列の
終了文字を指すポインタに成る様にする事に成ます!
次は、「str=end_str;」です!※元々「str」は仮引数でし
たが、C言語では関数内で値を書き換えても実引数には影響
が無いのでローカル変数として扱う事に注意
※ここでは、一旦、「 ' '」か「'0'」の文字で指定した
桁数分文字を埋めていた最後の終了文字を指すポインタを
「str」にセットしています!
「for(i=1;i<=absmod;i++){if((c=--str1)==0){
break;}--str=c;}」と後ろ(終了文字の一つ前)から
逆方向に最大指定桁数分(最大と記載したのは、
打ち切り条件「if((c=--str1)==0){break;}」と前方に
終了文字「その為に番兵を用意」)で「break;」文で強制
ループ終了するが、ループしている間「--str=c;」で
逆向きに進みながら文字列を格納!
そして「左詰め」では、「str1=&temp_str[1];」で単純
文字列作成関数「ltos()」で使用した先頭ポインタが
「&temp_str[1]」と成る事は、
}else{ // 左詰めにするには
str1 = &temp_str[ 1 ]; // 文字の先頭にセットする
for( i = 1; i <= absmod; i++ ){ // 桁数分繰り返す
if( ( c = *str1++ ) == 0 ){ // 前からの文字が終了したら
break; // 抜ける
} //
*str++ = c; // 前から文字をセットする
} //
} //
で変換して居るので分かると思います!
「for(i=1;i<=absmod;i++){if((c=*str1++)==0){break;}*str++=c;}」で桁数分順方向に文字を一文字ヅツ
打ち切り条件「if((c=*str1++)==0){break;}」も考慮して
コピーしています!
そして最後は、「return(end_str); 」と文字列の
終了文字を示すポインタを返す事で関数が終了します!
これで関数「ltosx()」まで説明しました!まだ、「Supportクラス」の解説
は終わりませんが、本日の講義はココまで!お疲れさまでした!
取り敢えず、今日(2月4)は、ココまで疲れた?!
また、これは近々続きの講義を始めますのでココまでは、
理解して下さい!!
2024年2月5継続講義
(3-4-3)関数「 int ltox(
long data,char*str,int mod);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
int Support::ltox(
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod // 変換モード
// 0→0を含めて8桁変換
// 1→0を消して左詰
// 2→0を消して右詰
){
union{ // 32と8Bits変換共用体
BYTE a[4]; // 8ビット4個
long b; // 32ビット1個
} d; // 上記の総称名d
int d1; // 1桁分のデータ
int zeroflag; // 真→データが0のまま
int i; // 桁数カウンタ
int temp; // 元文字列ptr:長算出用
temp = (int)str; // 文字列PtrAdr値を保存
if( data == 0 && mod == 1 ){ // 数値が0で左詰めなら
*str++ = '0'; // 文字列に0をセット
} //
zeroflag = TRUE; // データは、0のまま
for( i = 1; i <= 8; i++, data <<= 4 ){ // 8桁分処理する
d.b = data; // 最上位4ビット分
d1 = ( d.a[3] >> 4 ) & 0xf; //※桁データを取り出す※
if( zeroflag && ( d1 == 0 ) ){ // データが0時
if( mod == 0 ){ // モード0なら
*str++ = '0'; // 0をセット
}else if( mod == 1 ){ // モード1なら
; // 何もしない
}else if( mod == 2 ){ // モード2なら
*str++ = ' '; // スペースをセット
} //
}else{ // 0以外が、あれば
zeroflag = FALSE; // 0フラグを偽にセット
if( d1 < 10 ){ // 桁データが10未満時
*str++ = d1 + '0'; // 左記のように変換する
}else{ // 10以上の時
*str++ = d1 + 'A' - 10; // 左記のように変換する
} //
} //
} //
if( zeroflag && mod == 2 ){ // 最後まで0でMode2なら
*( str - 1 ) = '0'; // 最後の桁に0をセット
} //
*str = '\0'; // 終了文字をセット
return( (int)str - temp ); // 文字列の長さを返す
}
(3-4-3-A)エンディアンの話
真に恥ずかしい話だが、エッセイ『音楽と英語が出来無い』
に記載している様に私は「英語が全く駄目」で!
このエンディアンと言う単語を聴いた時、私の頭の中
「ワン、リトル、ツー、リトル、スリー、リトルインデアン
」⇒勿論、ディズニーの「イッツ・ア・スモール・ワールド
」のテーマ曲の中のインデアン≪今は、物語世界で
「アメリカ先住民」を扱う時か、マジでインド人に対しての
言葉でしか無い≫と語感が似た物が思い浮かんだのだが
全く違う意味の単語である事は、英単語に私より詳しい
読者様は御存知ですね?!
でもコンピュータサイエンスでは、独特の意味に成る事は、
今なら逆に少ないかな?!
では、本題、「CPUが複数バイト扱う時、連続する
アドレスのバイト列を16ビット・32ビット・64ビット
単位での数値(整数値や浮動小数点数値)としてメモリー
からCPU内部で扱う時の桁数の扱いが【リトルエンディ
アン⇒主にインテル系x86・ペンティアム系】と【
ビッグエンディアン⇒主にモトローラ系MC68及び
その後継CPU】で違うのです!
中には、CPUに外部信号か内部でモードチェンジで変更
出来るARM系のCPUが出来ると聞いた事が有る様な無い
様なだがARMは、私は使った覚えが無い?!」で何が、
リトル/ビッグで違うかと言うと?!
例えば、バイト列(ヘキサデシマル表現)「0x1234」
⇒
メモリー上のアドレスとデーター
アドレス データ
0000 12
0001 34
0002 56
0003 78
0004 9A
0005 BC
0006 DE
0007 F0
と有ったとします!
リトルエンディアンでCPUの中では、16ビットデータ
「0x3412」十進法数字では、「13330」と成りま
す!
ビッグエンディアンでは、CPUの中では、16ビット
データ「0x1234」十進法数字では、「4660」と
成ります!
パット目にビッグエンディアンの方が、分かり易いと思いま
すが、実は、リトルエンディアンの方が良く考えられている
事が、CPUを使用したハードウェアシステムを設計して居
ると分かってきました!
今回、例にした「アドレス0000」で始まるバイト列なら、
リトル/ビッグも同じですが、例えば、「アドレス0002」で
始まるバイト列で16ビットデータなら、データ「5678」、
リトルエンディアン「0x7856」、
ビッグエンディアン「0x5678」、そして32ビット
データならば、データ「56789ABC」、リトルエンディアン
「0xBC9A7856」、ビッグエンディアン
「0x56789ABC」、と扱われます?!
私の解説
が下手かも知れませんが、アドレスが同じでもCPU内部へ
取り込む値が、異なる事は、理解出来たかな?!
マズイ、この程度の解説は、分かり易い簡単な文章で説明
出来た筈が、記載出来無く成った?!
説明能力が衰える前に画像処理本体の解説まで行かねば?!
まっ、エンディアンの話が気に成ると思いますが、
気に成る方は、エンディアンの解説専門を探して調べて下さ
い?!
ココでは、「元々画像処理ライブラリは、消滅させられた
ベンチャー企業ADS社の画像処理装置PIPシリーズ用に
開発したC言語のコンピュータプログラミングですが、
PIPはモトローラ系CPUを採用していた為に
ビッグエンディアン用のソースコードですが、21世紀に
ウィンドウで動作する様に改修したライブラリとして
リトルエンディアン用のソースコードに変更して居ます!」
⇒だから、このライブラリは、リトルエンディアン用に変更
した物です!
コレが、長々と解説した理由です!
(3-4-3-B)関数「 int ltox(・・・);」の【仮引数】説明
int Support::ltox(
long data, // 32ビット数値データ
char *str, // 変換された文字列
int mod // 変換モード
// 0→0を含めて8桁変換
// 1→0を消して左詰
// 2→0を消して右詰
){
「long data,」は、32ビットデータ≪「long型」だが、
単にサイズだけが意味を持ち符号有無(±関係無し)≫の
元の数値データを示します!
「char*str,」は、結果として変換すれたヘキサデシマル
表現の文字列を格納する文字列ポインタ先頭です!
「int mod」は、細かい指定≪mod=0⇒8桁の文字列、
mod=1⇒数値として上位桁が「0」ならばソコを消去して
左詰め、
mod=2⇒数値として上位桁が「0」ならばソコを消去して
右詰め≫
(3-4-3-C)関数「 int ltox(・・・);」の【ローカル変数】説明
){
union{ // 32と8Bits変換共用体
BYTE a[4]; // 8ビット4個
long b; // 32ビット1個
} d; // 上記の総称名d
int d1; // 1桁分のデータ
int zeroflag; // 真→データが0のまま
int i; // 桁数カウンタ
int temp; // 元文字列ptr:長算出用
「union{BYTE a[4];long b;}d;」この「共用体」を
使うのが、コノ関数のミソです!
因みに「BYTE」は、解説『エラーコード等各種単純定義』で
説明しているヘッダファイル『ImageFuncDef.h』で定義して
有る「unsigned char」です!
この「BYTE」型は画像処理で基本的に良く使いますので
覚えてね!
そして「共用体」に関しては、余りC言語系の教習所では
教え無いと思うが、ココで使って居るので覚えてね!
私のライブラリでも他では余り使用して無い筈です!
「int d1;」は、一桁分切り取り置いて置く場所です!
「int zeroflag;」は、対象桁がゼロか否かの判別した値!
「int i;」は、桁数を数えるカウンタ!
「int temp;」は、一旦、仮引数「char*str,」の
ポインタの値を保存する場所です!
関数最後の「return((int)str-temp);」でバイト数算出に利用!
(3-4-3-D)関数「 int ltox(・・・);」の【アルゴリズム】説明
temp = (int)str; // 文字列PtrAdr値を保存
if( data == 0 && mod == 1 ){ // 数値が0で左詰めなら
*str++ = '0'; // 文字列に0をセット
} //
zeroflag = TRUE; // データは、0のまま
for( i = 1; i <= 8; i++, data <<= 4 ){ // 8桁分処理する
d.b = data; // 最上位4ビット分
d1 = ( d.a[3] >> 4 ) & 0xf; //※桁データを取り出す※
if( zeroflag && ( d1 == 0 ) ){ // データが0時
if( mod == 0 ){ // モード0なら
*str++ = '0'; // 0をセット
}else if( mod == 1 ){ // モード1なら
; // 何もしない
}else if( mod == 2 ){ // モード2なら
*str++ = ' '; // スペースをセット
} //
}else{ // 0以外が、あれば
zeroflag = FALSE; // 0フラグを偽にセット
if( d1 < 10 ){ // 桁データが10未満時
*str++ = d1 + '0'; // 左記のように変換する
}else{ // 10以上の時
*str++ = d1 + 'A' - 10; // 左記のように変換する
} //
} //
} //
if( zeroflag && mod == 2 ){ // 最後まで0でMode2なら
*( str - 1 ) = '0'; // 最後の桁に0をセット
} //
*str = '\0'; // 終了文字をセット
return( (int)str - temp ); // 文字列の長さを返す
}
「temp=(int)str;」は、仮引数「str」の先頭ポインタ値を
整数値として保存
「if(data==0&&mod==1){*str++='0';}」は、
仮引数「data」データの値が「0」で仮引数「mod」関数の
動作モードが「mod=1⇒左詰め」
「zeroflag=TRUE;」は、初期値が「真」にセット
「for(i=1;i<=8;i++,data<<=4){・・ループ中身・・ }」は
処理の本体として数値データをヘキサデシマル表現に変換
します!
以降で中身の説明を行います!
で、先ずは、「for()」繰り返し構文で「i=1;」は、
8桁と桁数の1から8までをカウントする値の初期値、
「i<=8」は、繰り返し条件で8回まで繰り返しと極普通の
教科書的な記載!
「i++,data<<=4」は、勿論、「i++,」の部分は、繰り返し
カウンターのアップだが、「data<<=4」は、ループの中身が
終わった後で4ビット(ヘキサデシマル表現1桁分)
左シフト≪数値的には×16倍したとシフト演算に不慣れな
人は考えてください!
元々速度的に有利なのと意味的に4ビット一桁動かすので
依りふさわしいと考えた≫する事で中身で紹介する
「(d.a[3]>>4)&0xf;」で最上位4ビットを取り出す為の
場所にシフト移動する為です!
勿論、ループする本体の最後にしても良いのですが、
意味的に「for構文」に入れたのは、桁数を
カウントアップと同じ時に4ビットシフトする事を明示した
心算です!速度的には関係ありません?!
では、中身
「d1=(d.a[3]>>4)&0xf;」は、変数「d1」に32ビット
データ最上位の4ビットデータを切り取り「0~15」の
値にした事を意味します?!
動作はC言語を使われた方は判る筈ですし、不明ならば
C言語教科書で確認して下さい!
「if(zeroflag&&(d1==0)){・・成立中身・・}else{
・・不成立中身・・}」
は、先ず条件「zeroflag&&(d1==0)」で
初期値「zeroflag」は真なので実質的に「(d1==0)」条件で
成立「対象の桁のデータが0」ならば成立中身を実行、
非成立で「zeroflag = FALSE;」と常に非成立に成る事に
注意!
そして「・・成立中身・・」を解説して行きます!
「if(mod==0){*str++='0';}else if(mod==1){;}else
if(mod==2){*str++=' ';}」は、C言語を使われた事が
ある人にはお馴染みの「if~elseif~else~」
と三分岐で「mod=0」の時「*str++='0';」と
仮引数「str」を文字列ポインタ操作で文字コード「'0'」
を文字列に追加、「mod=1」の時「;」と何もシナイ、
「mod=2」の時「*str++=' ';」と仮引数「str」を文字列
ポインタ操作で文字コード「' '」空白文字を文字列に
追加します!
では今度は、「・・不成立中身・・」を解説して行き
ます!
「zeroflag=FALSE;if(d1<10){*str++=d1+'0';}else
{*str++=d1+'A'-10;}」は、「zeroflag = FALSE;」と
常に非成立に成るフラグ(旗)をセットします!
そして「if(d1<10){*str++=d1+'0';}else
{*str++=d1+'A'-10;}」で条件「d1<10」詰り
10未満なら⇒「str++=d1+'0';」文字'0'~文字'9'まで
数値から文字コードへ変更し文字を文字列に追加します!
条件「d1<10」不成立詰り10以上なら⇒
「str++=d1+'A'-10;」詰り、数値10~15を
文字'A'~文字'F'まで数値から文字コードへ変更し文字を
文字列に追加します!
これでループの中身は理解出来たと思います!
「if(zeroflag&&mod==2){(str-1)='0';}」は、
条件「zeroflag&&mod==2」でコメント「最後まで0」とは、
「・・不成立中身・・」の「zeroflag=FALSE;」
が一度も実行されず、且つ「mod==2」の場合、
「(str-1)='0';」を使用して「*str++=' ';」で
空白文字を文字コード「'0'」に置き換える処理です!
「*str='\0';」で終了文字を仮引数「str」の文字列
ポインタ操作の最後に実行!
「return((int)str-temp);」と数値計算で仮引数「str」の
文字列ポインタ操作で文字列が格納された長さを算出し、
関数のリターン値として呼び出し側へ値を返します!
☆勿論、ビッグエンディアンのCPU対応する為には、
「d.a[3]」を何に置き換えれば良いか分かると思いますが、
ココは宿題として置きます!☆
今回は、関数「void cvtStrHex()」:文字列を
16進数値列に変換するを扱います!
(3-4-4)関数「void cvtStrHex(
charstr,charbuf,int sw);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
void Support::cvtStrHex(
char* str, // 文字列
char* buf, //16進数値列
int sw // 0:ABCDEF(省略時)
// 1:abcdef
){
static const char tbl[] = "0123456789ABCDEF"; // 16進文字Table
static const char tbls[] = "0123456789abcdef"; // 16進文字Table
int mask; // 4ビットマスク値
int data; // 個別変換データ
mask = 0x0f; // マスク値をセット
if( sw == 0 ){ // ABCDEFならば
while( ( data = *str++ ) != 0 ){ // 文字Data個別を
*buf++ = tbl[ ( data >> 4 ) & mask ]; // 上位4Bits変換し
*buf++ = tbl[ data & mask ]; // 下位4Bits変換し
} // Bufferに登録
}else if( sw == 1 ){ // abcdefならば
while( ( data = *str++ ) != 0 ){ // 文字Data個別を
*buf++ = tbls[ ( data >> 4 ) & mask ]; // 上位4Bits変換し
*buf++ = tbls[ data & mask ]; // 下位4Bits変換し
} // Bufferに登録
} //
*buf = 0; // 終了文字セット
}
(3-4-4-A)関数名「cvtStrHex」
は、「cvt」で英単語「convert」の略語です!
「Str」は、「string」文字列の一般的な略で「S」と
英大文字なのは、単語の区切りとします!
「Hex」は、「ヘキサデシマル」で英単語「hexadecimal」
の略です!
と記載したのですが、関数の中身を精査したら、
コメントの「文字列を」が、何故か、オカシイ??
私が、設計した筈だが、忙しい時にコメントを間違えたと
考えた方が、良い!
コノ関数は、文字列と言う依りも「char str」は、
確かに「文字を扱う【char】」を型として使用しますが
単に「バイト=8ビット」データの数値データ列を扱い?!
関数自体の機能は、「バイト単位数値データ列を
ヘキサデシマル表現文字列に変換」です!
※言い訳※30年以上前に作成したソースコードを
ウィンドウズ用に動作を考えて改修したのが20年程前で
元々「公開を前提として無かった」物なので改修した時に
精査し無かったのが原因です!
解説の過程で色々出て来ると思いますが、
ソースコード上のコメントは、基本変更する事は
シナイ事にします!
下手に変更すると泥縄が泥縄を呼ぶと言う酷い事に成り
カネナイからです!
(3-4-4-B)関数「void cvtStrHex(・・・);」の【仮引数】説明
void Support::cvtStrHex(
char* str, // 文字列
char* buf, //16進数値列
int sw // 0:ABCDEF(省略時)
// 1:abcdef
){
「char*str,」は、コメントでは「文字列」と
記載して居ますが、バイト「8ビット数値データ」の
データ列として変換する元のデータです!
「char*buf,」は、ヘキサデシマル表現文字列に変換した
文字列を格納する場所を示すポインタ変数です!
「int sw」は、値が「0」で "0123456789ABCDEF"と英大文字
値が「1」で"0123456789abcdef"と英小文字の文字列に変換
するモードスイッチです!
(3-4-4-C)関数「void cvtStrHex(・・・);」の【ローカル変数】説明
static const char tbl[] = "0123456789ABCDEF"; // 16進文字Table
static const char tbls[] = "0123456789abcdef"; // 16進文字Table
int mask; // 4ビットマスク値
int data; // 個別変換データ
定数テーブル「tbl[]」は、英大文字系の変換テーブル
です!
定数テーブル「tbls[]」は、英小文字系の変換テーブル
です!「s」の名称は小文字(スモール)を意味します!
ローカル変数「int mask;」は、マスク≪この言い回しは、
覚えて欲しい⇒マスキング(絵画とか塗装作業する時に
必要な部分をマスキングテープ等で覆う様な処理)する事
です≫用で数値データの下位4ビットをマスキングする
用途です!
ローカル変数「int data;」は、仮引数「char*str」から
1バイト分データを取り出した値を一時的に置いて置く
所です!
(3-4-4-D)関数「void cvtStrHex(・・・);」の【アルゴリズム】説明
mask = 0x0f; // マスク値をセット
if( sw == 0 ){ // ABCDEFならば
while( ( data = *str++ ) != 0 ){ // 文字Data個別を
*buf++ = tbl[ ( data >> 4 ) & mask ]; // 上位4Bits変換し
*buf++ = tbl[ data & mask ]; // 下位4Bits変換し
} // Bufferに登録
}else if( sw == 1 ){ // abcdefならば
while( ( data = *str++ ) != 0 ){ // 文字Data個別を
*buf++ = tbls[ ( data >> 4 ) & mask ]; // 上位4Bits変換し
*buf++ = tbls[ data & mask ]; // 下位4Bits変換し
} // Bufferに登録
} //
*buf = 0; // 終了文字セット
}
「mask = 0x0f;」は、数値データ下位4ビット≪2進数
表現「00001111」と考えて下さい!≫、序にローカル変数
定義で「static const int mask= 0x0f;」と記載してココは
略した方が自然だと思われる読者様も多いと思いますが、
※説明※当初、「static const・・」と記載したら
コンパイルに使用した「WindowsXPのVisualC++6.0」が正常
に実行動作するのだが、ワーニングが出た為に「const」の
扱いを考慮して一番ワーニングが表示しない記法にしたから
です!
正直な所「const」の扱いは、処理系に依って考え方が異
なる場合も有るので読者様が使用されるコンパイラシステム
で最適に成る様に変更が必要かも知れませんが、私は、
コの様に記述しました!
「if(sw==0){while((data=*str++)!=0){・・条件(sw=0)
成立時のループの中身・・}}else if(sw==1){・・
条件(sw=1)成立時のループの中身・・}}」と
「if分岐」の中身が同じような「whileループ」と
分かると思います、読者様の中には、「whileループ」
を外側にして「if分岐」を内側にしても同じ、更には、
その方が分かり易いと考える人も居るかも知れませんが、
基本私のソースコードは、高速に動作させる事を目的に
記述していますので無駄(1回条件判断すれば良い所を
ループ内でループ回数判断は非効率)と考える為、
他の処理でも基本的にコノ方式≪外が分岐・内側が、
ループが多い?!
例外が有るとすれば、それ成りの理由が有ると考えて下さ
い≫
では、「条件(sw=0)成立時のループ」でループ条件
「(data=*str++)!=0」は、私のソースコードで良く使われる
記述で「data=*str++」でデータ列(文字列)等の
ポインタ変数でデータを取り出し一時置き場(多くの
CPUでは高速処理する内部レジスターへ格納)にセットし
ながら、ポインタの示す位置を「*str++」とCPUに
依っては、アクセス方式の一部なので「ロード系・
ムーブ系」インストラクションの一部に成り、
特に高速化可能、更に「!=0」と判定しているが往々にして
CPU命令では内部レジスターに格納時にステータス
フラグが=0か≠0のフラグが立つので賢いコンパイラ
ならば余計な機械コードを発生せず最適な機械コードが
生成されループ判断にも使用される事に成ります!
ヨッポド変なコンパイラシステムで無ければ、
この様な記載が最速に成る筈です!
この様に「data」に既に処理するデータがループ本体を
実行する前に格納されて居る事に注意して下さい!
そしてループの中身、
「*buf++=tbl[(data>>4)&mask];」は、データの上位4
ビット≪C言語を使用されている方には説明の必要が無いと
思うが、算術演算しか理解されて無い方に解説として
「data>>4」とデータを4ビット右へシフトするとは、
16で算術除算(割り算)する事と今回の場合同じです?!
詰り、バイトと8ビットデータがヘキサデシマル表現で
2桁として上位桁を抜き出した事です、更に「&mask」は、
下位4ビットに右シフトの結果の数値にマスキングして必要
な所を抜き出すとの意味です!※2の補数表現では右シフト
すると負の値を表す「1」埋めが起こるからです※
更に大昔のCPUでは除算は無茶苦茶動作に時間が掛った
のでシフト演算にした事もコノ方式にした理由ですが
元々上位桁の4ビットを右シフトで下位4ビットに移す方が
意味的にも正当と考えたのです!≫という事で
「tbl[・・演算中身・・]」で中身は、0~15迄と
4ビット内の値に成る事は理解して頂いたと思います!
では、「tbl[・・演算中身・・]」は、中身の値を
定数テーブル「tbl[]」へテーブル変換≪中身の値が
インデックス添え字としてテーブルの中身の値に置き換わる
≫が起こります≪テーブル変換は、純粋な日本語では
「表引き」かな≫では、「条件(sw=1)成立時のループ」
では、「条件(sw=0)」との違いは、読んで頂ければ、
分かる様にテーブル変換のテーブルが「条件(sw=0)」で
は「tbl[]」と英大文字で変換するテーブルに成って居るの
が、「tbls[]」と英小文字で変換するテーブルに成って居る
事が異なります!
これで分岐したブロック説明までは理解して頂いたと思いま
す!
関数実行部の最後は、「*buf=0;」は、終了文字のセットで
す!
今回は、関数「char stoi(charptr,int&data);」:数字列を数値に変換を扱います!
(3-4-5)関数「char stoi(char*ptr,int&data);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて居て次の様に
char* Support::stoi(
char* ptr, // 数字文字列
int& data // 結果整数
){
int d; // 数値データの格納場所
int c; // 文字
int c0; // 文字 '0'
int c9; // 文字 '9'
c0 = '0'; // 文字定数 '0' をセット
c9 = '9'; // 文字定数 '9' をセット
d = 0; // データをクリア
while( ( c = *ptr++ ) != 0 ){ // 文字終了まで処理する
if( c >= c0 && c <= c9 ){ // 10進数字なら
do{ // 10進数文字を 0..9
d *= 10; // の数値に変換し最下位
d += ( c - c0 ); // 桁として加える
c = *ptr++; // 次の文字を取出、10
}while( c >= c0 && c <= c9 ); // 進数字なら繰り返す
data = d; // 変換したデータを返す
return( ptr - 1 ); // 現行の文字列ptrを返す
} //
} // 文字最後まで数値文字
return( 0 ); // 以外 → 0 を返す
}
(3-4-5-A)関数名「stoi();」の説明
「s」は、勿論、「string」文字列を示し、「to」は英語ソノママ「何々か
ら何処其処へ」を意味すします、「i」は、「integer」整数値です!
詰り、文字列を整数値に変換する関数です!
(3-4-5-B)関数「stoi();」の【仮引数】説明
char* Support::stoi(
char* ptr, // 数字文字列
int& data // 結果整数
){
「char*ptr,」は、コメント「数字文字列」と有る様に十進法数字≪"0"~"9"≫で構成された文字列を想定して居ます!
「int&data」は、仮引数に「&」を使用して居ます!多分、読者様の中でも
分かり難いと思うので使わ無い用法で使わ無い事に決めている方も多いと思いますが、今回は「整数値を返す為に」今回だけ限定して使用した筈です!
※「int*ptrData」とでも記載して「data=d;」で無く「*ptrData=d;」とこの関数内で記載し、実引数が、例えば「int data;stoi("123",data)」
と現行の記載では無く、「int data;stoi("123",&data)」と記載しても同じですが、現行の方が多少分かり易いと作成時当時は思ったのです!※
(3-4-5-C)関数「stoi();」の【ローカル変数】説明
){
int d; // 数値データの格納場所
int c; // 文字
int c0; // 文字 '0'
int c9; // 文字 '9'
「int d;」は、関数内部で合成される数値データを格納する変数です!
「int c;」は、仮引数「char*ptr」の文字列ポインタから一文字取り出し
数値合成演算する為に置いた場所の変数です!
「int c0;」は、本体のアルゴリズムで「c0='0';」と初期化して定数代わり
に使います!何故、直接「'0'」や「static const int c0='0';」と記載しないかと言うと高速化の為です※
アルゴリズム本体の中に初期値を入れるとCPU内部レジ
スターに変数を割り当てる事が可能なCPUでは割り当て
る為にメモリーアクセスを抑止出来高速に動作可能との理由です※
「int c9;」は、本体のアルゴリズムで「c9='9';」と初期化して定数代わりに使います!
この記述にした理由は「int c0;」と同じ理由です!
(3-4-5-D)関数「stoi();」の【アルゴリズム】説明
c0 = '0'; // 文字定数 '0' をセット
c9 = '9'; // 文字定数 '9' をセット
d = 0; // データをクリア
while( ( c = *ptr++ ) != 0 ){ // 文字終了まで処理する
if( c >= c0 && c <= c9 ){ // 10進数字なら
do{ // 10進数文字を 0..9
d *= 10; // の数値に変換し最下位
d += ( c - c0 ); // 桁として加える
c = *ptr++; // 次の文字を取出、10
}while( c >= c0 && c <= c9 ); // 進数字なら繰り返す
data = d; // 変換したデータを返す
return( ptr - 1 ); // 現行の文字列ptrを返す
} //
} // 文字最後まで数値文字
return( 0 ); // 以外 → 0 を返す
}
「c0='0';」は、定数初期化です!説明はローカル変数の説明を参考!
「c9='9';」は、定数初期化です!説明はローカル変数の説明を参考!
「d=0;」は、数値を合成するので初期値として値を0クリア
「while((c=*ptr++)!=0){・・ループ中身・・}」は、ループ条件「
(c=ptr++)!=0」と文字列ポインタから文字コード一文字取り出し終了する
までループすると理解して下さい!そして中身「if(c>=c0&&c<=c9){・・
分岐条件成立・・}」とループの中で分岐して居ます!※
3-4-4-D関数「void cvtStrHex(・・・);」の【アルゴリズム】説明の中で「whileループ」の中で
「if分岐」が有るのは高速に動作する事を考えたら無駄な事だと説明して有るのにココで「if分岐」が有るのは
オカシイと思われた読者様も居ると思いますが、
ココは単純に繰り返しループを行う処理を行って居るので無い事に注意して下さい※
if条件「c>=c0&&c<=c9」は、一文字取り出した文字コードデータが、
数字用の文字「'0'から'9'の範囲に入って居る、即ち、
十進法数字を判断」しています!
この条件で実行する中身をコレから説明します、
「do{do・・中身・・⇒whileループ」は、御存知の様に中身を1回は実行し中身の直後の「while(c>=c0&&c<=c9);」で次のループするかループ
終了を判断します!
では、ココの中身をコレから説明します、
「d=10;」は、十進法数値としての桁上げです!
「d+=(c-c0);」は、「c-c0」を行う事で文字「'0'から'9'の範囲」を数値の「0から9」へ変換している事は理解して頂け
ますね!
そして「d+=」で桁上げ後で今回の新しい桁の数値を加える事を意味します!
「c=*ptr++;」は、次の文字コード一文字取り出す事は理解して頂けますね!
この新しい文字コード「c」を「while条件」で次の
ループするかループ終了を判断します!
「data=d;」は、仮引数「int&data」に合成した数値データを格納します!
「return(ptr-1);」は、「c=*ptr++;」≪whileループ条件「while((c=*ptr++)!=0){」とdoループの中身「c=ptr++;」と二か所≫ありますが何方も「ptr++」と実行したらポインタが一つ進んでいるので「(ptr-1)」と一つ戻す事を行います!
そしてループも分岐も外の
「return(0);」は、仮引数「charptr,」で示される文字列の中に数字文字「'0'から'9'の範囲」が存在して無かった事を示します!
★備考★
解説『解説クラスTypeArray』でも記載したが、
noteエディタの変な特性でコピペした文章の半角「*」
が消されたり、空白「 」が消される事が多々あります!
注意して手作業で修正している筈ですが、必ず、code
機能で表示して居る物を正しいとして確認して下さい
私も疲れたので「stoi()」の解説までで一旦、
今日(2月5)の講義は終わりにします!
マダ続きが有りますので後日!!
2024年2月6継続講義
今回は、関数「char skipSpace(charptr);」:空白文字の
読み飛ばしを扱います!
更に関数「char skipSpaceComma(char*ptr);」:空白文字と
「,」の読み飛ばしを扱います!
(3-4-6)関数「char skipSpace(char*ptr);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
char* Support::skipSpace(
char* ptr // 文字列
){
int c; // 文字
for(;;){ // 以下を繰り返し
c = *ptr; // 一文字取り出し
if( c == ' ' || c == '\t' ){ // 空白文字ならば
ptr++; // Skip
}else{ // 上記以外ならば
return( ptr ); // ポインタを返す
} //
} //
}
(3-4-6-A)関数名「skipSpace();」の説明
「skip」は、英単語の意味通り「飛び越える、省略する」と
言う意味です!
「Space」は、「空白文字’ ’=文字コード(「0x20」
)を意味します!
詰り、関数としては「空白文字を読み飛ばす」事に成りま
す!
この関数のリターン値は、「char*」とポインタの値で
スキップした場所を示します!
(3-4-6-B)関数「skipSpace();」の【仮引数】説明
char* Support::skipSpace(
char* ptr // 文字列
){
仮引数「char*ptr」は、処理対象の文字列ポインタです!
(3-4-6-C)関数「skipSpace();」の【ローカル変数】説明
){
int c; // 文字
文字「int c;」は、「c=*ptr;」と仮引数「ptr」から
一文字文字コードを取り出した置き場所です!
独立した変数に一旦、置くのは、勿論、高速化記述で
CPU内部レジスターに変数が割り当てる事が可能な
CPU用のコンパイラならば内部レジスターに割り当てる
事で高速化される筈です!
(3-4-6-C)関数「skipSpace();」の【アルゴリズム】説明
for(;;){ // 以下を繰り返し
c = *ptr; // 一文字取り出し
if( c == ' ' || c == '\t' ){ // 空白文字ならば
ptr++; // Skip
}else{ // 上記以外ならば
return( ptr ); // ポインタを返す
} //
} //
}
「for(;;){・・ループの中身・・ }」は、無限ループ≪
終了条件はループの中で判断≫の記載法です!
データの中身が分からない処理を行う場合、良く使用する
パターンですから覚えてね?!
その「ループの中身」は、これから解説します!
「c=*ptr;」は、一文字分、データを取り出します!
注意して欲しいのは、単純に「*ptr」とポインタの値は
進行も後退もシナイ事です!
「if(c==' '||c=='\t'){ptr++;}else{return(ptr);}」は、
if条件「c==' '||c=='\t'」、詰り、
文字コードが空白「0x20」かタブ「'\t'=0x09」ならば、
条件成立⇒「ptr++;」とポインタを一文字進める!
「}else{」詰り、条件不成立⇒「return(ptr);」と
リターン値として現行のポインタ「ptr」の値を
リターン文として関数の値として返し関数を終了する事と
ココで関数の実行が終わる事に注意して下さい!
(3-4-7)関数「char* skipSpaceComma(charptr);」の説明
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
char* Support::skipSpaceComma(
char* ptr // 文字列
){
int c; // 文字
for(;;){ // 以下を繰り返し
c = *ptr; // 一文字取り出し
if( c == ' ' || c == '\t' || c == ',' ){ // 空白文字と「,」ならば
ptr++; // Skip
}else{ // 上記以外ならば
return( ptr ); // ポインタを返す
} //
} //
}
(3-4-7-A)関数名「skipSpaceComma();」の説明
「skip」は、英単語の意味通り「飛び越える、省略する」と
言う意味です!
「Space」は、「空白文字’ ’=文字コード【0x20】」
を意味します!
「Comma」は、「記号コンマ’, ’=文字コード【0x2c
】」を意味します!
詰り、関数としては「空白文字とコンマを読み飛ばす」事に
成ります!
この関数のリターン値は、「char*」とポインタの値で
スキップした場所を示します!
(3-4-7-B)関数「skipSpaceComma();」の【仮引数】説明
char* Support::skipSpaceComma(
char* ptr // 文字列
){
仮引数「char*ptr」は、処理対象の文字列ポインタです!
(3-4-7-C)関数「skipSpace();」の【ローカル変数】説明
){
int c; // 文字
文字「int c;」は、「c=*ptr;」と仮引数「ptr」から
一文字文字コードを取り出した置き場所です!
独立した変数に一旦、置くのは、勿論、高速化記述で
CPU内部レジスターに変数が割り当てる事が可能な
CPU用のコンパイラならば内部レジスターに割り当てる
事で高速化される筈です!
(3-4-7-D)関数「skipSpaceComma();」の【アルゴリズム】説明
for(;;){ // 以下を繰り返し
c = *ptr; // 一文字取り出し
if( c == ' ' || c == '\t' || c == ',' ){ // 空白文字と「,」ならば
ptr++; // Skip
}else{ // 上記以外ならば
return( ptr ); // ポインタを返す
} //
} //
}
「for(;;){・・ループの中身・・ }」は、無限ループ≪
終了条件はループの中で判断≫の記載法です!
データの中身が分からない処理を行う場合、良く使用する
パターンですから覚えてね?!
その「ループの中身」は、これから解説します!
「c=*ptr;」は、一文字分、データを取り出します!
注意して欲しいのは、単純に「*ptr」とポインタの値は
進行も後退もシナイ事です!
「if(c==' '||c=='\t'||c==','){ptr++;}else
{return(ptr);}」は、if条件「c==' '||c=='\t'||c==','
」、詰り、文字コードが空白「0x20」かタブ「'\t'=0x09」
及びカンマ「','=0x2c」ならば、条件成立⇒「ptr++;」と
ポインタを一文字進める!
「}else{」詰り、条件不成立⇒「return(ptr);」と
リターン値として現行のポインタ「ptr」の値をリターン文
として関数の値として返し関数を終了する事とココで関数の
実行が終わる事に注意して下さい!
今回は、関数「getStringLineToInt(char*p,int&d);」:
「文字列から整数データを抽出」を扱います!
(3-4-8)関数「getStringLineToInt(char*p,int&d);」の説明
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
char* Support::getStringLineToInt(
char* p, // 行バッファー内ポインタ
int& d // データ:整数
){
int c; // 文字
int flagM; // マイナスフラグ
flagM = FALSE; // フラグ初期化
p = skipSpaceComma( p ); // 空白文字と「,」の読み飛ばし
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d ); // 整数値を取り出す
if( flagM ){ // マイナスフラグがONの場合
d = -d; // 符号反転を行う
} //
return( p ); // ポインタを返す
}else if( c == '-' ){ // 「-」ならば
if( flagM ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM = TRUE; // フラグをONする
p++; // Skip
}else{ // 上記以外ならば
break; // Loop終了
} //
} //
return( 0 ); // 数値がない場合左記を返す
}
(3-4-8-A)関数名「getStringLineToInt();」の説明
「get」は、英単語の意味通り「得る」と言う意味です!
「String」は、「文字列」を意味します!
「StringLine」で文字列行(改行までの文字列)を意味し
ます!
「To」は、英語ソノママ「何々から何処其処へ」を意味し
ます、そして
「Int」は、整数値を意味、詰り、
関数としては「文字列行を数値に変換」した事に成ります!
この関数のリターン値は、「char*」とポインタの値で処理
した文字列の場所を示します!
(3-4-8-B)関数「getStringLineToInt();」の【仮引数】説明
char* Support::getStringLineToInt(
char* p, // 行バッファー内ポインタ
int& d // データ:整数
){
「char*p,」は、処理する文字列へのポインタです!
「int&d」は、結果としての整数値を返す仮引数です!
勿論、対応する実引数は、メモリーとして実体が有る事が
必要です!
(3-4-8-C)関数「getStringLineToInt();」の【ローカル変数】説明
){
int c; // 文字
int flagM; // マイナスフラグ
「int c;」は、アルゴリズムで処理(主に判断する為)する
為に仮引数「char*p」から一文字分取り出した文字コードの
置き場です!
「int flagM;」は、整数値の±関係のフラグ(旗)です!
私のプログラムでは、この「flag」フラグ(旗)と言う
表現と「st」ステータスフラグを示す物とかを使用します!
微妙に言い回しが異なるのは、作成時の気分です!
ニュアンスも含めて読み解くときに考えたら良いかな??
(3-4-8-D)関数「getStringLineToInt();」の【アルゴリズム】説明
flagM = FALSE; // フラグ初期化
p = skipSpaceComma( p ); // 空白文字と「,」の読み飛ばし
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d ); // 整数値を取り出す
if( flagM ){ // マイナスフラグがONの場合
d = -d; // 符号反転を行う
} //
return( p ); // ポインタを返す
}else if( c == '-' ){ // 「-」ならば
if( flagM ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM = TRUE; // フラグをONする
p++; // Skip
}else{ // 上記以外ならば
break; // Loop終了
} //
} //
return( 0 ); // 数値がない場合左記を返す
}
「flagM=FALSE;」は、フラグ(旗)を「FALSE」偽に
初期設定!
「p=skipSpaceComma(p);」は、
「(3-4-7)関数skipSpaceComma()」で説明した
「空白文字とコンマを読み飛ばす」処理でポインタ「p」を
操作します!
「for(;;){・・ループの中身・・ }」は、無限ループ≪
終了条件はループの中で判断≫の記載法です!
データの中身が分からない処理を行う場合、良く使用する
パターンですから覚えてね?!
その「ループの中身」は、これから解説します!
「p=skipSpace(p);」は、
「(3-4-6)関数skipSpace()」で説明した
「空白文字を読み飛ばす」処理でポインタ「p」を操作しま
す!
「c=*p;」は、一文字分、データを取り出します!
注意して欲しいのは、単純に「*p」とポインタの値は進行も
後退もシナイ事です!
「if(c>='0'&&c<='9'){・・成立中身A・・}else
if(c=='-'){・・成立中身B・・}else{・・不成立中身・・}
」は、先ず、「成立中身A」でこの成立する
条件「c>='0'&&c<='9'」は、C言語に馴染んでいる読者様
には「文字コード【'0'=0x30】から
【'9'=0x39】」の範囲の文字コードなら成立と言う
事は理解して頂いていると思います!
中身の「p=stoi(p,d);」は、
【(3-4-5-A)関数名「stoi();」の説明】で
「文字列を整数値に変換する関数です!」を使用して仮引数
ポインタ「p」の中の文字列を操作し仮引数「int&d」へ
整数値をセットします!
「if(flagM){d=-d;}」は、「flagM」±フラグが既に何処
かで真に成って居た場合に「d=-d;」と整数値の符号を
逆転させます!
「return(p);」とリターン値として現行のポインタ「p」の
値をリターン文として関数の値として返し関数を終了する事
とココで関数の実行が終わる事に注意して下さい!
「成立中身B」でこの成立する条件「c=='-'」は、
「文字コード【'-'=0x2d】」詰り、
「マイナスを示す【文字'-'】」の場合の処理で
「if(flagM){return(0);}flagM=TRUE;p++;」と先ず、
「if(flagM){return(0);}」で「flagM」±フラグが既に
何処かで真に成って居た場合に「return(0);」と
リターン値として「0」をリターン文として関数の値として
返し関数を終了する事とココで関数の実行が終わる事に注意
して下さい!
リターン値が「0」、詰り「ポインタの値」としては、
「空ポインタ」=この関数で実行エラーが生じた事を
示します!
「flagM=TRUE;」は、「flagM」±フラグ(旗)を
ココで「マイナスを示す【文字'-'】」が存在した事を示す
為に真にセットします!
「p++;」は、【文字'-'】を処理した為にポインタを
一文字分進行させます!
「不成立中身」は、「break;」と
無限ループ「for(;;){・・ループの中身・・}」から脱出
する事を示します!
ここで無限ループのブロック「for(;;){・・
ループの中身・・}」を終了します!
そして最後に「return(0);」とリターン値として「0」を
リターン文として関数の値として返し関数を終了する事と
ココで関数の実行が終わる事に注意して下さい!
リターン値が「0」、詰り「ポインタの値」としては、
「空ポインタ」=この関数で実行エラーが生じた事を
示します!
★備考★
解説『解説クラスTypeArray』でも記載したが、
noteエディタの変な特性でコピペした文章の半角「*」
が消されたり、空白「 」が消される事が多々あります!
注意して手作業で修正している筈ですが、必ず、code
機能で表示して居る物を正しいとして確認して下さい
取り敢えず、今日(2月6)の講義はココまでお疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月7継続講義
(3-4-9)BNFと構文グラフ
読者様の中には、BNF≪バッカス・ナウア記法≫と記載し
てもピンと来無い人が、多いでしょうが「メタ」の一種と
言ったら少しは喰いついて呉れるかな!
最近、「メタバース」とかメタ始まりの単語が跋扈している
からですが「メタ」とは「超」の意味があります!
ココでは、「メタ文法」としての「BNF」を使用します!
因みにコンピュータ系で「メタ文法」とは、コンピュータ
言語の文法を説明する為の言語記述「構文」の説明に使われ
る物です?!
そして元々のBNFは、英語文化圏で開発された物で
角括弧「<>」でアルファベットの列を囲った「<>」で定義
した名称を示しす?!
例えば、「<degit>」数字を意味、そして、垂直棒「|」で
選択≪何々と「|」で区切った物を並べて内の一つを選ぶ≫
とか、「::=」で「左辺::=右辺」で左辺の
「<>」で定義した名称を右辺の構造と同じ例えば、
「<degit>::=0|1|2|3|4|5|6|7|8|9」と
「数字とは、0123456789の何れか」と定義した事に成り
ます?!
所で
これらは、「半角文字コード(ASCIIコード)」で事足りる
英字文化圏で開発された物です?!
幸い?我々日本人は、「全角文字コード(漢字・ひらがな・
カタカナ)等や(。、)等の独自特殊記号」を使う文字
表現をITとしても使用する文化圏に属します!
だから、半角文字コードで表される「画像処理装置操作
コマンド」の定義を全角文字コードを使用する事で明確に
分離・区別できるとして「拡張BNF」記法の一種で
ADS社画像処理装置専用に開発した専用拡張BNFで
記載した定義に従って「半角文字」をコマンドと見なして
解釈する為の関数群が、今回説明した
関数「getStringLineToInt()」やこれから解説する
関数「getStringLineToReal()」です!
そしてこれ等のBNF記法を記載した参考資料として
解説『参考:ADS社画像処理装置』に「PIPL」解説書
をPDF形式のファイルがダウンロード出来る様にしていま
す!
その
「PIPL」解説書の中に専用拡張BNFで記載した定義が
記載しています
私としては、BNFは理解を深める≪分かり易くする為≫に
紹介した心算ですが、
「文字の塊」なので???の方(お○○系)にも視覚的に
「構文グラフ」の説明もシヨウと思いました!
取り敢えず、参考にして下さい!
コレラを解析する為の関数が、関数「stoi();」とか
関数「getStringLineToInt()」です!
私としては、BNFは理解を深める≪分かり易くする為≫に
紹介した心算ですが、
「文字の塊」なので???の方(お○○系)にも視覚的に
「構文グラフ」の説明もシヨウと思いました!
今回は、関数「getStringLineToReal(
char*p,double&d);」:を扱います!
(3-4-10)関数「getStringLineToReal(
char*p,double&d);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
char* Support::getStringLineToReal(
char* p, // 行バッファー内ポインタ
double& d // データ:実数
){
int c; // 文字
int d1; // 整数(実数の大数部)
int d2; // 整数(実数の小数部)
int d3; // 整数(実数の指数数部)
int flagP; // プラス符号フラグ
int flagM1; // マイナス符号フラグ:全体
int flagM2; // マイナス符号フラグ:指数
flagP = FALSE; // フラグ初期化
flagM1 = FALSE; // フラグ初期化
flagM2 = FALSE; // フラグ初期化
p = skipSpaceComma( p ); // 空白文字と「,」の読み飛ばし
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d1 ); // 整数値を取り出す
if( *p == '.' ){ // 「.」ならば
p++; // Skip
} //
break; // Loop終了
}else if( c == '-' ){ // 「-」ならば
if( flagM1 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM1 = TRUE; // フラグをONする
p++; // Skip
}else if( c == '.' ){ // 「.」ならば
d1 = 0; // 大数部=0とし
p++; // 一つ進め
break; // Loop終了
}else{ // 上記以外ならば
return( 0 ); // 数値がないと見なし左記を返す
} //
} //
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d2 ); // 整数値を取り出す
p = skipSpace( p ); // 空白文字読み飛ばし
if( *p == 'e' || *p == 'E' ){ // 「e」「E」ならば
p++; // Skip
}else{ // 上記以外なら
d = makeReal( d1, d2, 0 ); // 実数値を作成
if( flagM1 ){ // 符号反転時
d = -d; // 符号反転
} //
return( p ); // 現ポインタを返す
} //
break; // Loop終了
}else if( c == 'e' || c == 'E' ){ // 「e」「E」ならば
d2 = 0; // 小数部=0とし
p++; // 一つ進め
break; // Loop終了
}else{ // 上記以外ならば
if( flagM1 ){ // 符号反転時
d = -(double)d1; // 大数部の符号反転を数値とする
}else{ // 正の値の時
d = (double)d1; // 大数部を数値とする
} //
return( p ); // 現ポインタを返す
} //
} //
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d3 ); // 整数値を取り出す
break; // Loop終了
}else if( c == '+' ){ // 「+」ならば
if( flagP || flagM2 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagP = TRUE; // フラグをONする
p++; // 一つ進め
}else if( c == '-' ){ // 「+」ならば
if( flagP || flagM2 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM2 = TRUE; // フラグをONする
p++; // 一つ進め
}else{ // 上記以外ならば
return( 0 ); // 書式違反を返す
} //
} //
if( flagM2 ){ // 指数部符号反転ならば
d3 = -d3; // 符号反転
} //
d = makeReal( d1, d2, d3 ); // 実数値を作成
if( flagM1 ){ // 符号反転時
d = -d; // 符号反転
} //
return( p ); // 現ポインタを返す
}
(3-4-10-A)関数名「getStringLineToReal();」の説明
「get」は、英単語の意味通り「得る」と言う意味です!
「String」は、「文字列」を意味します!
「StringLine」で文字列行(改行までの文字列)を
意味します!
「To」は、英語ソノママ「何々から何処其処へ」を意味し
ます、そして
「Real」は、実数値を示します!
ここで実数は小数点「.」や指数部を示す「e」及び
「E」を含んだ浮動小数点フォーマットの文字列を意味し
ます!
CPU内部では、内部で処理できる形のC言語での
「float」型や「double」型に成ります!
この関数は文字列の浮動小数点フォーマットを「float」型
や「double」型に変換する物です!
この関数のリターン値は、「char*」とポインタの値で処理
した文字列の場所を示します!
(3-4-10-B)関数「getStringLineToReal();」の【仮引数】説明
char* Support::getStringLineToReal(
char* p, // 行バッファー内ポインタ
double& d // データ:実数
){
「char*p,」は、処理する文字列へのポインタです!
「double&d」は、結果としての整数値を返す仮引数です!
勿論、対応する実引数は、メモリーとして実体が有る事が
必要です!
(3-4-10-C)関数「getStringLineToReal();」の【ローカル変数】説明
){
int c; // 文字
int d1; // 整数(実数の大数部)
int d2; // 整数(実数の小数部)
int d3; // 整数(実数の指数数部)
int flagP; // プラス符号フラグ
int flagM1; // マイナス符号フラグ:全体
int flagM2; // マイナス符号フラグ:指数
int c;//文字
int d1;//整数(実数の大数部)
int d2;//整数(実数の小数部)
int d3;//整数(実数の指数数部)
intf lagP;//プラス符号フラグ
int flagM1;//マイナス符号フラグ:全体
int flagM2;//マイナス符号フラグ:指数
「int c;」は、アルゴリズムで処理(主に判断する為)する
為に仮引数
「char*p」から一文字分取り出した文字コードの置き場
です!
「int d1;」は、実数のフォーマット
「aaaa.bbbEcc」⇒具体的には、
「123.45E01」=「123.45e01」と成る事の中で
「aaaa」と小数点より左側の部分の整数値データ=
先ほど例だと「123」が格納される所ですだから
コメントで「実数の大数部」と記載!
「intd2」は、「bbb」と小数点より右側の部分の整数値
データ=先ほど例だと「45」が格納される所ですだから
コメントで「実数の小数部」と記載!
「int d3」は、「cc」と「E指数を示す区切り」より
右側の部分の整数値データ=先ほど例だと「01」が格納
される所ですだからコメントで「実数の指数部」と記載!
「int flagP;」は、文字列の中に「+」記号が含まれる
場合の処理用です!
「int flagM1」は、文字列の中に「-」記号が含まれる
場合の処理用ですが数値全体が「正の値」/「負の値」に
成る事の制御用です!
「int flagM2」は、文字列の中に「-」記号が含まれる
場合の処理用ですが、このフラグ(旗)は、指数部が
「正の値」/「負の値」に成る事の制御用です!
int c; // 文字
int flagM1; // マイナス符号フラグ:全体
「int c;」は、アルゴリズムで処理(主に判断する為)する
為に仮引数
「char*p」から一文字分取り出した文字コードの置き場
です!
「int flagM1;」は、整数値の±関係のフラグ(旗)です!
私のプログラムでは、この「flag」フラグ(旗)と言う
表現と「st」ステータスフラグを示す物とかを使用します!
微妙に言い回しが異なるのは、作成時の気分です!
ニュアンスも含めて読み解くときに考えたら良いかな??
(3-4-10-D)関数「getStringLineToReal();」の【アルゴリズム】説明
flagP = FALSE; // フラグ初期化
flagM1 = FALSE; // フラグ初期化
flagM2 = FALSE; // フラグ初期化
p = skipSpaceComma( p ); // 空白文字と「,」の読み飛ばし
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d1 ); // 整数値を取り出す
if( *p == '.' ){ // 「.」ならば
p++; // Skip
} //
break; // Loop終了
}else if( c == '-' ){ // 「-」ならば
if( flagM1 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM1 = TRUE; // フラグをONする
p++; // Skip
}else if( c == '.' ){ // 「.」ならば
d1 = 0; // 大数部=0とし
p++; // 一つ進め
break; // Loop終了
}else{ // 上記以外ならば
return( 0 ); // 数値がないと見なし左記を返す
} //
} //
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d2 ); // 整数値を取り出す
p = skipSpace( p ); // 空白文字読み飛ばし
if( *p == 'e' || *p == 'E' ){ // 「e」「E」ならば
p++; // Skip
}else{ // 上記以外なら
d = makeReal( d1, d2, 0 ); // 実数値を作成
if( flagM1 ){ // 符号反転時
d = -d; // 符号反転
} //
return( p ); // 現ポインタを返す
} //
break; // Loop終了
}else if( c == 'e' || c == 'E' ){ // 「e」「E」ならば
d2 = 0; // 小数部=0とし
p++; // 一つ進め
break; // Loop終了
}else{ // 上記以外ならば
if( flagM1 ){ // 符号反転時
d = -(double)d1; // 大数部の符号反転を数値とする
}else{ // 正の値の時
d = (double)d1; // 大数部を数値とする
} //
return( p ); // 現ポインタを返す
} //
} //
for(;;){ // 以下を繰り返し
p = skipSpace( p ); // 空白文字読み飛ばし
c = *p; // 一文字取り出し
if( c >= '0' && c <= '9' ){ // 「0」~「9」の間なら
p = stoi( p, d3 ); // 整数値を取り出す
break; // Loop終了
}else if( c == '+' ){ // 「+」ならば
if( flagP || flagM2 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagP = TRUE; // フラグをONする
p++; // 一つ進め
}else if( c == '-' ){ // 「+」ならば
if( flagP || flagM2 ){ // 既にフラグがONの場合
return( 0 ); // 書式違反を返す
} //
flagM2 = TRUE; // フラグをONする
p++; // 一つ進め
}else{ // 上記以外ならば
return( 0 ); // 書式違反を返す
} //
} //
if( flagM2 ){ // 指数部符号反転ならば
d3 = -d3; // 符号反転
} //
d = makeReal( d1, d2, d3 ); // 実数値を作成
if( flagM1 ){ // 符号反転時
d = -d; // 符号反転
} //
return( p ); // 現ポインタを返す
}
「flagP=FALSE;flagM1=FALSE;flagM2=FALSE;」は、
フラグ(旗)の初期化このフラグ(旗)の操作に関しては、
前に解説した「(3-4-8)関数
「getStringLineToInt(char*p,int&d);」の説明」とコレは
整数値データの件なので少しは理解し易いのでコレを理解
してから理解して下さい!
「p=skipSpaceComma(p);」は、
「(3-4-7)関数skipSpaceComma()」
で説明した「空白文字とコンマを読み飛ばす」処理で
ポインタ「p」を操作します!
「for(;;){・・ループの中身・・ }」は、無限ループ≪
終了条件はループの中で判断≫の記載法です!
データの中身が分からない処理を行う場合、良く使用する
パターンですから覚えてね?!
その「ループの中身」は、これから解説します!
「p=skipSpace(p);」は、
「(3-4-6)関数skipSpace()」で説明した
「空白文字を読み飛ばす」処理でポインタ「p」を操作
します!
「c=*p;」は、一文字分、データを取り出します!注意して
欲しいのは、単純に「*p」とポインタの値は進行も後退も
シナイ事です!
「if(c>='0'&&c<='9'){・・成立中身A・・}else
if(c=='-'){・・成立中身B・・・・}else if(c=='.'){
成立中身C・・}else{・・不成立中身・・}」は、先ず、
「成立中身A」でこの成立する条件「c>='0'&&c<='9'」は、
C言語に馴染んでいる読者様には「文字コード
【'0'=0x30】から【'9'=0x39】」の範囲の文字
コードなら成立と言う事は理解して頂いていると思います!
中身の「p=stoi(p,d);」は、【(3-4-5-A)関数名
「stoi();」の説明】で「文字列を整数値に変換する関数で
す!」を使用して仮引数ポインタ「p」の中の文字列を操作
しローカル変数「int&d1」へ整数値をセットします!
これは、(実数の大数部)です!
とココまで説明して居て「日本語で解説する」事の愚かさが
身に染みてシマッタ?!
作者の私がソウなら、読まされる読者様はヨッポド寛容な
方で無いと難しいと思えます!
上記の構文グラフで示した実数の「大数部」・
「小数点以下部」・「指数部」と整数型の「int d1;」・
「int d2;」・「int d3;」に整数の値が算出格納される事は
理解して頂けると思います!
この関数の終わりの部分
d = makeReal( d1, d2, d3 ); // 実数値を作成
if( flagM1 ){ // 符号反転時
d = -d; // 符号反転
} //
return( p ); // 現ポインタを返す
}
で「d = makeReal( d1, d2, d3 )」で実数値を算出する
関数「makeReal」を使用して倍精度「double型」の数値を
作成して仮引数「d」で値を答えとして実引数に返している
事は、理解して居ると思います!
ワザワザ数式で無く、専用の関数を用意して居るのは、
CPUのハードウェア演算の中で一番、処理時間が掛るのは
【整数値⇔浮動小数点数値】への変換です!最新のCPUで
も基本的には、一番、処理時間が掛る部分ですから、
昭和に作成されたオリジナルな時は、尚更なので少しでも
高速に考えたからです!最も関数「makeReal()」で解説しま
すので読んで下さい!
最後の最後「if(flagM1){d=-d;}」で全体の±符号を
調整します!
★備考★
解説『解説クラスTypeArray』でも記載したが、
noteエディタの変な特性でコピペした文章の半角「*」
が消されたり、空白「 」が消される事が多々あります!
注意して手作業で修正している筈ですが、必ず、code
機能で表示して居る物を正しいとして確認して下さい
取り敢えず、今日(2月7)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月8継続講義
今回は、
関数「char append(charstr1,const charstr2);」及び
関数「char strTime(charstr,int h,int mi,int s,
int sw,int mode);」及び、
関数「char strDate(char*str,int y,int mo,int d,
int sw,int mode);」を扱います!
(3-4-11)関数「char*append(char*str1,const char*str2);」の説明
この関数の本体は、ファイル「Support.cpp」に格納されて
居て次の様に
char* Support::append(
char *str1, // つながられる文字列領域
const char *str2 // つなげる文字列
){
char c; // 文字
if( str2 != 0 ){ // 有効なつなげる文字列で
while( ( c = *str2++ ) != '\0' ){ // 文字列2が空になるまで
*str1++ = c; // 文字を文字列1に
} // 追加する
} //
*str1 = '\0'; // 終了文字を与える
return( str1 ); // 現ポインタを返す
}
(3-4-11-A)関数名「append();」の説明
「append」は、英単語の意味通り「追加」と言う意味です!
この関数のリターン値は、「char*」とポインタの値で処理
した文字列の場所を示します!
(3-4-11-B)関数「append();」の【仮引数】説明
char* Support::append(
char *str1, // つながられる文字列領域
const char *str2 // つなげる文字列
){
「char *str1,」は、コメント「つながられる文字列領域」
と記載して在る様にコレに対応する実引数の文字列領域が、
結果として繋がった文字列が格納されます!
「const char *str2」は、コメント「つなげる文字列」と
記載して在る様にこの文字列を「*str1」の尻尾に繋げ
ます!
(3-4-11-C)関数「append();」の【ローカル変数】説明
char c; // 文字
「char c;」は、整数型ですが「int」で無く「char」を
使用している事に注意してください!
単に一時的に置いて置くだけの物なので出来るだけ型変換が
色々なCPU(翻訳するコンパイラ)で起こるコードが起き
て無駄な処理コードが発生する事を避ける為です!
(3-4-11-D)関数「append();」の【アルゴリズム】説明
if( str2 != 0 ){ // 有効なつなげる文字列で
while( ( c = *str2++ ) != '\0' ){ // 文字列2が空になるまで
*str1++ = c; // 文字を文字列1に
} // 追加する
} //
*str1 = '\0'; // 終了文字を与える
return( str1 ); // 現ポインタを返す
}
「if(str2!=0){・・中身・・}」は、追加する文字列が
空ポインタで無い「つまり文字列が存在」する場合は
「・・中身・・」を実行します!
中身は、「while((c=*str2++)!='\0'){*str1++=c;}」と
「while(・・条件・・)!='\0'){・・本文・・}」と
ループし、条件「(c=*str2++)!='\0'」と一文字取得
「c=*str2++」でローカル変数「c」に値を入れ終了文字で
無ければループする!
本文「*str1++=c;」で一文字追加します!
最後の「*str1='\0';return(str1);」で結果の文字列最後
に終了文字を追加し、追加した後の文字列ポインタの値を
関数の値として返します!
(3-4-12)関数「char*strTime(
char*str,int h,int mi,int s,int sw,int mode);」の説明
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
char* Support::strTime(
char *str, // 変換された文字列
int h, // 時間データ(0~24)
int mi, // 分データ(0~60)
int s, // 秒データ(0~60)
int sw, // 0(省略時):空白,1:「0」詰め
int mode // -1 hhmmss
// 0(省略時) hh:mm:ss
){
if( mode == -1 ){ // hhmmss ならば
str = ltosx( (long)h, str, 2, sw ); // 時間を2桁変換
str = ltosx( (long)mi, str, 2, sw ); // 分間を2桁変換
str = ltosx( (long)s, str, 2, sw ); // 秒間を2桁変換
}else if( mode == 0 ){ // hh:mm:ss ならば
str = ltosx( (long)h, str, 2, sw ); // 時間を2桁変換
*str++ = ':'; // :
str = ltosx( (long)mi, str, 2, sw ); // 分間を2桁変換
*str++ = ':'; // :
str = ltosx( (long)s, str, 2, sw ); // 秒間を2桁変換
} //
*str = '\0'; // 終了文字
return( str ); // 現ポインタを返す
}
(3-4-12-A)関数名「strTime();」の説明
「str」は、「文字列」と言う意味です!
「Time」は、「時刻」と言う意味です!
詰り、「時刻を表す文字列を作成」と言う意味です!
(3-4-12-B)関数「strTime();」の【仮引数】説明
char* Support::strTime(
char *str, // 変換された文字列
int h, // 時間データ(0~24)
int mi, // 分データ(0~60)
int s, // 秒データ(0~60)
int sw, // 0(省略時):空白,1:「0」詰め
int mode // -1 hhmmss
// 0(省略時) hh:mm:ss
){
「char *str」は、対応する実引数の領域に結果としての
文字列を出力!
「int h」は、時間(Hour)を0~24を範囲に指定!
「int mi」は、分(Minutes)を0~60を範囲に
指定!
「int s」は、秒(Seconds)を0~60を範囲に
指定!
「int sw」は、自分秒を二桁ずつhhmmssと文字列
作成するのだが、数値が一桁の場合に「空白文字」・
「数字"0"」を選べるスイッチです!
「int mode」は、書式「hhmmss」と書式
「hh:mm:ss」とセパレータ(分離記号)に
コロン「':'」を入れる入れ無いを選択するモード!
(3-4-12-C)関数「strTime();」の【アルゴリズム】説明
){
if( mode == -1 ){ // hhmmss ならば
str = ltosx( (long)h, str, 2, sw ); // 時間を2桁変換
str = ltosx( (long)mi, str, 2, sw ); // 分間を2桁変換
str = ltosx( (long)s, str, 2, sw ); // 秒間を2桁変換
}else if( mode == 0 ){ // hh:mm:ss ならば
str = ltosx( (long)h, str, 2, sw ); // 時間を2桁変換
*str++ = ':'; // :
str = ltosx( (long)mi, str, 2, sw ); // 分間を2桁変換
*str++ = ':'; // :
str = ltosx( (long)s, str, 2, sw ); // 秒間を2桁変換
} //
*str = '\0'; // 終了文字
return( str ); // 現ポインタを返す
}
「if(mode==-1){・・成立1・・}else if(mode==0)
{・・成立2・・}」は、条件「mode==-1」の時、成立1の
ブロック処理、条件「mode==0」の時、成立2のブロック
処理を行います!
成立1は、「str=ltosx((long)h,str,2,sw);
str=ltosx((long)mi,str,2,sw);
str=ltosx((long)s,str,2,sw);」と関数「ltosx()」で
時分秒の値を2桁毎に作成し文字列連結した事が判る筈
です!
成立2は、「str=ltosx((long)h,str,2,sw);*str++=':';
str=ltosx((long)mi,str,2,sw);*str++=':';
str=ltosx((long)s,str,2,sw);」と成立1との違いは
「*str++=':';」と時分・分秒の切れ目に文字「':'」
文字列の中に入れて居る事が判る筈です!
最後に「*str='\0';return(str);」で文字列最後に
終了文字を加えて処理した最後のポインタの値を返します!
(3-4-13)関数「strDate()」の説明
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
char* Support::strDate(
char* str, // 変換された文字列
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d, // 日データ(1~31)
int sw, // 0(省略時):空白,1:「0」詰め
int mode // -1 yymmddwww
// 0(省略時) yy-mm-dd www
// 1 yymmdd
){
static const char* tbl[] = { // 曜日の文字列テーブル
"SUN", // 日曜日
"MON", // 月曜日
"TUE", // 火曜日
"WED", // 水曜日
"THU", // 木曜日
"FRI", // 金曜日
"SAT", // 土曜日
}; //
int w; // 曜日データ
if( mode == -1 ){ // yymmddwww ならば
w = calcWeek( y, mo, d ); // 曜日を算出
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
return( append( str, tbl[ w ] ) ); // 曜日文字列をつなげptrを返す
}else if( mode == 0 ){ // yy-mm-dd www ならば
w = calcWeek( y, mo, d ); // 曜日を算出
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
*str++ = '-'; // 「-」
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
*str++ = '-'; // 「-」
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
*str++ = ' '; // 空白
return( append( str, tbl[ w ] ) ); // 曜日文字列をつなげptrを返す
}else if( mode == 1 ){ // yymmdd ならば
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
return( str ); // 文字列最終ポインタを返す
} //
return( 0 ); // 空を返す
}
(3-4-13-A)関数名「strDate()」の説明
「str」は、「文字列」と言う意味です!
「Date」は、「日付」と言う意味です!
詰り、「Dateを表す文字列を作成」と言う意味です!
(3-4-13-B)関数「strDate()」の【仮引数】説明
char* Support::strDate(
char* str, // 変換された文字列
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d, // 日データ(1~31)
int sw, // 0(省略時):空白,1:「0」詰め
int mode // -1 yymmddwww
// 0(省略時) yy-mm-dd www
// 1 yymmdd
){
「char *str」は、対応する実引数の領域に結果としての
文字列を出力!
「int y,」は、西暦年(1~9999)を示す数値データ!
「int mo,」は、月(1~12)を示す数値データ!
「int d,」は、日(1~31)を示す数値データ!
「int sw,」は、年月日を二桁ずつyymmddと
文字列作成するのだが、数値が一桁の場合に「空白文字」・
「数字"0"」を選べるスイッチです!
「int mode」は、表示フォーマットを「sw=-1」で
yymmddWWWと最後の「www」は曜日文字列
(SUN、MON、TUE、WED、THU、FRI、SAT)です!
そして「sw=-1」でyy-mm-dd-WWWとセパレータ
(分離記号)に文字「'-'」を挿む事を示し、「sw=1」で
yymmddと曜日文字列無しを選択できるモードスイッチ
です!
(3-4-13-C)関数「strDate()」の【ローカル変数】説明
){
static const char* tbl[] = { // 曜日の文字列テーブル
"SUN", // 日曜日
"MON", // 月曜日
"TUE", // 火曜日
"WED", // 水曜日
"THU", // 木曜日
"FRI", // 金曜日
"SAT", // 土曜日
}; //
int w; // 曜日データ
「static const char*tbl[]={・・中身・・};」は、
「tbl」とテーブル(表)を意味する名称にして居ます!
良く使用する表変換(conversion table)の為の意味で
ココでは、「tbl[0]⇒"SUN"、tbl[1]⇒"MON"・・・
tbl[6]⇒"SAT"」と[添字]で選択可能な表を意味します!
「int w;」は、この表を引く添え字を一時的に置く変数
です!
(3-4-13-D)関数「strDate()」の【アルゴリズム】説明
if( mode == -1 ){ // yymmddwww ならば
w = calcWeek( y, mo, d ); // 曜日を算出
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
return( append( str, tbl[ w ] ) ); // 曜日文字列をつなげptrを返す
}else if( mode == 0 ){ // yy-mm-dd www ならば
w = calcWeek( y, mo, d ); // 曜日を算出
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
*str++ = '-'; // 「-」
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
*str++ = '-'; // 「-」
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
*str++ = ' '; // 空白
return( append( str, tbl[ w ] ) ); // 曜日文字列をつなげptrを返す
}else if( mode == 1 ){ // yymmdd ならば
str = ltosx( (long)(y % 100), str, 2, sw ); // 西暦を2桁変換
str = ltosx( (long)mo, str, 2, sw ); // 月を2桁変換
str = ltosx( (long)d, str, 2, sw ); // 日を2桁変換
return( str ); // 文字列最終ポインタを返す
} //
return( 0 ); // 空を返す
}
「if(mode==-1){・・成立1・・}else if(mode==0)
{・・成立2・・}else if(mode==1){・・成立3・・}」は、
仮引数の説明で既に説明した様に条件「mode=-1」で
成立1ブロック、条件「mode=0」で成立2ブロック、
条件「mode=1」で成立3ブロックを実行します!
「成立1ブロック」は、「w=calcWeek(y,mo,d);」で
関数「calcWeek()」で曜日(0=日曜・・6=土曜)と成る
値を年月日から算出します!
何故、ブロックの中に入れてif文の前に実行し無いかは
成立3ブロックで曜日を使用し無いからです!
「str=ltosx((long)(y%100),str,2,sw);」は、
西暦の下二桁を「y%100」で抜き取り2桁の文字列にした事
です!
「str=ltosx((long)mo,str,2,sw);
str=ltosx((long)d,str,2,sw);」は、月日の数値データを
文字列に変えた事は、理解いただけると思います!
「return(append(str,tbl[w]));」は、曜日の文字列を
「tbl[w]」で表引きした文字列を連結して文字列
ポインタを関数の値として返す事を示します!
「成立2ブロック」は、「成立1ブロック」の年月の間と
月日の間に文字「'-'」を挿み、月週名の間に空白文字挿み
文字列ポインタを関数の値として返す事を示します!
「成立3ブロック」は、セパレータ(分離文字)が無い
文字列を作成し、文字列ポインタを関数の値として返す事を
示します!
取り敢えず、今日(2月8)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月9継続講義
今回は、関数「int calcWeek(inty,int mo,int d);」及び
関数「int calcLenString(char*str);」及び
関数「int calcWeek(int y,int mo,int d);」を扱います!
関数「double makeReal(int d1,int d2,int exp);」を
扱います!
(3-4-14)関数「int calcWeek(inty,int mo,int d);」の説明
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
int Support::calcWeek(
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d // 日データ(1~31)
){
static const short tbl_mo1[] = { // 平年の月当たりの日数
0, 31, 59, 90, 120, 151, // 1, 2, 3, 4, 5, 6
181, 212, 243, 273, 304, 334 // 7, 8, 9, 10, 11, 12
}; //
static const short tbl_mo2[] = { // 閏年の月当たりの日数
0, 31, 60, 91, 121, 152, // 1, 2, 3, 4, 5, 6
182, 213, 244, 274, 305, 335 // 7, 8, 9, 10, 11, 12
}; //
int y0; // 一つ前の年数
int md; // 月数当たりの日数
int ct; // ずれ日数カウンタ
if( y < 1 ){ // 西暦がBCならば
return( 0 ); // 0:日曜日を返す
} //
if( ( ( y % 4 == 0 ) && ( y % 100 != 0 ) ) // 閏年なら
|| ( y % 400 == 0 ) ){ //
md = tbl_mo2[ mo - 1 ]; // 閏年で月当たり日数
}else{ // 平年なら
md = tbl_mo1[ mo - 1 ]; // 平年で月当たり日数
} // を算出
y0 = y - 1; // 一つ前の年数算出
ct = y; // 0年からの年数をSET
ct += (y0 / 4) - (y0 / 100) + (y0 / 400); // 昨年までの閏年数加算
ct += md; // 月当たりの日数を加算
ct += d; // 日数を加算
ct -= 1; // 補正
return( ct % 7 ); // 曜日の数に整形し返す
}
(3-4-14-A)関数名「calcWeek」の説明
「calc」は、計算です!
「Week」は、「週」で曜日≪0:日、1:月、2:火、
3:水、4:木、5:金、6:土≫コードを算出する事を
意味します!
(3-4-14-B)関数「calcWeek()」の【仮引数】説明
int Support::calcWeek(
int y, // 西暦年データ(1~9999)
int mo, // 月データ(1~12)
int d // 日データ(1~31)
){
「int y,」は、西暦年(1~9999)を指定します!
「int mo,」は、月(1~12)を指定します!
「int d」は、日(1~12)を指定します!
(3-4-14-C)関数「calcWeek()」の【ローカル変数】説明
static const short tbl_mo1[] = {// 平年の月当たりの日数
0, 31, 59, 90, 120, 151,// 1, 2, 3, 4, 5, 6
181, 212, 243, 273, 304, 334 // 7, 8, 9, 10, 11, 12
}; //
static const short tbl_mo2[] = {// 閏年の月当たりの日数
0, 31, 60, 91, 121, 152,// 1, 2, 3, 4, 5, 6
182, 213, 244, 274, 305, 335 // 7, 8, 9, 10, 11, 12
}; //
int y0; // 一つ前の年数
int md; // 月数当たりの日数
int ct; // ずれ日数カウンタ
「static const short tbl_mo1[] = {・・中身・・};」は
「tbl」とテーブル(表)を意味する名称にして居ます!
良く使用する表変換(conversion table)の為の意味で
ココでは、
「tbl_mo1[0]⇒1月が始まった時の年初からの日数、
[1]⇒2月が始まった時の年初からの日数、[2]⇒3月が
始まった時の年初からの日数・・・[11]⇒12月が始まった
時の年初からの日数を意味する変換テーブルです!
コレは、平年用です!
「static const short tbl_mo2[] = {・・中身・・};」
は、「tbl」とテーブル(表)を意味する名称にして
居ます!良く使用する表変換(conversion table)の為の
意味でココでは、「tbl_mo1[0]⇒1月が始まった時の年初
からの日数、[1]⇒2月が始まった時の年初からの日数、
[2]⇒3月が始まった時の年初からの日数・・・[11]⇒
12月が始まった時の年初からの日数を意味する変換
テーブルです!コレは、閏年用です!
「int y0;」は、コメント「一つ前の年数」⇒西暦0年から
の経過年数で閏年が何回有ったかを算出する為の物です!
「int md;」は、コメント「月数当たりの日数」⇒年初から
の日数!
「int ct;」は、コメント「ずれ日数カウンタ」
原点(西暦0年1月1日)からの日数計数用!
(3-4-14-D)関数「calcWeek()」の【アルゴリズム】説明
if( y < 1 ){ // 西暦がBCならば
return( 0 ); // 0:日曜日を返す
} //
if( ( ( y % 4 == 0 ) && ( y % 100 != 0 ) ) // 閏年なら
|| ( y % 400 == 0 ) ){ //
md = tbl_mo2[ mo - 1 ]; // 閏年で月当たり日数
}else{ // 平年なら
md = tbl_mo1[ mo - 1 ]; // 平年で月当たり日数
} // を算出
y0 = y - 1; // 一つ前の年数算出
ct = y; // 0年からの年数をSET
ct += (y0 / 4) - (y0 / 100) + (y0 / 400); // 昨年までの閏年数加算
ct += md; // 月当たりの日数を加算
ct += d; // 日数を加算
ct -= 1; // 補正
return( ct % 7 ); // 曜日の数に整形し返す
}
「if(y<1){return(0);}」は、西暦年が所為の数
「所謂BC(ビフォーアークリスチャン)とイエスキリスト
が生まれた後の西暦年が対象」で取り敢えず、
違えば「0:日曜日」を示す値を返す!
「if(((y%4==0)&&(y%100!=0))||(y%400==0)){
・・閏年処理・・}else{・・平年処理・・}」は、
条件「((y%4==0)&&(y%100!=0))||(y%400==0)」で
西暦年から平年と閏年を判別≪400年に一回・4年に一回
で且つ100で割り切れる年は除く≫し、
それぞれの処理を行います!
・・閏年処理・・は、「md=tbl_mo2[mo-1];」で
変数「md」に閏年での年初から先月迄の経過日数算出!
・・平年処理・・は、「md=tbl_mo1[mo-1];」で
変数「md」に平年での年初から先月迄の経過日数算出!
「y0=y-1;ct=y;」は、変数「y0」に前年の値をセット≪
後で3回も使うので途中の値保存用に使用≫と変数「ct」に
原点(西暦0年1月1日)からの日数計数用最初に加える
セット!
「ct+=(y0/4)-(y0/100)+(y0/400);」は、閏年の数を加える
事です!
「ct+=md;ct+=d;」は、年初からの日数を加える事と日数を
加える事です!
「ct-=1;」は、カレンダーに合したズレ補正です!
「return(ct%7);」は、「0~6」の曜日数値データを
算出し関数の結果として返します!
(3-4-15)関数「int calcLenString(char*str);」の説明
int Support::calcLenString(
char* str // 文字列
){
int n; // 有効個数
n = 0; // 有効個数初期化
while( *str != 0 ){ // 文字列有効な間
str++; // 次へ進め
n++; // カウントアップ
} //
return( n ); // 有効個数を返す
}
(3-4-15-A)関数名「calcLenString()」の説明
「calc」は、計算、
「Len」は、長さ、
「String」は、文字列を示し「仮引数の文字列の有効長さ
(終了文字「'\0'」コード)」
(3-4-15-B)関数「calcLenString()」の【仮引数】説明
int Support::calcLenString(
char* str // 文字列
){
「char *str」は、有効文字数を計数する文字列!
(3-4-15-C)関数「calcLenString()」の【ローカル変数】説明
int n; // 有効個数
「int n;」は、有効文字を数えるカウンターです!
(3-4-15-D)関数「calcLenString()」の【アルゴリズム】説明
n = 0; // 有効個数初期化
while( *str != 0 ){ // 文字列有効な間
str++; // 次へ進め
n++; // カウントアップ
} //
return( n ); // 有効個数を返す
}
「n=0;」は、計数カウンタの初期化!
「while(*str!=0){・・中身・・}」は、ループ条件
「*str!=0」とポインタ「*str」で示される文字コードの
値が0で無い(終了文字以外)で中身を繰り返します。
・・中身・・は、「str++;n++;」とポインタを進め、
計数カウンタを計数!
最後に「return(n);」は、関数の結果として計数した値を
返す!
(3-4-16)関数「double makeReal(int d1,int d2,int exp);」の説明
double Support::makeReal(
int d1, // 仮数(大数)
int d2, // 仮数(小数)
int exp // 指数:-40 ~ +49
){
static const double tbl[] = { // 指数部TABLE
0.0, 1.0e-39, 1.0e-38, 1.0e-37, 1.0e-36, // 0.. 4
1.0e-35, 1.0e-34, 1.0e-33, 1.0e-32, 1.0e-31, // 5.. 9
1.0e-30, 1.0e-29, 1.0e-28, 1.0e-27, 1.0e-26, // 11..14
1.0e-25, 1.0e-24, 1.0e-23, 1.0e-22, 1.0e-21, // 15..19
1.0e-20, 1.0e-19, 1.0e-18, 1.0e-17, 1.0e-16, // 21..24
1.0e-15, 1.0e-14, 1.0e-13, 1.0e-12, 1.0e-11, // 25..29
1.0e-10, 1.0e-9, 1.0e-8, 1.0e-7, 1.0e-6, // 31..34
1.0e-5, 1.0e-4, 1.0e-3, 1.0e-2, 1.0e-1, // 35..39
1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, // 40..44
1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, // 45..49
1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, // 50..54
1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, // 55..59
1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, // 60..64
1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29, // 65..69
1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, // 70..74
1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39, // 75..79
}; //
double f; // 浮動小数データ
int i; // カウンタ
if( exp < -40 || exp > 49 ){ // 指数部が範囲外
return( 0.0 ); // なら、0.0 を返す
} //
for( i = 0; i <= 11; i++ ){ //
if( (double)d2 < tbl[i+40] ){ //
break; //
} //
} //
f = (double)d1 * tbl[i+40] + (double)d2; // 仮数部をSETし
exp -= i; // 指数部を補正
if( exp > 39 ){ // 指数部が 39超なら
f *= 1.0e-10; // 10桁分補正し
exp -= 10; //
} //
return( f * tbl[ 40 + exp ] ); // 実数値を返す
}
(3-4-16-A)関数名「makeReal」の説明
「make」は、作成するで「Real」は、実数値を示します!
3個の整数≪仮数部の大数=小数点より左側、仮数部の
小数=小数点より右側、及び、指数部≫で示した
浮動小数点数を作成します!
(3-4-16-B)関数「makeReal()」の【仮引数】説明
double Support::makeReal(
int d1, // 仮数(大数)
int d2, // 仮数(小数)
int exp // 指数:-40 ~ +49
){
「int d1,」は、仮数部の大数=小数点より左側の値
「int d2,」は、仮数部の小数=小数点より右側の値
「int exp」は、指数部で範囲「-40~+49」
(3-4-16-C)関数「makeReal()」の【ローカル変数】説明
){
static const double tbl[] = { // 指数部TABLE
0.0, 1.0e-39, 1.0e-38, 1.0e-37, 1.0e-36, // 0.. 4
1.0e-35, 1.0e-34, 1.0e-33, 1.0e-32, 1.0e-31, // 5.. 9
1.0e-30, 1.0e-29, 1.0e-28, 1.0e-27, 1.0e-26, // 11..14
1.0e-25, 1.0e-24, 1.0e-23, 1.0e-22, 1.0e-21, // 15..19
1.0e-20, 1.0e-19, 1.0e-18, 1.0e-17, 1.0e-16, // 21..24
1.0e-15, 1.0e-14, 1.0e-13, 1.0e-12, 1.0e-11, // 25..29
1.0e-10, 1.0e-9, 1.0e-8, 1.0e-7, 1.0e-6, // 31..34
1.0e-5, 1.0e-4, 1.0e-3, 1.0e-2, 1.0e-1, // 35..39
1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, // 40..44
1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, // 45..49
1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, // 50..54
1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, // 55..59
1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, // 60..64
1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29, // 65..69
1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, // 70..74
1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39, // 75..79
}; //
double f; // 浮動小数データ
int i; // カウンタ
「static const double tbl[]={・・中身・・};」は、
整数型の仮引数「int exp」を倍精度浮動小数点数
(double)型へ変換する定数テーブルです!
「double f」は、倍精度浮動小数点数型の途中数値置き場
です!
「int i;」は、仮引数「int d2,」仮数部の小数の桁数が
多い時の為の対処するカウンターです!
(3-4-16-D)関数「makeReal()」の【アルゴリズム】説明
if( exp < -40 || exp > 49 ){ // 指数部が範囲外
return( 0.0 ); // なら、0.0 を返す
} //
for( i = 0; i <= 11; i++ ){ //
if( (double)d2 < tbl[i+40] ){ //
break; //
} //
} //
f = (double)d1 * tbl[i+40] + (double)d2; // 仮数部をSETし
exp -= i; // 指数部を補正
if( exp > 39 ){ // 指数部が 39超なら
f *= 1.0e-10; // 10桁分補正し
exp -= 10; //
} //
return( f * tbl[ 40 + exp ] ); // 実数値を返す
}
「if(exp<-40||exp>49){return(0.0);}」は、指数部の
範囲検査です⇒範囲外なら「return(0.0);」を結果に
します!
「for(i=0;i<=11;i++)
{if((double)d2<tbl[i+40]){break;}}」は、小数部(少数点の
右側)の桁数を倍精度浮動小数点数として矛盾無い様に「(double)d2<tbl[i+40]」比較して桁数を数えるルーチンです!
「f=(double)d1tbl[i+40]+(double)d2;」は、整数の
大数(小数点の左側)・整数の小数(小数点の右側)を
浮動小数点数の仮数に変換する式です!
「exp-=i;if(exp>39){f=1.0e-10;exp-=10;}」は、
指数部の数の補正です!
「return(f*tbl[40+exp]);」は、指数部を計算して関数の
結果として倍精度浮動小数点数の実数値を返します!
☆注意☆現在のCPUに詳しい方は、CPUの機械演算で
一番、処理時間が掛かるのは、整数型⇔浮動小数点数型の
変換≪C言語の記述的にはキャスト演算とか、自動で
デフォルト変換≫だとは、ご存じと思います!
そして
深読みした読者様の中には、この「makeReal()」は、
型変換を起こさ無い様にして速度的に有利にする関数では
無いかと思ったが、中に「(double)d2」とキャストしている
部分が有り、何でと訝しく思ったと思います!
「深読み」通り、当初は、型変換を起こさ無い様に
ソースコードを作成していたのですが、仮数部の桁数が
整数で10桁程度しか、32ビット整数の中では、
出来無かったので仕方が無く、コンナ本来、型変換を使わず
に高速に倍精度浮動小数点数を作成しようとした残滓ですが
今なら「long long」とか「int_64」とかと64ビット整数
が使える処理系が出回っている筈ですので宿題として、
型変換を完璧に起こさない「makeReal()」を作成して見て
下さい!
☆更に注意☆ココで解説した物の多くは、
C言語標準関数「printf()系列」で出来る物をワザワザ
作成しているかと言うと、元々組み込み用の機器に使用
するので作成当時使用していた半導体ROM(リード・
オンリー・メモリ)の中に格納するプログラムサイズを
小さくする為に「printf()系列」を組み込むとサイズが
大きく成るから必要な物だけ作成した心算です!
取り敢えず、今日(2月9)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月10継続講義
今回は、関数「dtos(double data,char*str);」と
裸のCPU向け関数を説明し、次から画像処理に直接関わ
る高速動作させる為の関数群の解説を始めます!
(3-4-17)関数「dtos(double data,char*str);」の説明
void Support::dtos(
double data, // 浮動小数数値データ
char *str // 変換された文字列
){
char *p; // 文字列ポインタ
double a; // データの絶対値
a = ( data < 0 ) ? -data : data; // 絶対値を求める
if( a == 0.0 // 0.0か
|| a >= 0.01 && a <= 10000000.0 ){ // 0.01~10000000.0の時
sprintf( str, " %f", data ); // 小数点形式に変換
for( p = str; *p != 0; p++ ){ // 文字列の最後をサーチ
; //
} //
while( *--p == '0' ){ // 最後の文字が「0」の
; // 場合は、「0」分戻す
} //
if( *p == '.' ){ // 小数点まで「0」分戻せ
p++; // ば、一つ進め
} //
*(p+1) = 0; // 終了文字を書き込む
}else{ // 上記範囲外なら
sprintf( str, " %e", data ); // 指数表示に変換
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-17-A)関数名「dtos」の説明
「d」は、「double型」の省略です!
「to」は、目的地や方向を表す英単語「to」です!
「s」は、「string」文字列の省略を意味します!
(3-4-17-B)関数「dtos()」の【仮引数】説明
void Support::dtos(
double data, // 浮動小数数値データ
char *str // 変換された文字列
){
「double data,」は、倍精度浮動小数点数型の
数値データです!
「char*str 」は、関数の結果として対応する実引数の
領域に十進法数字列の少数点表現とか、浮動小数点数
フォーマットの数値表現に変換した文字列を格納します!
(3-4-17-C)関数「dtos()」の【ローカル変数】説明
){
char *p; // 文字列ポインタ
double a; // データの絶対値
「char *p;」は、作業用の文字列ポインタです!
「double a; 」は、仮引数「double data」の絶対値です!
(3-4-17-D)関数「dtos()」の【アルゴリズム】説明
a = ( data < 0 ) ? -data : data; // 絶対値を求める
if( a == 0.0 // 0.0か
|| a >= 0.01 && a <= 10000000.0 ){ // 0.01~10000000.0の時
sprintf( str, " %f", data ); // 小数点形式に変換
for( p = str; *p != 0; p++ ){ // 文字列の最後をサーチ
; //
} //
while( *--p == '0' ){ // 最後の文字が「0」の
; // 場合は、「0」分戻す
} //
if( *p == '.' ){ // 小数点まで「0」分戻せ
p++; // ば、一つ進め
} //
*(p+1) = 0; // 終了文字を書き込む
}else{ // 上記範囲外なら
sprintf( str, " %e", data ); // 指数表示に変換
} //
}
「a=(data<0)?-data:data;」は、
仮引数「double data」の絶対値を変数「a」に格納!
「if(a==0.0||a>=0.01&&a<=10000000.0)
{・・中身1・・}else{・・中身2・・」は、
条件「a==0.0||a>=0.01&&a<=10000000.0」で絶対値が
「0.0」及び「0.01・・10000000.0」の範囲ならば、
中身1をそれ以外は、中身2のブロックを実行します!
中身1ブロックは、「sprintf(str,"%f",data);」と
C言語標準の「sprintf()」で「"%f"」詰り単純小数点
フォーマット変換し、
「for(p=str;p!=0;p++){;}」でポインタを終了文字まで
進め、
「while(--p=='0'){;}」で最後に有るかも知れない
文字「'0'」を無駄として判別、
「if(p=='.'){p++;}」で小数点「'.'」が最後なら
ポインタを一つ進め、
「(p+1)=0;」で有効な文字の後に終了文字コードを付け
る!
詰り、ここのブロックでは、小数点フォーマットで文字列を
作成!
中身2のブロックは、「sprintf(str,"%f",data);」と
C言語標準の「sprintf()」で「"%e"」詰り指数部付き
小数点フォーマット変換し、それを結果文字列を作成した
事にします!
☆注意☆元々、ADS社の画像処理装置用に半導体ROMに
収まる為にサイズを小さくする目的で嵩張る「printf()系」
の関数を使用シナイ様に考えたが、十分なサイズのROMが
使用出来たのでC言語標準の「printf()系」が使用出来る
様に成ったのでココでは、「sprintf()」を使用して居ま
す!
このライブラリの元の関数を作成した時は、趣味で作成し
たので無く締め切りが有る、業務として作成したので
無駄な作業としての「sprintf()」代替は断念したのだ?!
読者様には、C言語の勉強として「sprintf()」代替に
挑んで見るのも良いかも知れません!
ここから、画像処理に直接関わる高速動作させる為の
関数群の解説を始めます!
関数「up_fill(int s,BYTEpd,int l);」、及び
接頭語「up_fill_」を付けた
関数群「void up_fill_short(int s,shortpd,int l);を
始め、から「up_fill_void()まで」、
及び「up_fill_inc_byte();を始め、
up_fill_inc_void()まで」解説して行きます!
(3-4-18)関数「void up_fill(
int s,BYTE*pd,int l);」の説明
void Support::up_fill(
int s, // 書き込むデータ 0..255
BYTE *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-18-A)関数名「up_fill」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」と
しての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込む
)との意味です!
(3-4-18-B)関数「up_fill()」の【仮引数】説明
void Support::up_fill(
int s, // 書き込むデータ 0..255
BYTE *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「BYTE *pd,」は、対応する実引数を示す書き込みポインタ
です!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
(3-4-18-C)関数「up_fill()」の【アルゴリズム】説明
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
「if(l>=8){・・分岐中身・・}」は、条件「l>=8」を
満足(詰り書き込む長さが8以上と十分有る場合の
高速動作用)が中身です!
そして分岐中身は、
「l-=8;do{・・ループ中身・・}while((l-=8)>0);」と
「do{中身}while後置ループ条件;」でループ中身は、
「*pd++=s;*pd++=s;*pd++=s;*pd++=s;*pd++=s;*pd++=s;
*pd++=s;*pd++=s;」と「*pd++=s;」と1単位書き込んで
1単位ポインタを進を8個並べて処理します(詰り、
8単位毎処理)!
そして後置ループ条件「(l-=8)>0」と「(l-=8)」と
長さ「l」を8減らした数が0より大きいと、8単位処理
した事を長さにも反映した後でループ判定します
☆注意☆この様にするのは、解説『高速大容量の代表的
処理が、画像処理』で説明した様に実際に必要な
「*pd++=s;」の処理はCPU内の処理は変わら無いが、
ループ回数を制御する処理は1/8に単純計算減らせる
からです!
「l+=8;」は、後置ループ条件の中で「l-=8」と
長さ「l」を8減らす操作をしている為にループの直後に
補正を入れる必要が有ったからです!
if(l>=8){・・分岐中身・・}」を終えると
「while(--l>=0){*pd++=s;}」と本来は、この記述だけで
関数「up_fill()」は記述出来るのですが、
ココでは長さ「l」が8未満の処理と8単位毎の処理で
細かく対処出来無かった部分が処理されます!
(3-4-19)関数「void up_fill_short(
int s,short*pd,int l);」の説明
void Support::up_fill_short(
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-19-A)関数名「up_fill_short」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」と
しての接頭語として付けた物ですクラス構造にした
画像処理ライブラリでは、根本のクラス「Support」で
使用したので大域関数と同じ意味を持ちます!
「fill」は、穴埋めを意味する英単語です!穴埋めと言う
依り、埋め込む=全て同じ値にする(書き込む)との意味
です!
「short」は、C言語の「short型」です!この単位で処理
するとの意味です!
何故、関数「up_fill()」で「BYTE型」なのに「BYTE」が
付かないのは、関数「up_fill()」がベース・
基本だからです!★備考★何故、「BYTE型」が基本かと説明
すると、カメラからの生画像の画素は、「BYTE型」が基本だ
からと物理的な事です!
「BYTE型」以外の型に関しては、関数「up_fill_short()」
の「short」の様に型名を明記します!
(3-4-19-B)関数「up_fill_short()」の【仮引数】説明
void Support::up_fill_short(
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「short *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は
「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
(3-4-19-C)関数「up_fill_short()」の【アルゴリズム】説明
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
中身は、関数「up_fill()」と全く同じです!
C++以降の機能をご存じの読者様ならば、関数名は同じに
して仮引数「pd」の型だけを「BYTE*」・「short*」と
型名を替えう事でオーバーロード(多重定義)更には、
テンプレート機能を使えばと考えると思いますが、元々、
昭和の終わり頃からC言語しか無い時に開発した物ですので
ソノママ別名義の関数に成って居ます!
(3-4-20)関数「void up_fill_long(
int s,long*pd,int l);」の説明
void Support::up_fill_long(
int s, // 書き込むデータ
long* pd, // メモリ領域へのptr
int l // 書き込む大きさ
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-20-A)関数名「up_fill_long」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「long」は、C言語の「long型」です!
この単位で処理するとの意味です何故、
関数「up_fill()」で「BYTE型」なのに「BYTE」が
付かないのは、関数「up_fill()」がベース・
基本だからです!「BYTE型」以外の型に関しては、
関数「up_fill_long()」の「long」の様に型名を
明記します!
(3-4-20-B)関数「up_fill_long()」の【仮引数】説明
void Support::up_fill_long(
int s, // 書き込むデータ
long* pd, // メモリ領域へのptr
int l // 書き込む大きさ
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「long *pd,」は、対応する実引数を示す書き込みポインタ
です!
「d」は
「destination」行き先(宛先)です。詰り
「source to destination」とデータの向きを示します!
「int l」は、書き込む長さです!
(3-4-20-C)関数「up_fill_long()」の
【アルゴリズム】説明
){
if( l >= 8 ){ // 8単位以上なら
l -= 8; // DownCuntを8単位進め
do{ // 8単位毎に
*pd++ = s; // データを書き込む
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
*pd++ = s; //
}while( ( l -= 8 ) > 0 ); //
l += 8; // DownCuntを8単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = s; // データを書き込む
} //
}
中身は、関数「up_fill()」と全く同じです!C++以降の
機能をご存じの読者様ならば、関数名は同じにして
仮引数「pd」の型だけを「BYTE*」・「long*」と型名を
替える事でオーバーロード(多重定義)更には、
テンプレート機能を使えばと考えると思いますが、元々、
昭和の終わり頃からC言語しか無い時に開発した物ですので
ソノママ別名義の関数に成って居ます!
(3-4-21)関数「void up_fill_int(
int s,int*pd,int l);」の説明
void Support::up_fill_int(
int s, // 書き込むデータ
int* pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
up_fill_long( s, (long*)pd, l ); // LONG型で動作させる
}
この関数の本体は、ファイル「Support.cpp」に格納されて居て次の様に
(3-4-21-A)関数名「up_fill_int」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「int」は、C言語の「int型」です!
この単位で処理するとの意味です!
何故、関数「up_fill()」で「BYTE型」なのに「BYTE」が
付かないのは、関数「up_fill()」がベース・基本だから
です!
「BYTE型」以外の型に関しては、関数「up_fill_int()」の
「int」の様に型名を明記します!
(3-4-21-B)関数「up_fill_int()」の【仮引数】説明
void Support::up_fill_int(
int s, // 書き込むデータ
int* pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「int *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
(3-4-21-C)関数「up_fill_int()」の
【アルゴリズム】説明
){
up_fill_long( s, (long*)pd, l ); // LONG型で動作させる
}
「up_fill_long( s, (long*)pd, l );」で単に仮引数
「int* pd,」を型変換キャストして「(long*)pd」と置き
換え関数「up_fill_long(s,(long*)pd,l );」を実行して居
るだけです!
「long型」と「int型」が整数型で32ビットだった
処理系で作成した為の物です!
(3-4-22)関数「void up_fill_void(
int s,void*pd,int l);」の説明
void Support::up_fill_void(
int s, // 書き込むデータ
void* pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
union{ // 32と8Bits変換共用体
BYTE a[4]; // 8ビット4個分
int b; // 32ビット1個分
} d; // 上記の総称名d
long* pnxt; // 次のlong単位書込ptr
BYTE* pbyte; // 次のbyte単位書込ptr
int off; // オフセット
int l4; // int単位書き込み数
pbyte = (BYTE*)pd; // バイト単位PtrをSET
if( l <= 32 ){ // 32バイト以下なら
up_fill( s, pbyte, l ); // バイト単位で書き込む
return; //
} //
d.a[0] = s; // 同じバイト単位データ
d.a[1] = s; // を4組組み合わせて
d.a[2] = s; // int単位データとして
d.a[3] = s; // 作成
pnxt = (long*)( (int)pd & 0xfffffffc )+1; // long単位書込PtrSet
off = (int)pnxt - (int)pbyte; // オフセット算出
l -= off; // 書き込み大きさ補正
while( --off >= 0 ){ // オフセット数分バイト
*pbyte++ = s; // 単位に書き込み
} //
l4 = l >> 2; // long単位書込数算出し
up_fill_long( d.b, pnxt, l4 ); // long単位で書き込み
l4 *= 4; // BYTE単位数に変換し
pbyte += l4; // 残りのBYTE単位書込Ptr
l -= l4; // と書込数を算出し
while( --l >= 0 ){ // 残りをバイト単位で
*pbyte++ = s; // 書き込む
} //
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-22-A)関数名「up_fill_void」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「void」は、C言語の「何でも型」です!
どの型のデータでも処理するとの意味です!
具体的には「BYTE型」単位で処理します!
(3-4-22-B)関数「up_fill_void()」の【仮引数】説明
void Support::up_fill_void(
int s, // 書き込むデータ
void* pd, // メモリ領域へのPtr
int l // 書き込む大きさ
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「void *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
単位は、1バイト単位です!
(3-4-22-C)関数「up_fill_void()」の【ローカル変数】説明
){
union{ // 32と8Bits変換共用体
BYTE a[4]; // 8ビット4個分
int b; // 32ビット1個分
} d; // 上記の総称名d
long* pnxt; // 次のlong単位書込ptr
BYTE* pbyte; // 次のbyte単位書込ptr
int off; // オフセット
int l4; // int単位書き込み数
「union{BYTE a[4];int b;}d;」は、共用体定義を使用して
「BYTE a[4];」と「int b;」は、同じメモリー上の場所を
共用させる事を使用して「int型」つまり4バイトデータの
全てのデータに同じ値を入れる事が出来ます!
この記述をした時は、「int型」=「long型」だった
時です!
「long* pnxt;」は、「long型」=4バイト整数型の
ポインタです!
「BYTE* pbyte;」は、「BYTE型」=1バイト整数型の
ポインタです!
「int off;」は、ポインタ「long* pnxt;」と
ポインタ「BYTE* pbyte;」の差分です!
「int l4;」は、「long型」=4バイト整数型の単位毎の
処理数です!
(3-4-22-D)関数「up_fill_void()」の
【アルゴリズム】説明
pbyte = (BYTE*)pd; // バイト単位PtrをSET
if( l <= 32 ){ // 32バイト以下なら
up_fill( s, pbyte, l ); // バイト単位で書き込む
return; //
} //
d.a[0] = s; // 同じバイト単位データ
d.a[1] = s; // を4組組み合わせて
d.a[2] = s; // int単位データとして
d.a[3] = s; // 作成
pnxt = (long*)( (int)pd & 0xfffffffc )+1; // long単位書込PtrSet
off = (int)pnxt - (int)pbyte; // オフセット算出
l -= off; // 書き込み大きさ補正
while( --off >= 0 ){ // オフセット数分バイト
*pbyte++ = s; // 単位に書き込み
} //
l4 = l >> 2; // long単位書込数算出し
up_fill_long( d.b, pnxt, l4 ); // long単位で書き込み
l4 *= 4; // BYTE単位数に変換し
pbyte += l4; // 残りのBYTE単位書込Ptr
l -= l4; // と書込数を算出し
while( --l >= 0 ){ // 残りをバイト単位で
*pbyte++ = s; // 書き込む
} //
}
「pbyte = (BYTE*)pd;」は、ポインタ「BYTE* pbyte;」を
仮引数「void pd,」からセット!
「if(l<=32){up_fill(s,pbyte,l);return;}」は、書き込む
バイト数が32以内だと関数「up_fill(s,pbyte,l)」と
バイト型で処理する関数を実行し関数を終了します!
☆注意☆元々、巨大なサイズを処理する為の物とは理解
して頂いてますね!
「d.a[0]=s;d.a[1]=s;d.a[2]=s;d.a[3]=s;」は、
共用体のバイトデータ側に全て書き込むバイトデータ
「int s」をセットします!
「pnxt=(long*)((int)pd&0xfffffffc)+1;」は、
ポインタ「long* pnxt;」先頭の算出です!
「0xfffffffc」と最後がヘキサデシマル「0xc」=
2進数「1100」と最後の2ビットだけビット演算「&」で
0にマスキングする事で4バイトデータ単位のポインタに
変換!
式最後の「+1」で次の「long*」型ポインタを示す値に
成って居る事を確認して下さい!
「off=(int)pnxt-(int)pbyte;」は、バイト型ポインタ
「BYTE* pbyte;」とロング型ポインタ「long* pnxt;」の
差分計算です!
「l-=off;」は、差分、書き込み長さを補正!
「while(--off>=0){pbyte++=s;}」は、
その補正した差分分バイトデータを書き込み!
「l4=l>>2;」は、「l4=l/4;」と同じです!
気分的にコウ記載しただけ!
勿論、「long」型ポインタでの書き込み長さの算出です!
「up_fill_long(d.b,pnxt,l4 );」は、「d.b」と共用体の
「int型」つまり4バイトデータ部のデータで関数
「up_fill_long();」で書き込み処理!
「l4*=4;pbyte+=l4;」は、バイト型ポインタ
「BYTE* pbyte;」の位置調整!
「l-=l4;」は、バイト単位長さの調整!
「while(--l>=0){*pbyte++=s;}」は、最後の部分を書き込
み!
(3-4-23)画像処理ライブラリで扱う画像メモリの話
この画像処理ライブラリで扱う画像は、上図の格子状の
図の様に左上を原点(0,0)とする幾何学的には、
「第Ⅳ象限」を扱うと考えて下さい!
そして「h(水平幅)」と有るのは、
横方向(X座標方向)で左端が原点(0)で右方向に移動
≪メモリー上では、ポインタを+1すると右へ移動≫しま
す!
さらに「v(垂直幅)」と有るのは縦方向(Y座標方向)
で上端が原点(0)で下方向に移動≪メモリー上では、
ポインタを+(inc増加幅)すると下へ移動≫します!
何故、幾何学的には、馴染みが薄い様な
「h(水平幅)」・「v(垂直幅)」の文言を使用したの
は、元々ベンチャー企業ADS社の画像処理装置≪
アナログビデオカメラから、連続的にキャプチャ⇒
アナログからデジタルにサンプリングし画像メモリに格納
⇒その画像メモリに対して専用のディスクリュート回路で
処理したり、CPUで処理≫した時に作成したCPUで
処理する部分のソースコードが元なので
アナログビデオカメラの水平・垂直同期信号等の意味的
文言をCPU処理にも使用した為です!
そして、「inc(増加幅)」はインクリメント
【increment】の省略(inc)でh(水平幅)
とは、別個に垂直方向への増加幅を設定出来る事を示して
居ます!
何故、h(水平幅)=inc(増加幅)で無いかと説明する
と部分画像・親画像と大本の親画像からソノ一部を
部分画像として定義する事でinc(増加幅)が、
親画像の水平幅と同じに成るが、部分画像の場合は、
部分画像が所属する親画像の水平幅と同じに成ります!
今回は、画像処理に直接関わる高速動作させる為の
関数群の解説を始めます!
関数「up_fill_inc_byte(int s,BYTE*pd,int l,int inc);」
と接頭語
「up_fill_inc_」を付けた関数群
「void up_fill_inc_short(int s,short*pd,int l,
int inc);を始め、から「up_fill_inc_void()まで」、
及び「up_fill_inc_void()まで」解説して行きます!
「(3-4-18)関数「void up_fill(int s,BYTE*pd,
int l);」の説明」から「(3-4-22-C)関数
「up_fill_void()」の【アルゴリズム】説明」までは、
連続する領域の書き込み動作関数だが、今回は「inc」と
言う「インクリメント」が関数名に入って居る事に
注意して下さい!
画像処理としては、横(水平)方向に書き込むのが
「up_fill()」系の関数で縦(垂直)方向に書き込むのが
「up_fill_inc()」系の関数です!
(3-4-24)関数「void up_fill_inc_byte(
int s,BYTE*pd,int l,int inc);」の説明
void Support::up_fill_inc_byte(
int s, // 書き込むデータ
BYTE *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-24-A)関数名「up_fill_inc_byte」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「inc」は、インクリメント 【increment】の
省略(inc)で任意の増加幅を設定出来る事が特徴です!
「byte」は、「BYTE型」を意味します!
(3-4-24-B)関数「up_fill_inc_byte()」の
【仮引数】説明
void Support::up_fill_inc_byte(
int s, // 書き込むデータ
BYTE *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
「int s,」は、書き込むデータです!「s」は、
英単語「source」ソースで元とかの意味です!
「BYTE *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
「int inc」は、こn関数「_inc」特徴の増分です!
「_inc」が付かない関数「up_fill()系」では「inc=1」と
成って居ると同等です!
(3-4-24-C)関数「up_fill_inc_byte()」の
【アルゴリズム】説明
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
「if(l>=4){・・分岐中身・・}」は、条件「l>=4」を満足
(詰り書き込む長さが4以上と十分有る場合の高速動作用)
が中身です!
そして分岐中身は、
「l-=4;do{・・ループ中身・・}while((l-=4)>0);」と
「do{中身}while後置ループ条件;」でループ中身は、
「*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;pd+=inc;
*pd=s;pd+=inc;」と「*pd=s;pd+=inc;」と1単位書き込んだ
直後に増分単位ポインタを進を4個並べて処理します
(詰り、4単位毎処理)!
そして後置ループ条件「(l-=4)>0」と「(l-=4)」と
長さ「l」を4減らした数が0より大きいと、
4単位処理した事を長さにも反映した後でループ判定
します!
☆注意☆この様にするのは、解説『高速大容量の代表的処理
が、画像処理』で説明した様に実際に必要な
「*pd=s;pd+=inc;」の処理はCPU内の処理は変わら
無いが、ループ回数を制御する処理は1/4に単純計算
減らせるからです!
「l+=4;」は、後置ループ条件の中で「l-=4」と
長さ「l」を4減らす操作をしている為にループの直後に
補正を入れる必要が有ったからです!
if(l>=4){・・分岐中身・・}」を終えると
「while(--l>=0){*pd=s;pd+=inc;}」と本来は、
この記述だけで関数「up_fill_inc_byte()」は記述出来る
のですが、ココでは長さ「l」が4未満の処理と4単位毎の
処理で細かく対処出来無かった部分が処理されます!
(3-4-25)関数「void up_fill_inc_short(
int s,short*pd,int l,int inc);」の説明
void Support::up_fill_inc_short(
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-25-A)関数名「up_fill_inc_short」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」と
しての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!穴埋めと言う
依り、埋め込む=全て同じ値にする(書き込む)との意味
です!
「inc」は、インクリメント 【increment】の
省略(inc)で任意の増加幅を設定出来る事が特徴です!
「short」は、「short型」を意味します!
(3-4-25-B)関数「up_fill_inc_short()」の
【仮引数】説明
void Support::up_fill_inc_short(
int s, // 書き込むデータ
short *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「short *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
「int inc」は、この関数「_inc」特徴の増分です!
「_inc」が付かない関数「up_fill()系」では「inc=1」と
成って居ると同等です!
(3-4-25-C)関数「up_fill_inc_short()」の
【アルゴリズム】説明
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
「if(l>=4){・・分岐中身・・}」は、条件「l>=4」を
満足(詰り書き込む長さが4以上と十分有る場合の高速
動作用)が中身です!
そして分岐中身は、
「l-=4;do{・・ループ中身・・}while((l-=4)>0);」と
「do{中身}while後置ループ条件;」でループ中身は、
「*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;pd+=inc;
*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;
pd+=inc;*pd=s;pd+=inc;」と「*pd=s;pd+=inc;」と
1単位書き込んだ直後に増分単位ポインタを進を4個並べて
処理します(詰り、4単位毎処理)!
そして後置ループ条件「(l-=4)>0」と「(l-=4)」と
長さ「l」を4減らした数が0より大きいと、4単位処理
した事を長さにも反映した後でループ判定します!
☆注意☆この様にするのは、解説『高速大容量の代表的
処理が、画像処理』で説明した様に実際に必要な
「*pd=s;pd+=inc;」の処理はCPU内の処理は変わら無いが
ループ回数を制御する処理は1/4に単純計算減らせるから
です!
「l+=4;」は、後置ループ条件の中で「l-=4」と
長さ「l」を4減らす操作をしている為にループの直後に
補正を入れる必要が有ったからです!
if(l>=4){・・分岐中身・・}」を終えると
「while(--l>=0){*pd=s;pd+=inc;}」と本来は、
この記述だけで関数「up_fill_inc_short()」は記述出来
るのですが、ココでは長さ「l」が4未満の処理と4単位
毎の処理で細かく対処出来無かった部分が処理されます!
(3-4-26)関数「void up_fill_inc_long(
int s,long*pd,int l,int inc);」の説明
void Support::up_fill_inc_long(
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-26-A)関数名「up_fill_inc_long」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「inc」は、インクリメント 【increment】の
省略(inc)で任意の増加幅を設定出来る事が特徴です!
「long」は、「long型」を意味します!
(3-4-26-B)関数「up_fill_inc_long()」の
【仮引数】説明
void Support::up_fill_inc_long(
int s, // 書き込むデータ
long *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
「int s,」は、書き込むデータです!
「s」は、英単語「source」ソースで元とかの意味です!
「long *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は
「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!
「int inc」は、この関数「_inc」特徴の増分です!
「_inc」が付かない関数「up_fill()系」では「inc=1」と
成って居ると同等です!
(3-4-26-C)関数「up_fill_inc_long()」の
【アルゴリズム】説明
){
if( l >= 4 ){ // 4単位以上なら
l -= 4; // DownCuntを4単位進め
do{ // 4単位毎に
*pd = s; // データを書き込む
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
*pd = s; //
pd += inc; //
}while( ( l -= 4 ) > 0 ); //
l += 4; // DownCuntを4単位戻す
} //
while( --l >= 0 ){ // 残りを1単位毎に
*pd = s; // データを書き込む
pd += inc; //
} //
}
「if(l>=4){・・分岐中身・・}」は、条件「l>=4」を
満足(詰り書き込む長さが4以上と十分有る場合の高速
動作用)が中身です!そして分岐中身は、
「l-=4;do{・・ループ中身・・}while((l-=4)>0);」と
「do{中身}while後置ループ条件;」でループ中身は、
「*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;pd+=inc;
*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;pd+=inc;*pd=s;
pd+=inc;*pd=s;pd+=inc;」と「*pd=s;pd+=inc;」と
1単位書き込んだ直後に増分単位ポインタを進を4個並べ
て処理します(詰り、4単位毎処理)!
そして後置ループ条件「(l-=4)>0」と「(l-=4)」と
長さ「l」を4減らした数が0より大きいと、4単位処理
した事を長さにも反映した後でループ判定します!
☆注意☆この様にするのは、解説『高速大容量の代表的処理
が、画像処理』で説明した様に実際に必要な
「*pd=s;pd+=inc;」の処理はCPU内の処理は変わら無い
が、ループ回数を制御する処理は1/4に単純計算減らせる
からです!
「l+=4;」は、後置ループ条件の中で「l-=4」と
長さ「l」を4減らす操作をしている為にループの直後に
補正を入れる必要が有ったからです!
if(l>=4){・・分岐中身・・}」を終えると
「while(--l>=0){*pd=s;pd+=inc;}」と本来は、
この記述だけで関数「up_fill_inc_long()」は記述出来る
のですが、ココでは長さ「l」が4未満の処理と4単位毎の
処理で細かく対処出来無かった部分が処理されます!
(3-4-27)関数「void up_fill_inc_int(
int s,int*pd,int l,int inc);」の説明
void Support::up_fill_inc_int(
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
up_fill_inc_long( (long)s, (long*)pd, // long型へキャストして
l, inc ); // 実行
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-27-A)関数名「up_fill_inc_int」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「int」は、C言語の「int型」です!
この単位で処理するとの意味です!
「inc」は、インクリメント 【increment】の
省略(inc)で任意の増加幅を設定出来る事が特徴
です!
(3-4-27-B)関数「up_fill_inc_int()」の
【仮引数】説明
void Support::up_fill_inc_int(
int s, // 書き込むデータ
int *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
「int s,」は、書き込むデータです!「s」は、
英単語「source」ソースで元とかの意味です!
「int *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!単位は、
1バイト単位です!
「int inc」は、この関数「_inc」特徴の増分です!
「_inc」が付かない関数「up_fill()系」では「inc=1」と
成って居ると同等です!
(3-4-27-C)関数「up_fill_inc_int()」の
【アルゴリズム】説明
){
up_fill_inc_long( (long)s, (long*)pd, // long型へキャストして
l, inc ); // 実行
}
単に仮引数「int pd,」を型変換キャストして
「(long)pd」と置き換え関数
「up_fill_inc_long(s,(long*)pd,l,inc);」を実行して
居るだけです!
「long型」と「int型」が整数型で32ビットだった
処理系で作成した為の物です!
(3-4-28)関数「void up_fill_inc_void(
int s,void*pd,int l,int inc);」の説明
void Support::up_fill_inc_void(
int s, // 書き込むデータ
void *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
up_fill_inc_byte( s, (BYTE*)pd, l, inc ); // BYTEg型キャスト実行
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-28-A)関数名「up_fill_inc_void」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「fill」は、穴埋めを意味する英単語です!
穴埋めと言う依り、埋め込む=全て同じ値にする(書き込
む)との意味です!
「void」は、C言語の「何でも型」です!
どの型のデータでも処理するとの意味です!
具体的には「BYTE型」単位で処理します!
「inc」は、インクリメント 【increment】の
省略(inc)で任意の増加幅を設定出来る事が特徴です!
(3-4-28-B)関数「up_fill_inc_void()」の
【仮引数】説明
void Support::up_fill_inc_void(
int s, // 書き込むデータ
void *pd, // メモリ領域へのPtr
int l, // 書き込む大きさ
int inc // 増分
){
「int s,」は、書き込むデータです!「s」は、
英単語「source」ソースで元とかの意味です!
「void *pd,」は、対応する実引数を示す書き込み
ポインタです!
「d」は「destination」行き先(宛先)です。
詰り「source to destination」とデータの向きを示し
ます!
「int l」は、書き込む長さです!単位は、1バイト単位
です!
「int inc」は、こn関数「_inc」特徴の増分です!
「_inc」が付かない関数「up_fill()系」では「inc=1」と
成って居ると同等です!
(3-4-28-C)関数「up_fill_inc_void()」の
【アルゴリズム】説明
){
up_fill_inc_byte( s, (BYTE*)pd, l, inc ); // BYTEg型キャスト実行
}
単に仮引数「int pd,」を型変換キャストして
「(BYTE)pd」と置き換え
関数「up_fill_inc_byte(s,(long*)pd,l,inc);」を実行
して居るだけです!
取り敢えず、今日(2月10)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月11継続講義
今回から、画像処理に直接関わる高速動作させる為の
関数群の解説を始めます!
今日は、その内、
関数「up_movb(BYTE*ps,BYTE*pd,int l);」を解説、
その後で接頭語「up_mov」を付けた関数群
「void movs();を始めとしup_movv()と
「source to destination」も同じ型のポインタ、
更には、up_movb2s()≪コレは、b2s⇒BYTEからshort≫へ
型変換を意味すると異なる型のポインタの組み合わせを
処理出来る様に組み合わせた名前の関数」を
解説して行きます!
(3-4-29)関数「void up_movb(BYTEps,BYTEpd,int l);」の説明
void Support::up_movb(
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
){
int c16; // 16を示す定数値
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-29-A)関数名「up_movb」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味
する英単語です!
詰り「source to destination」と元から先(宛先)へ移動と
言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現
しました!
「b」は、型名「BYTE」の略です?!
勿論、「unsigned char」と符号無しの1バイト単位を意味
します!この関数が、このライブラリで一番使用頻度が
高いので特に高速化出来る様にソースコードを記述して
居ます!
(3-4-29-B)関数「up_movb()」の【仮引数】説明
void Support::up_movb(
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
){
「BYTE*ps,」は、「source to destination」と元から
宛先へコピーする元のデータポインタです!
「BYTEpd,」は、「source to destination」と元から
先へコピーする宛先(対応する実引数の書き込み領域を示
す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-29-C)関数「up_movb()」の【ローカル変数】説明
){
int c16; // 16を示す定数値
「int c16;」は、定数値「16を示す値」を格納する変数
です!
何故、
初期値「int c16=16;」と記載せずにアルゴリズム中に
代入しているかと言うとコンパイラシステムに依っては、
私の意図した≪CPUの高速レジスターに割り当てる≫で
無く、モット実行速度に影響を及ぼす機械コードに変換
する場合が在り、この記述に落ち着いたのです!
勿論、名称先頭の「c」はコンスト定数の意味です!
(3-4-29-D)関数「up_movb()」の【アルゴリズム】説明
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
「c16=16;」は、定数扱いのCPU高速レジスターに定数
「16」をセット!
「if(l>=c16){l-=c16;do{・・ループ中身・・}
while((l-=c16)>0);」は、分岐条件「l>=c16」と長さが
16以上在る場合は、先ず、「l-=c16;」とコレから処理する
長さ16分処理するので一旦、16減算し
「do{・・ループ中身・・}while((l-=c16)>0);」後置
ループ判断のループ中身≪「*pd++ = *ps++;」と1単位
処理を16個連続で記述して16回分のループの代わりに
高速に動作させて居ます!
関数「up_fill()」の説明でも紹介した様に「ループ判断の
処理割合を減らし中身だけに特化」した事は、
理解して頂けると思います!≫を実行し、
後置ループ判断!
「while((l-=c16)>0);」は、勿論「l-=c16」と16減算し
判断、
この分岐ブロック最後の「l+=c16;」は、
減算した値の補正です!
「while(--l>=0){*pd++=*ps++;}」は、本来、
この関数「up_movb()」の基本的な処理部分と判るで
しょう?!
コレを処理長さ「l」が十分大きい時に小細工を施した事が
判るでしょう!
ここから、画像処理に直接関わる高速動作させる為の関数群
の解説を始めます!
今日は、その内、
関数「up0movb(BYTEps,BYTEpd,int l);」を解説、
その後で接頭語「up_mov」を付けた関数群「void movs();
を始めとしup_movv()と「source to destination」も同じ
型のポインタ、更には、up_movb2s()≪コレは、b2s⇒
BYTEからshort≫へ型変換を意味すると異なる型のポインタ
の組み合わせを処理出来る様に組み合わせた名前の関数」
を解説して行きます!
(3-4-30)関数「void up0movb(BYTEps,BYTEpd,
int l);」の説明
void Support::up0movb(
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
){
while( --l >= 0 ){ // バイト単位毎に数量分
*pd++ = *ps++; // 転送する
} //
}
この関数の本体は、ファイル「Support.cpp」に格納
されて居て次の様に
(3-4-30-A)関数名「up0movb」の説明
「up0」は、「up_」と異なり、「private」プライベート
属性を持つ!
このクラス「Support」内で使用するローカル関数
です!
何故、「0」としたかは、基本の基本と言う事で考えたと
言って置きます!
(3-4-30-B)関数「up0movb()」の【仮引数】説明
void Support::up0movb(
BYTE *ps, // 転送元へのポインタ
BYTE *pd, // 転送先へのポインタ
int l // 転送する数量
){
「BYTE*ps,」は、「source to destination」と元から先へ
コピーする元のデータポインタです!
「BYTE*pd,」は、「source to destination」と元から先へ
コピーする先(対応する実引数の書き込み領域を示す)
ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-30-C)関数「up0movb()」の【アルゴリズム】説明
){
while( --l >= 0 ){ // バイト単位毎に数量分
*pd++ = *ps++; // 転送する
} //
}
「while(--l>=0){・・中身・・}」は、
ループ条件「--l>=0」と長さ「l」分処理「lを
デクリメント減算」を伴いループを回数「l」中身を繰り
返す事は、理解出来るでしょう!
中身「*pd++=*ps++;」は、1バイト単位処理した事は、
前の関数「up_movb()」で説明した通りです!
何故、関数「up0movb()」を作成したか、長さ「l」が
極めて小さい時は、関数「up_movb()」では、
余計な判別(if文等)が入っている為、遅く成るから
です!
取り敢えず、今日(2月11)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
2024年2月12継続講義
(3-4-31)関数「void up_movs(shortps,shortpd,int l);」の説明
void Support::up_movs(
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
){
int c16; // 16を示す定数値
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-31-A)関数名「up_movs」の説明
「up_」は、元々、C言語でADS社画像処理装置用の関数
をグローバル大域関数として「高速動作用汎用関数」とし
ての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味
する英単語です!
詰り「source to destination」と元から先(宛先)へ移動
と言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現し
ました!
「s」は、型名「short」の略です?!勿論、符号有りの
2バイト単位を意味します!
(3-4-31-B)関数「up_movs()」の【仮引数】説明
void Support::up_movs(
short *ps, // 転送元へのポインタ
short *pd, // 転送先へのポインタ
int l // 転送する数量
){
「short*ps,」は、「source to destination」と元から
先(宛先)へコピーする元のデータポインタです!
「short*pd,」は、「source to destination」と元から
先(宛先)へコピーする先(対応する実引数の書き込み
領域を示す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-31-C)関数「up_movs()」の
【ローカル変数】説明
){
int c16; // 16を示す定数値
「int c16;」は、定数値「16を示す値」を格納する変数
です!
何故、
初期値「int c16=16;」と記載せずにアルゴリズム中に
代入しているかと言うとコンパイラシステムに依っては、
私の意図した≪CPUの高速レジスターに割り当てる≫で
無く、モット実行速度に影響を及ぼす機械コードに変換す
る場合が在り、この記述に落ち着いたのです!
勿論、名称先頭の「c」はコンスト定数の意味です!
(3-4-31-D)関数「up_movs()」の
【アルゴリズム】説明
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
「c16=16;」は、定数扱いのCPU高速レジスターに
定数「16」をセット!
「if(l>=c16){l-=c16;do{・・ループ中身・・}
while((l-=c16)>0);」は、分岐条件「l>=c16」と長さが
16以上在る場合は、先ず、「l-=c16;」とコレから処理する
長さ16分処理するので一旦、16減算し
「do{・・ループ中身・・}while((l-=c16)>0);」後置ループ
判断のループ中身≪「*pd++ = *ps++;」と1単位処理を
16個連続で記述して16回分のループの代わりに高速に
動作させて居ます!
関数「up_fill()」の説明でも紹介した様に「ループ判断の
処理割合を減らし中身だけに特化」した事は、
理解して頂けると思います!≫を実行し、後置ループ判断
「while((l-=c16)>0);」は、勿論「l-=c16」と16減算し
判断、この分岐ブロック最後の「l+=c16;」は、
減算した値の補正です!
「while(--l>=0){*pd++=*ps++;}」は、本来、
この関数「up_movs()」の基本的な処理部分と判るで
しょう?!コレを理解すると処理長さ「l」が十分大きい時
に小細工を施した事が判るでしょう!
(3-4-32)関数「void up_movl(long*ps,long*pd,int l);」の説明
void Support::up_movl(
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
){
int c16; // 16を示す定数値
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-32-A)関数名「up_movl」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」と
しての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味
する英単語です!
詰り「source to destination」と元から先(宛先)へ移動
と言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現
しました!
「l」は、型名「long」の略です?!
勿論、符号有りの4バイト単位を意味します!
(3-4-32-B)関数「up_movl()」の【仮引数】説明
void Support::up_movl(
long *ps, // 転送元へのポインタ
long *pd, // 転送先へのポインタ
int l // 転送する数量
){
「long*ps,」は、「source to destination」と元から
先(宛先)へコピーする元のデータポインタです!
「long*pd,」は、「source to destination」と元から
先(宛先)へコピーする先(対応する実引数の書き込み領域
を示す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-32-C)関数「up_movl()」の
【ローカル変数】説明
){
int c16; // 16を示す定数値
「int c16;」は、定数値「16を示す値」を格納する変数
です!何故、初期値「int c16=16;」と記載せずに
アルゴリズム中に代入しているかと言うとコンパイラシス
テムに依っては、私の意図した≪CPUの高速レジスター
に割り当てる≫で無く、モット実行速度に影響を及ぼす
機械コードに変換する場合が在り、この記述に落ち着いた
のです!
勿論、名称先頭の「c」はコンスト定数の意味です!
(3-4-32-D)関数「up_movl()」の
【アルゴリズム】説明
c16 = 16; // 定数16セット
if( l >= c16 ){ // 16単位以上なら
l -= c16; // DownCuntを16単位
do{ // 毎に進め16単位
*pd++ = *ps++; // 毎に転送する
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
*pd++ = *ps++; //
}while( ( l -= c16 ) > 0 ); //
l += c16; // DownCuntを16単位戻
} // す
while( --l >= 0 ){ // 残りを1単位毎に
*pd++ = *ps++; // 転送する
} //
}
「c16=16;」は、定数扱いのCPU高速レジスターに
定数「16」をセット!
「if(l>=c16){l-=c16;do{・・ループ中身・・}
while((l-=c16)>0);」は、分岐条件「l>=c16」と長さが
16以上在る場合は、先ず、「l-=c16;」とコレから処理する
長さ16分処理するので一旦、16減算し
「do{・・ループ中身・・}while((l-=c16)>0);」後置ループ
判断のループ中身≪「*pd++ = *ps++;」と1単位処理を
16個連続で記述して16回分のループの代わりに高速に
動作させて居ます!
関数「up_fill()」の説明でも紹介した様に「ループ判断の
処理割合を減らし中身だけに特化」した事は、理解して
頂けると思います!≫を実行し、後置ループ判断
「while((l-=c16)>0);」は、勿論「l-=c16」と16減算し
判断、この分岐ブロック最後の「l+=c16;」は、減算した
値の補正です!
「while(--l>=0){*pd++=*ps++;}」は、本来、この関数
「up_movl()」の基本的な処理部分と判るでしょう?!
コレの処理を理解すると処理長さ「l」が十分大きい時に
小細工を施した事が判るでしょう!
(3-4-33)関数「void up_movi(intps,intpd,
int l);」の説明
void Support::up_movi(
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l // 転送する数量
){
up_movl( (long*)ps, (long*)pd, l ); // LONG単位で転送
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-33-A)関数名「up_movi」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味す
る英単語です!
詰り「source to destination」と元から先(宛先)へ移動
と言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現
しました!
「i」は、型名「int」の略です?!勿論、符号有りの
4バイト単位を意味します!
(3-4-33-B)関数「up_movi()」の【仮引数】説明
void Support::up_movi(
int *ps, // 転送元へのポインタ
int *pd, // 転送先へのポインタ
int l // 転送する数量
){
「int*ps,」は、「source to destination」と元から
先(宛先)へコピーする元のデータポインタです!
「int*pd,」は、「source to destination」と元から
先(宛先)へコピーする先(対応する実引数の書き込み
領域を示す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-33-C)関数「up_movi()」の
【アルゴリズム】説明
){
up_movl( (long*)ps, (long*)pd, l ); // LONG単位で転送
}
単に仮引数「int *ps,」仮引数「int *pd,」を型変換
キャストして「(long*)pd」・「(long*)pd」と置き換え
関数「up_movl((long*)ps,(long*)pd,l,inc);」を実行して
居るだけです!
「long型」と「int型」が整数型で32ビットだった
処理系で作成した為の物です!
(3-4-34)関数「void up_mova(BYTE*ps,BYTE*pd,int l);」の説明
void Support::up_mova(
BYTE* ps, // 転送元へのポインタ
BYTE* pd, // 転送先へのポインタ
int l // 転送する数量
){
int adrs; // 転送元の各種単位Adr
int adrd; // 転送先の各種単位Adr
int mask; // 1632Bits単位用マスク*/
int off; // バイト単位との差
int l1; // 転送量:ローカル
if( l >= 32 ){ // 転送幅32以上なら
mask = 0x7ffffffc; // Ptr32Bits単位MaskSET
adrs = (int)ps & mask; // Ptrを32Bits単位変換
adrd = (int)pd & mask; // Ptrを32Bits単位変換
off = (int)ps - adrs; // 32Bits単位との差算出
if( off == ( (int)pd - adrd ) ){ // SDの差が同じなら
if( off == 0 ){ // 差が0の時
l1 = l / 4; // 32Bits幅転送量算出
up_movl( (long*)adrs, // 先頭から始まる32Bits
(long*)adrd, l1 ); // 幅の転送を行う
l1 *= 4; // 実転送バイト数算出
}else{ // 0以外の時
up0movb( ps, pd, 4 - off ); // 前縁の転送を行う
l1 = ( l - off ) / 4 - 1; // 32Bits幅転送量算出
up_movl( (long*)adrs + 1, // 次から始まる32Bits幅
(long*)adrd + 1, l1 ); // の転送を行う
l1 = l1 * 4 + ( 4 - off ); // 実転送バイト数算出
} //
up0movb( ps + l1, pd + l1, l - l1 );// 残りをByte単位で転送
return; // 抜ける
} //
} //
if( l >= 16 ){ // 転送幅16以上なら
mask = 0x7ffffffe; // Ptr16Bits単位MaskSET
adrs = (int)ps & mask; // Ptrを16Bits単位変換
adrd = (int)pd & mask; // Ptrを16Bits単位変換
off = (int)ps - adrs; // 16Bits単位との差算出
if( off == ( (int)pd - adrd ) ){ // SDの差が同じなら
if( off == 0 ){ // 差が0の時
l1 = l / 2; // 16Bits幅転送量算出
up_movs( (short*)adrs, // 先頭から始まる16Bits
(short*)adrd, l1 ); // 幅の転送を行う
l1 *= 2; // 実転送バイト数算出
}else{ // 0以外(1)の時
*pd = *ps; // 前縁の転送を行う
l1 = ( l - 1 ) / 2 - 1; // 16Bits幅転送量算出
up_movs( (short*)adrs + 1, // 次から始まる16Bits幅
(short*)adrd + 1, l1 );// の転送を行う
l1 = l1 * 2 + 1; // 実転送バイト数算出
} //
up0movb( ps + l1, pd + l1, l - l1 );// 残りをByte単位で転送
return; // 抜ける
} //
} // 上記以外なら
up_movb( ps, pd, l ); // バイト単位で転送
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-34-A)関数名「up_mova」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です!
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味
する英単語です!
詰り「source to destination」と元から先(宛先)へ移動
と言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現
しました!
「a」は、「address」の略です?!
CPUシステムの機械アドレスをポインタの代りに使用した
事を意味します!
処理単位として「BYTE型」詰り1バイト単位処理する処理を
高速に動作≪8ビット単位で無く動作可能な部分は
32ビット単位若しくは16ビット単位で処理し1回の
転送単位処理時間を最小化を考えた≫させる為の物です!
(3-4-34-B)関数「up_mova()」の【仮引数】説明
void Support::up_mova(
BYTE* ps, // 転送元へのポインタ
BYTE* pd, // 転送先へのポインタ
int l // 転送する数量
){
「BYTE*ps,」は、「source to destination」と元から
先(宛先)へコピーする元のデータポインタです!
「BYTE*pd,」は、「source to destination」と元から
先(宛先)へコピーする先(対応する実引数の書き込み
領域を示す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-34-C)関数「up_mova()」の
【ローカル変数】説明
){
int adrs; // 転送元の各種単位Adr
int adrd; // 転送先の各種単位Adr
int mask; // 1632Bits単位用マスク*/
int off; // バイト単位との差
int l1; // 転送量:ローカル
「int adrs;」は、ポインタで表現された物をCPU機械
アドレスとしての整数型の値に変換した物です!
ソース(元データ)側です!
「int adrd;」は、ポインタで表現された物をCPU機械
アドレスとしての整数型の値に変換した物です!
ディスティネーション(転送先)です!
「int mask;」は、32ビット単位若しくは16ビット単位
で処理出来るポインタに変換する為にアドレスに
マスキングを掛ける処理を行う為のマスキング値です!
「int off; 」は、差分です!
「int l1; 」は、32ビット単位若しくは16ビット単位
で処理する時の処理長さです!
(3-4-34-D)関数「up_mova()」の
【アルゴリズム】説明
if( l >= 32 ){ // 転送幅32以上なら
mask = 0x7ffffffc; // Ptr32Bits単位MaskSET
adrs = (int)ps & mask; // Ptrを32Bits単位変換
adrd = (int)pd & mask; // Ptrを32Bits単位変換
off = (int)ps - adrs; // 32Bits単位との差算出
if( off == ( (int)pd - adrd ) ){ // SDの差が同じなら
if( off == 0 ){ // 差が0の時
l1 = l / 4; // 32Bits幅転送量算出
up_movl( (long*)adrs, // 先頭から始まる32Bits
(long*)adrd, l1 ); // 幅の転送を行う
l1 *= 4; // 実転送バイト数算出
}else{ // 0以外の時
up0movb( ps, pd, 4 - off ); // 前縁の転送を行う
l1 = ( l - off ) / 4 - 1; // 32Bits幅転送量算出
up_movl( (long*)adrs + 1, // 次から始まる32Bits幅
(long*)adrd + 1, l1 ); // の転送を行う
l1 = l1 * 4 + ( 4 - off ); // 実転送バイト数算出
} //
up0movb( ps + l1, pd + l1, l - l1 );// 残りをByte単位で転送
return; // 抜ける
} //
} //
if( l >= 16 ){ // 転送幅16以上なら
mask = 0x7ffffffe; // Ptr16Bits単位MaskSET
adrs = (int)ps & mask; // Ptrを16Bits単位変換
adrd = (int)pd & mask; // Ptrを16Bits単位変換
off = (int)ps - adrs; // 16Bits単位との差算出
if( off == ( (int)pd - adrd ) ){ // SDの差が同じなら
if( off == 0 ){ // 差が0の時
l1 = l / 2; // 16Bits幅転送量算出
up_movs( (short*)adrs, // 先頭から始まる16Bits
(short*)adrd, l1 ); // 幅の転送を行う
l1 *= 2; // 実転送バイト数算出
}else{ // 0以外(1)の時
*pd = *ps; // 前縁の転送を行う
l1 = ( l - 1 ) / 2 - 1; // 16Bits幅転送量算出
up_movs( (short*)adrs + 1, // 次から始まる16Bits幅
(short*)adrd + 1, l1 );// の転送を行う
l1 = l1 * 2 + 1; // 実転送バイト数算出
} //
up0movb( ps + l1, pd + l1, l - l1 );// 残りをByte単位で転送
return; // 抜ける
} //
} // 上記以外なら
up_movb( ps, pd, l ); // バイト単位で転送
}
「if(l>=32){・・中身・・}」は、条件「l>=32」詰り
長さが32以上なら中身を実行します!中身は、
「mask = 0x7ffffffc;」でマスキングデータとして正の値
マスク&下位2ビットを「0」にする事で「0,4,8,
12,16,,,」とアドレスの値が4の倍数、詰り
「long型」の単位に調整出来るマスキング値がセット
されます!
「adrs=(int)ps&mask;adrd=(int)pd&mask;」で
仮引数「BYTEps,BYTEpd」に「&mask」とマスキングを
掛けて「long型」の単位に調整しました!
☆注意☆機械アドレスが、32ビットで先頭の整数値なら
符号ビットが0詰り、正の32ビット値に成る様な範囲の
メモリー構成を想定したプログラムですが、
メモリー空間がこの場合、(0~2147483647)
と2ギガバイト以下を想定して居ますが、
コレを超えるメモリー空間の場合は、使用出来ませんので
変更する必要が有ります!!
「off=(int)ps-adrs;」でソース(元データ)側の元の
ポインタの値とマスキングを掛けたアドレスの値の差分を
算出します!
そして更に、
「if(off==((int)pd-adrd)){・・内側分岐成立中身・・}
は、条件「off==((int)pd-adrd)」、詰りソース側の
オフセットとディスティネーション側のそれが同じ数なら
ば、内側分岐成立中身実行そして内側分岐成立中身は、
次の様に
「if(off==0){・・最内側分岐成立中身・・}else{
・・最内側分岐非成立中身・・}」と先ず条件「off==0」で
詰り、オフセットの値が0ならば、最内側分岐成立中身を
処理し、それが「l1=l/4;」で関数「up_movl();」で処理
する長さを算出し、
「up_movl((long*)adrs,(long*)adrd,l1);」で
関数「up_movl();」詰り、ロング単位で処理、最内側分岐
成立中身最後で「l1*=4;」でバイト単位で実際に処理した
長さを変数「l1」にセット!
オフセットの値が0以外ならば、最内側分岐非成立中身を
処理し、
それが、「up0movb(ps,pd,4-off);」で前側のズレ分を
転送!
「l1=(l-off)/4-1;」でロング単位の長さ算出!
「up_movl((long*)adrs+1,(long*)adrd+1,l1);」で
関数「up_movl();」、詰り、ロング単位で処理、最内側
分岐非成立中身最後で「l1=l14+(4-off);」と
この最内側分岐非成立中身ブロックで処理したバイト単位
長さを算出!
内側分岐成立中身の最後、
「up0movb(ps+l1,pd+l1,l-l1);return;」でロング単位で
転送した最後のズレを転送し、この関数を終了します!
「if(l>=16){・・中身・・}」は、条件「l>=16」詰り長さ
が16以上なら中身を実行します!中身は、
「mask = 0x7ffffffe;」でマスキングデータとして
正の値マスク&下位1ビットを「0」にする事で「0,2,
4,6,8,,,」とアドレスの値が2の倍数、詰り
「short型」の単位に調整出来るマスキング値がセットされ
ます!
「adrs=(int)ps&mask;adrd=(int)pd&mask;」で
仮引数「BYTE*ps,BYTE*pd」に「&mask」とマスキングを
掛けて「short型」の単位に調整しました!
「off=(int)ps-adrs;」でソース(元データ)側の元の
ポインタの値とマスキングを掛けたアドレスの値の差分を
算出します!
そして更に、
「if(off==((int)pd-adrd)){・・内側分岐成立中身・・}は、
条件「off==((int)pd-adrd)」、詰りソース側の
オフセットとディスティネーション側のそれが同じ数なら
ば、内側分岐成立中身実行そして内側分岐成立中身は、
次の様に
「if(off==0){・・最内側分岐成立中身・・}else{
・・最内側分岐非成立中身・・}」と先ず条件「off==0」で
詰り、オフセットの値が0ならば、最内側分岐成立中身を
処理し、それが「l1=l/2;」で関数「up_movs();」で処理
する長さを算出し、
「up_movs((long*)adrs,(long*)adrd,l1);」で
関数「up_movs();」詰り、ロング単位で処理、最内側分岐
成立中身最後で「l1*=2;」でバイト単位で実際に処理した
長さを変数「l1」にセット!
オフセットの値が0以外ならば、最内側分岐非成立中身を
処理し、
それが、「up0movb(ps,pd,2-off);」で前側のズレ分を
転送!
「l1=(l-off)/2-1;」でロング単位の長さ算出!
「up_movs((long*)adrs+1,(long*)adrd+1,l1);」で
関数「up_movs();」
詰り、ロング単位で処理、最内側分岐非成立中身最後で
「l1=l1*2+(2-off);」とこの最内側分岐非成立中身ブロック
で処理したバイト単位長さを算出!内側分岐成立中身の
最後、
「up0movb(ps+l1,pd+l1,l-l1);return;」でロング単位で
転送した最後のズレを転送し、この関数を終了します!
関数の最後の「up_movb(ps,pd,l);」で内部のブロックで
処理出来無かった条件の場合、バイト単位で転送します!
(3-4-35)関数「void up_movv(void*ps,void*pd,int l);」の説明
void Support::up_movv(
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l // 転送する数量
){
up_mova( (BYTE*)ps, (BYTE*)pd, l ); // 8ビット単位で転送
}
この関数の本体は、ファイル「Support.cpp」に
格納されて居て次の様に
(3-4-35-A)関数名「up_movv」の説明
「up_」は、元々、C言語でADS社画像処理装置用の
関数をグローバル大域関数として「高速動作用汎用関数」
としての接頭語として付けた物です
クラス構造にした画像処理ライブラリでは、
根本のクラス「Support」で使用したので大域関数と同じ
意味を持ちます!
「mov」は、「move」≪動かす、移す、移動させる≫意味
する英単語です!
詰り「source to destination」と元から先(宛先)へ移動
と言う依り、コピー動作を行う物です!
一番使用頻度が多いと考えられたので省略して短く表現
しました!
「v」は、型名「void」の略です?!勿論、
なんでも型なので1バイト単位として扱います!
(3-4-35-B)関数「up_movv()」の【仮引数】説明
void Support::up_movv(
void *ps, // 転送元へのポインタ
void *pd, // 転送先へのポインタ
int l // 転送する数量
){
「void*ps,」は、「source to destination」と元から
先(宛先)へコピーする元のデータポインタです!
「void*pd,」は、「source to destination」と元から
先(宛先)へコピーする先(対応する実引数の書き込み
領域を示す)ポインタです!
「int l」は、転送(書き込む)長さです!
(3-4-35-C)関数「up_movv()」の
【アルゴリズム】説明
){
up_mova( (BYTE*)ps, (BYTE*)pd, l ); // 8ビット単位で転送
}
単に仮引数「void *ps,」仮引数「void *pd,」を型変換
キャストして「(BYTE*)pd」・「(BYTE*)pd」と置き換え
関数「up_mova((BYTE*)ps,(BYTE*)pd,l);」を実行して
居るだけです!
★備考★
解説『解説クラスTypeArray』でも記載したが、
noteエディタの変な特性でコピペした文章の半角「*」
が消されたり、空白「 」が消される事が多々あります!
注意して手作業で修正している筈ですが、必ず、code
機能で表示して居る物を正しいとして確認して下さい
取り敢えず、今日(2月12)の講義はココまで
お疲れ様です!マダ続きが有りますが、それは後日!!
「解説文章『解説クラスSupport』がnote
エディタで大きく成り過ぎ」たのか編集が重たいので
ここで、終わり次から
解説『解説クラスSupport続き』に続けます!
引き続きよろしくお願いいたします。