LPBYTE 配列が空かどうか確認する方法

[戻る]
なおぞう 2016/02/24(水) 11:46:41
お世話になります。
VS2013 標準Windowsライブラリを使用、Unicode文字セットを使用、共通言語ランタイム
サポートをしない の環境で開発しております。

出力データ(バイナリコード)として、LPBYTE 型の配列が引数として渡されてくるのです
が、その配列が引数として不正で無いかチェックする必要があります。

配列の1文字目がNULL(空?)だったら不正引数としようと考えましたが、(引数チェッ
クとしてこの考えも良くないというようでしたらご指摘いただけると嬉しいです)コード
を書いても期待する結果(空だよ)が返ってきません。


以下のテストコードを書いてみました。

BYTE ch[] = "0232421312";
LPBYTE x = ch;
memset(ch, '\0', sizeof(ch));

//xが空かどうかの判定
if ((!x)) printf("ERROR \n");

if (&x[0] == NULL) printf("ERROR \n");

if (&x[0] == '\0') printf("ERROR \n");

if (x == '\0') printf("ERROR \n");
if (x == NULL) printf("ERROR \n");

記述したものはすべてスルーしてしまいました…。

根本的に間違えているのかもしれませんが、それ自体が分かりません。
すみませんが、教えていただけますようお願いします。

tetrapod 2016/02/24(水) 12:01:54
「空」という用語に何を期待しているのかがわからないのでアドバイスしようが無い。
そもそも BYTE は「文字」でない。

0文字の char 相当のチェックであるなら
if (ch[0]=='\0') とか
if (x[0]=='\0') とか。
これが期待した「空」であるかどうかは要件次第。

瀬戸っぷ [E-Mail] 2016/02/24(水) 12:04:49
>if ((!x)) printf("ERROR \n");

xがNULLだった場合に真になる。
>LPBYTE x = ch;
なので、xにはchのアドレスが入っているのでNULLにはならない。


>if (&x[0] == NULL) printf("ERROR \n");

変数xのアドレスがNULLだった場合に真になる。
変数割り当てがあった時点でアドレスNULLはあり得ない。


>if (&x[0] == '\0') printf("ERROR \n");

変数xのアドレスが'\0'だった場合に真になる。
1つ上と同上。


>if (x == '\0') printf("ERROR \n");

xが'\0'だった場合に真になる。
最初の同じ。


>if (x == NULL) printf("ERROR \n");

xがNULLだった場合に真になる。
最初の同じ。


>配列の1文字目がNULL

配列の1文字目が'\0'だったら…ですよね?
一応、'\0'とNULLは区別した方がいいと思いますが。
# というかNULLポインタと言うべき?


if (*(x[0]) == '\0') printf("ERROR \n");
でいいんじゃないですかね?

瀬戸っぷ [E-Mail] 2016/02/24(水) 12:06:13
あれ?間違ったか?
微妙に理解が怪しいかも知れん。
# あんまし考えないで回答はいかんな。

なおぞう 2016/02/24(水) 13:32:13
[[解決]]
回答ありがとうございます。

>tetrapod様
データの入っているはずのLPBYEに、ちゃんとデータが入っているか確認したいというこ
となので、1バイト目にデータが無ければエラーという考えに至りました。

>瀬戸っぷ様
丁寧な説明ありがとうございます。

if (x[0]== '\0') printf("ERROR \n");
で、エラーが出力されました。
なぜ、このパターンを自分で書かなかったのか…。

ポインタって難しいですね。

ありがとうございました。

AR 2016/02/24(水) 13:44:50
 解決しちゃったようですが、せっかく書いたので

 文字とBYTEを混同しちゃってるのは、この辺りから推測できるわけでして
BYTE ch[] = "0232421312";
 また、この質問のボタンの掛け違いの本質もここだと思います。

 この部分を、こんな風に書き換えれば、多少なりとも理解が進むと思います。
char *szbuff = new char[20];
StrCpyA(szbuff, "0232421312");
LPBYTE lpch = (LPBYTE)szbuff;


 なんとなくVARIANT型の戻り値を展開しようとしてるんじゃないかと予想しています
が・・・その場合だとVariantInit()を調べると良いでしょう。

aetos 2016/02/24(水) 14:13:03
そもそもデータが文字列なのかバイナリデータなのかをはっきりすべきですね。
文字列なら char なり wchar_t なり(もしくはそのポインター)で扱うべきだし、LPBYT
E ならバイナリデータとして扱うべきです。

C/C++ では一般に文字列の終端を '\0' で表しますから、「有効な文字列がなくても '\0
' だけは必ずある」ことが保証されているならば、
ch[0] == '\0'
でよいでしょう。

同様に、バイナリデータの場合であっても、アプリケーションの仕様によって、「有効な
データがない場合でも、必ず 1 バイトの 0 は保証される」という約束があるならば、
ch[0] == 0
でよいでしょう。

ただし、文字列と違ってバイナリデータの場合、「データがない」というのは「データの
長さがゼロ」というのを指す方が一般的だと思います。
その場合、
ch[0]
は不正なアクセスになり得ます。

データの長さがゼロという事態が想定できるのであれば、LPBYTE というポインターだけ
から、その長さを知ることはできませんから、別途長さを表すパラメーターを受け取る必
要があります。

仲澤@失業者 2016/02/24(水) 15:04:45
もう結論が出ているようなので、別の注意点。

まぁ渡されるのが LPBYTE( = unsgined char *) であると明記されているので
間違いはしないと思いますが、「Unicode」でコンパイルしているとも
書いてあったのでで少し気になりました。

あたりまえですが、もしUnicodeテキストのやりとりについて、検討しているのなら、
そもそも LPBYTE や、chr * などでやり取りしてはいけません。
wchar_t や WCHAR を使用します。

wchar_t ch[] = L"0232421312";
WCHAR * x = ch;

てな感じ。

なおぞう 2016/02/24(水) 15:54:00
>皆様
解決後の書き込みありがとうございます。勉強になります。(とはいえ、理解が追い付か
ない現状ですが)

引数で受け取るLPBYTEはImageのデータのポインタなので、バイナリデータです。


>ただし、文字列と違ってバイナリデータの場合、「データがない」というのは「データの
>長さがゼロ」というのを指す方が一般的だと思います。

最初、データの長さで確認しようとしたのですが、
LPBYTE …unsigned char の長さをとる方法がみつけられなかったのです。

tetrapod 2016/02/24(水) 16:12:37
例えばバイナリファイルを読むことを考える。

#define BUFSIZE 1024
BYTE buf[BUFSIZE];
size_t len=fread(buf, 1, BUFSIZE, f);

としたのであれば「空である」とは len==0 のことだ。バイナリファイルの中身として
0x00 ('\0') は普通にありうる話だから buf を見ても判別不能。

> Image のデータのポインタ
の他にサイズを得るための引数、ないしは返却値があったりしないかい?

なおぞう 2016/02/24(水) 16:27:53
>tetrapod様

引数として渡されるのは、コールバック関数のポインタと、 LPBYTE (imageデータ) だ
けとなっています。戻り値は、関数内で発生したエラーIDになります。

この二つの引数が不正で無いことを確認しなくてはならないのです。
コールバック関数の不正チェックも、 if(PFUNC == NULL)  return err; って考えてし
まっています…。

tetrapod 2016/02/24(水) 18:19:38
今作ろうとしているのは image を作る側なんだよね。
ということは呼び出し側が渡してくる BYTE* には何も入っていないのが普通だろう。
「空」であることが当たり前なのではないかい?

呼び出し側の典型的コード
BYTE* buf=malloc(BUFSIZE); // 中身は無い
imagebuilderfunc(buf, callback); // 無いまま渡してくる

真にやりたいことは「空チェック」などではないに1票。
では真にやりたいことが何なのかは読者にはわからないことなので、
自分でもう一度整理してもらいたい。

> LPBYTE (imageデータ) だけ
それはダメ設計だ。

char buf[1];
strcpy(buf, "abc");
はメモリ破壊(バッファオーバーラン脆弱性)を起こしているわけだ。
今の設計(サイズ指定なし)だと、これと同じことが起こる。しかも回避策がない。
strncpy() のようにサイズ指定があれば回避できるので、
今のうちに仕様変更を提案しておくといい。

> コールバック関数の不正チェックも、 if(PFUNC == NULL)  return err; って
これは正しいだろう。でもオイラなら assert() で実装するよ。

C/C++ にはポインタの妥当性を調べる方法は提供されていないので、
呼び出される側でチェックできるとしたら nullptr くらいしかないよ。
呼ぶ側の責任で正しく呼ぶ以外に方法はない。

BYTE* p=malloc(100); // メモリ確保
free(p); // メモリ処分するが、ごみになったポインタ値は p に残ったまま
fread(p, 1, 100, f); // ダングリングポインタ使用バグ
fread 側にてチェックできる内容は実際問題として無い。

なおぞう 2016/02/24(水) 18:36:32
>tetrapod様

>今作ろうとしているのは image を作る側なんだよね。

いえ。
LPBYTE型のImageを引数でもらって、それをさらに次のDLLの関数に他の引数と一緒に渡す
だけという橋渡しの関数を作っています。なので、受け取ったImageが空だったらエラー
という判断をしたいのです。


LPBYTEのデータが不正かどうかなんてどうやって判断したら良いのか分からないので、質
問させていただきました。

>これは正しいだろう。でもオイラなら assert() で実装するよ。

間違いでないようで良かったです。
assertですか。…使った事ないです。今後利用したいと思います。
提案ありがとうございます。

tetrapod 2016/02/24(水) 19:00:26
うーんさっぱりわからん。
とりあえず既に書いた通り、呼ばれる側でできることはほとんど無い。

1.ポインタの値の妥当性を調査するすべはない (nullptr かどうかくらい)
2.ポインタの指す先に使用可能なメモリがあるかを調査するすべはない
3.バッファサイズを渡さない設計はただのバグ。

直すべきところは仕様のレベルで直そう。
できないことはきっぱりできないと言って断ろう。

ITO 2016/02/24(水) 19:07:04
>LPBYTE型のImageを引数でもらって、それをさらに次のDLLの関数に他の引数と一緒に
>渡すだけという橋渡しの関数を作っています。なので、受け取ったImageが空だったら
>エラーという判断をしたいのです。

ただ、データをもらって判断する。= 無理です。

正しいデータかどうかの識別の型が必要ですね。
まず、
  1. imageデータの種類は?
       jepg?  BMP?  png? gif? ......

   2. imageデータの固有情報(タイトル等)を入れてもらう。
  3. 1,2をもとにデータのヘッダー情報からタイトル、ファイル容量を調べる。
  4. タイトルが正しくなければそのデータは捨てる。
   5. 得られたファイル情報をもとに、データを取得する。
   6. データを渡す。

ですね。

ITO 2016/02/24(水) 19:09:12
修正です
ファイル情報 → ファイル容量
です。

ITO 2016/02/24(水) 19:20:31
ん?
 imageデータだったら、種類、圧縮率・大きさ・何色か?を一定にしてもらえば、
データ容量は一定になりますよね。
あとは、
 ファイルに書出し→画面表示又はオペレータ判断→よければデーターを渡す。
でもいいのでは?
 まぁ-、jepgやbmpならそのまま表示できますよね。

ITO 2016/02/24(水) 19:30:12
うーーん、
 DLLの関数で行うとなるとファイルに保存は無理ですね。
 ヘッダー情報のチェックぐらいですね。
何処までチェックするか仕様でもらわないとだめですね。

なおぞう 2016/02/24(水) 19:57:18
>tetrapodさま、ITOさま

LPBYTE型が渡されてくる場合に中身がおかしいとかってあり得ない前提で、LPBYTEについ
ては引数チェックしなくても良いかと思えてきました。
普通、渡す側でちゃんとチェックしますよね。


なにはともあれ、たくさんのアドバイスをありがとうございました。
いろいろと勉強になりました。

AR 2016/02/25(木) 12:25:56
>普通、渡す側でちゃんとチェックしますよね。

 データサイズを渡すインターフェイスも忘れてる設計なので、あまり信用できないと
思ってる人は多いと思いますよ。
 上位側との意識合わせをしておいた方が良いと思います。

 これがimageのやり取りじゃなくて、CImageのポインタとかが渡されるのであればイン
ターフェイスは適切で、NULLチェックだけで良いのですがね。

なおぞう 2016/02/26(金) 15:44:52
>AR様

ありがとうございます。
上位プログラムとの意識あわせを忘れずにしたいと思います。

古いスレッドには追加発言できません。