質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

5回答

1339閲覧

(LPWORD)が必要な理由

mery

総合スコア27

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

0クリップ

投稿2019/05/28 12:22

前提・実現したいこと

 エディットボックスから特定の行の文字列を取得する方法を調べていたらエディットボックスにEM_GETLINEを送信すればいいという情報を見つけました。
その内容によると文字列を受け取るバッファの先頭2バイトにバッファのサイズを指定しておく必要があると書いてあり、その部分のソースコードは下のようになっていたのですが、(LPWORD)がなぜ必要なのか理由がわかりません。
試しに(LPWORD)を削除し*szBuf = sizeof(szBuf) / sizeof(TCHAR);として実行してみましたが実行結果は何も変わりませんでした。
この(LPWORD)は何をするためのものなのですか?

該当のソースコード

c

1TCHAR szBuf[256]; 2*(LPWORD)szBuf = sizeof(szBuf) / sizeof(TCHAR);

補足情報(FW/ツールのバージョンなど)

Microsoft Visual C++ 2010 Express C言語
WIN32 ユニコードビルド Windows7

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

余談ですが、このキャストは安全ではないです。
ref:
(翻訳)C/C++のStrict Aliasingを理解する または - どうして#$@##@^%コンパイラは僕がしたい事をさせてくれないの! - yohhoyの日記

やるとしたら

c

1TCHAR szBuf[256] = { 0 }; 2const WORD size = (WORD)(sizeof(szBuf) / sizeof(TCHAR)); 3memcpy(szBuf, size, sizeof(size));

じゃないでしょうか。


なんか無駄に回答者が多くついている割に、質問者の理解に繋がっていないようなので、真面目に回答します。

仮にTCHARcharだとしましょう。

c

1TCHAR szBuf[256]; 2*(LPWORD)szBuf = sizeof(szBuf) / sizeof(TCHAR);

szBufTCHAR [256]型の変数です。C言語では配列は3つくらいの例外を除いて式中では常に配列の先頭要素へのポインタとして扱われるのでした。

ポインタ型はつねにもととなる方があってそこから派生して作られます。

c

1int n = 0; 2int* np = &n;

この例ならnpの型はint*型ですが、これはint型から派生してできますよね?

これは何を意味するかと言うと、pointerをdereferenceするときに(=たぐり寄せたとき)、メモリーをどう扱えばいいかを示すのが派生元の型である、ということができます。この例ではnpをdereferenceしたときの領域の大きさ、bitの使われ方はint型と同じだよ、といううのがint*型の意味ですね。

c

1TCHAR szBuf[256]; 2*(LPWORD)szBuf = sizeof(szBuf) / sizeof(TCHAR);

さて、本題に戻りましょう。この配列(=szBuf)の先頭要素へのポインタをLPWORD型(=WORD*型=16bitの符号なし整数型へのポインタ型)にキャストするというのはどういうことかというと、メモリーをどう扱えばいいかを偽る、という作業になります。配列szBufのメモリー領域は本当はTCAHRの配列ですが、WORD型の変数であると偽るわけです。

メモリー

では質問者さんがいう

まだよくわからないのですが、szBuf[0]からszBuf[256]までの要素のそれぞれを2バイトの領域として扱い、szBuf[0]にsizeof(szBuf) / sizeof(TCHAR)の結果を保存するという意味であってますか?

は誤りなのでしょうか?じつはあながち間違いでもありません。(szBuf[256]じゃなくてszBuf[255]でしょうが、ケアレスミスですよね?)

Re:Cのポインタと配列の関係について考察してみる
を読んでいただきたいのですが、ポインタは単にdereferenceして指し示す先を取り出すだけではなくて、ポインタ演算によってポインタの指す場所を移動させる、イテレータのような機能も持つのでした。

c

1int n1 = 3; 2int arr[2] = { 0 }; 3int* p; 4p = &n1;//OK 5p++;//OK 6p = arr;//OK 7p++;//OK

言い換えるとint型は要素数1の配列とみなすこともできるわけです。というかポインタには要素数の情報がありませんので、上のコードのような芸当ができるのでした。

つまり、

szBuf[0]からszBuf[256]までの要素のそれぞれを2バイトの領域として扱い

は正しく直すと

要素数不明のWORD型の配列と偽る

となります。


で追記前に書きました話に戻ります。

先程

メモリーをどう扱えばいいかを偽る、という作業

と書きましたが、こういう作業はコンパイラがプログラムの最適化をしやすくするために、多くの場合で禁止されています。言い換えるとszBufは何も変更されないというコードをコンパイラが生成する可能性があるということです。

ではどうすればいいのか、細かい理屈は冒頭でリンクしていてこのコメント欄にも書き込みされている
yohhoyさんの記事を見ていただくとして、結論から言うとmemcpyでbyte copyすればよいです。念の為memcpyとは何をするものなのかman memcpyでぐぐって確認すると

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/memcpy.3.html
memcpy() はメモリー領域 src の先頭 n バイトを メモリー領域 dest にコピーする。コピー元の領域と コピー先の領域が重なってはならない。重なっている場合は memmove(3) を使うこと。

とあります。

投稿2019/05/28 12:46

編集2019/05/29 17:21
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asm

2019/05/28 13:10

配列として宣言している場合でもStrict Aliasingにひっかかりましたっけ・・・?
yumetodo

2019/05/28 15:29

うーん、だいぶ自信ないのでもっかい見返してきます・・・。でもmemcpyしとけば確実に安全
yumetodo

2019/05/28 15:51

読み直したけど普通に違反に見えるんだけどなぁ・・・(だから人類にStrict Aliasing Rulesの理解とか無理だってばっ!
asm

2019/05/28 15:53

とりあえず -Wstrict-aliasing=2とかの警告つけてみましたが *(キャスト)配列名 = 数値; のパターンで警告が出る様子はなかったです。
yumetodo

2019/05/28 15:57

TCHARがcharだと問題ない >char*, signed char*, unsigned char*が別の何かを指すことが特別に許されています。 ので、検証する時そこ大丈夫ですか?
yumetodo

2019/05/28 15:58

wchar_t*に対しても許されるという話があるなら話は別
asm

2019/05/28 15:59

えぇ、それは知っていますのでwchar_tで検証しています。 また、character typeにwchar_tが入っていた場合も考えshortとlongなどのパターンも試し ポインタならば、警告が出ることも確認しています。
asm

2019/05/28 16:13

あ、ごめん 寝ぼけてたようです 警告でました。
yumetodo

2019/05/28 16:39

よかった、やっぱりいかなる時もキャストでデータ無理やりぶちこむの悪い文明だから安牌とってmemcpyするかbit_castするべし。
yohhoy

2019/05/29 16:47

言語仕様に厳格になるなら yumetodo さん対処コードの方が安全ですね。 ただまあ(私が知る限りは)MSVCはStrict Aliasing Ruleを考慮しない=同規則に基づく積極的な最適化をしないので、元々のコードで十分動く気はしますw
yumetodo

2019/05/29 17:23

まあ動いちゃう気はしますw(それはそれでどうなんだ
mery

2019/05/30 10:45

yumetodoさん詳細な解説ありがとうございました。
guest

0

ユニコードビルドだとTCHAR=WCHARなので、(LPWORD)があってもなくてもバッファのサイズが格納される領域のバイト数は2バイトですが、マルチバイトビルドだとTCHAR=CHARなので、(LPWORD)がないと、バッファのサイズは先頭1バイトのみに入ってしまいます。

どちらのビルドモードでも意図した結果になるようにLPWORDでキャストしているのでしょう。

投稿2019/05/28 12:33

KoichiSugiyama

総合スコア3041

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mery

2019/05/29 11:40

回答ありがとうございました。 マルチバイトビルドの時に*(LPWORD)がないと警告がでたのでその対策のようですね。
guest

0

まずは、LPWORDがどういう定義をされているか調べてみたらどうでしょう
それをまず理解しないことにはなぜ必要かわかりませんぜ

投稿2019/05/28 13:13

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mery

2019/05/29 09:48

回答ありがとうございました。 調べてみたらLPWORDはunsigned short 型の別名のようですね。
y_waiwai

2019/05/29 22:21

16ビット整数のポインタにキャストしてるってことですんで、あとは他の回答のとおりですね
guest

0

まずsizeof(szBuf) / sizeof(TCHAR)で求められるのは数値になりますから、格納先のTCHARとは型が異なります。
TCHARの領域に数値型を代入しようとしているので、通常ならWarningが出て注意されるので、代入先はTCHAR型ではなくWORD型として扱ってね、とやっているわけです。
LPWORD型はWORD型のポインタです。
WORD型はshort型ですから2バイトです。

投稿2019/05/29 01:16

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mery

2019/05/29 10:32

回答ありがとうございました。 まだよくわからないのですが、szBuf[0]からszBuf[256]までの要素のそれぞれを2バイトの領域として扱い、szBuf[0]にsizeof(szBuf) / sizeof(TCHAR)の結果を保存するという意味であってますか?
ttyp03

2019/05/29 11:05

残念ながら違います。 配列は[]を書かないとその配列の先頭アドレスを表します。 つまりszBufの先頭アドレスが指す領域をWORD型として扱うようにしているということです。 szBufの全体ではありません。 うまく説明するの難しいな。。。
mery

2019/05/29 12:09

szBufの先頭アドレスが指すのがszBuf[0]なんだからそこに続く領域のszBuf[1]からszbuf[256]もWORD型として扱われるんじゃないのですか? szBuf[0]だけが2バイトの領域になるとしたら、もしszBufをchar型で宣言していたらszBuf[0]が2バイトで残りの要素はそれぞれ1バイトの領域になるのでしょうか?
yumetodo

2019/05/29 17:22

>ttyp03 氏 別にあながちまちがってもいないような。ポインタはポインタ演算できるので(私の回答で大幅補足して言及しました
ttyp03

2019/05/29 23:51

yumetodoさん> まあ間違っているとは思ってませんが、初学者に理解できるかなという点で難しいと感じました。 特にC言語の場合、変数とメモリの関係を強く意識しないといけないわけで、そこの理解ができていないと通じない話なのかと思いました。 meryさん> コンピューターにはメモリがあって、そこに変数の領域が割り当てられます。 メモリ視点でみたときそこには単なる数値があって「型」が何かという情報は持っていません。 C言語ではその領域を変数の型として持っていて、TCHAR型の情報として内部処理します、int型の情報として内部処理します、などなど、と定義しているにすぎません。 なのでTCHARとして扱うと最初にいいましたけど、今だけはWORD型としてください、とお願いしていると考えてもらえれば伝わりやすいかな。 どうでしょう。
guest

0

windowsのTCHARはANSIとUNICODEで型が変化し
それぞれ、charwchar_tです。

というわけでcharの場合で実験すると

c

1#include <stdio.h> 2 3typedef unsigned short *LPWORD; 4 5int main(){ 6 { 7 char szBuf[256] = {0x11,0x11,0x11,0x11}; 8 *(LPWORD)szBuf = sizeof(szBuf) / sizeof(char); 9 printf("%02x %02x %02x %02x\n", szBuf[0],szBuf[1],szBuf[2],szBuf[3]); 10 } 11 puts("------------"); 12 { 13 char szBuf[256] = {0x11,0x11,0x11,0x11}; 14 *szBuf = sizeof(szBuf) / sizeof(char); 15 printf("%02x %02x %02x %02x\n", szBuf[0],szBuf[1],szBuf[2],szBuf[3]); 16 } 17}

では出力される文字が変わります。

投稿2019/05/28 12:51

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mery

2019/05/29 11:10

回答ありがとうございました。教えて頂いたコードを試した結果、キャストしない行があると「warning C4305: '=' : 'unsigned int' から 'char' へ切り詰めます。」という警告が表示されました。 どうやらcharの場合にこの警告を出さないためのキャストのようですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問