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

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

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

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

Q&A

解決済

4回答

1724閲覧

doubleからunsigned char *にキャストできない理由

WEjpon

総合スコア88

C

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

0グッド

1クリップ

投稿2020/03/18 02:26

編集2020/03/18 02:52

他の人から頂いたソースコードを解析しようとしている者です。元々組み込み用の開発環境で作成されたコードなのですが、同じ開発環境が準備できないためVisualStudio2010で構築しなおしたところ、下記のソースコードで下記エラーが発生します。

valはdouble型、dataはunsigned char型です。
valをint型にするとエラーは出なくなります。

C

1*(((unsigned char *)st1->val) + 0) = st2->data[cnt]; cnt++; 2*(((unsigned char *)st1->val) + 1) = st2->data[cnt]; cnt++; 3*(((unsigned char *)st1->val) + 2) = st2->data[cnt]; cnt++; 4*(((unsigned char *)st1->val) + 3) = st2->data[cnt]; cnt++; 5*(((unsigned char *)st1->val) + 4) = st2->data[cnt]; cnt++; 6*(((unsigned char *)st1->val) + 5) = st2->data[cnt]; cnt++; 7*(((unsigned char *)st1->val) + 6) = st2->data[cnt]; cnt++; 8*(((unsigned char *)st1->val) + 7) = st2->data[cnt]; cnt++;

C

1エラー 3 error C2440: '型キャスト' : 'double' から 'unsigned char *' に変換できません。

・このエラーが出る理由
・valをint型にするとエラーが出なくなる理由
・(もし可能なら)valをdouble型にしたままエラーを出さなくする方法
を教えていただけないでしょうか。

コードがやろうとしていることは、st2->dataにはdoubleの8byteデータが入っており、それをst1->valの先頭アドレスから8バイト分コピーしているものと想像します。st1->valの先頭アドレスを得るのが目的だと思ったので、試しにキャストのところを「(double char *)st1->val)」としましたが、上記同様のエラー('double *' に変換できません)が出ました。

以下、追記です。
・st1の型は下記です。(val以外の変数名は適当に変換しました)

C

1typedef struct { 2 unsigned char a; 3 unsigned short b; 4 unsigned char c; 5 unsigned int d; 6 unsigned short e; 7 unsigned int f; 8 double val; 9 double g; 10} GET_DETECTED_STAR_INFO_COMMAND_STRUCT; 11 12GET_DETECTED_STAR_INFO_COMMAND_STRUCT *st1

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

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

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

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

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

maisumakun

2020/03/18 02:43

st1の型は何でしょうか?
WEjpon

2020/03/18 02:52

st1の型を追記させていただきました。
dodox86

2020/03/18 03:25

すでにいただいている回答で指摘されているように、元の組み込み開発環境とターゲットで以下のコードが動いているのであれば、 > *(((unsigned char *)st1->val) + 0) = st2->data[cnt]; cnt++; double型のvalをアドレスとみなして動くコードは、Visual Studio 2010の環境下で動かない(確認のしようがない)のではないでしょうか。先に、その組み込みのターゲット環境で st2->data[0...7]に何が入っているか明らかにしないと、誤った解析作業になると思います。 > それをst1->valの先頭アドレスから8バイト分コピーしているものと想像します。 あくまで想像なのですよね。 どのような組み込み開発環境か分かりませんが、一般的にはコンパイル時に警告やエラーが出てもおかしくないコードです。その組み込み開発環境のコンパイラが、たまたま意図通りのコンパイル結果を吐き出していただけかもしれません。
WEjpon

2020/03/18 03:37

ありがとうございます。元の開発者とのコンタクトができそうであれば、意図を確認してみたいと思います。
guest

回答4

0

正直に書くと、元々の環境と、開発者意思が分からないと、この質問には答えられません。理由は、以下のように複数の可能性があるからです。

可能性1. &の書き忘れ
(((unsigned char)&st1->val) + 0) = st2->data[cnt];cnt++;
このようにすれば、valの領域を上書きできます。エラーの理由は、&をつけないと領域のアドレスを指定したことにならないからです。intに直すとコンパイルできてしまった理由は、「整数型は」ポインタ型に変換可能だからです。

可能性2. アドレス値がint型で表現できない。
環境によっては、int型とポインタ型でサイズが異なることがあります。例えば、int型が4バイト、ポインタ型が8バイト必要な環境では、アドレス値を保存するためにdouble型を利用する、という可能性もありえます。この場合、単に&をつけるだけでは直せません。幸い、新しい環境に移行したということなので、
(((unsigned char)((uintptr_t)&st1->val)) + 0) = st2->data[cnt];cnt++;
のようにすれば、doubleに収められているアドレス値を利用できます。

可能性2は低いですが、そもそも次のように書かなかった理由を開発者がコメントに残さないのが悪いのです。
st1->val = (double)&st2->data[cnt];cnt+=8;

投稿2020/03/18 02:55

majiponi

総合スコア1720

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

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

WEjpon

2020/03/18 03:25

ありがとうございます。(提示しておりませんでしたが)他の部分のコードを見る限り、私も可能性1が高いと思いました。
guest

0

ベストアンサー

こんにちは。

・このエラーが出る理由

ポインタでないものをポインタへ変更しようとしているからです。そのようなキャストはほとんどのケースでバグの筈というコンパイラ開発者の判断があるのだと思います。

・valをint型にするとエラーが出なくなる理由

昔は多くの環境でint型とポインタ型のバイト数が一致していたという事情もあり、そのような変換を行いたいケースがあったので例外的に許可しているのではないかと思います。

・(もし可能なら)valをdouble型にしたままエラーを出さなくする方法

dataにはdouble型のデータが入っていてそれをvalへコピーしている感じがします。(バグってますが)その通りの場合は、valそのものではなく、valへのポインタを unsigned char*型へキャストすれば良いです。
例えば、*(((unsigned char *)&(st1->val)) + 0) = st2->data[cnt];です。
(st1->val)の括弧は省略してもよいようですが、優先順位を覚えるのが面倒なので私は付けることが多いです。)

投稿2020/03/18 03:12

Chironian

総合スコア23272

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

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

WEjpon

2020/03/18 03:34 編集

ありがとうございます。すっきりしました。 理解したつもりなのですが・・・念のため1点だけ確認させてください。 st1にはunsigned short型のbもあります。bへの代入を意図したと思われるコードとして下記がありました。 *(((unsigned char *)st1->b) + 0) = st2->data[cnt]; cnt++; *(((unsigned char *)st1->b) + 1) = st2->data[cnt]; cnt++; 私の理解では、このコードは意図しないアドレスを書き換えているように思います。 下記のコードにするとst1->bの値を変更できるのに対し、上記コードではアドレス st1->bを書き換えてしまうと思ったからです。この理解でよいでしょうか? *(((unsigned char *)&st1->b) + 0) = st2->data[cnt]; cnt++; *(((unsigned char *)&st1->b) + 1) = st2->data[cnt]; cnt++;
Chironian

2020/03/18 04:56

修正されたコードはあっていると思いますが、short型のサイズに要注意です。 doubleもshortもほとんどの環境でそれぞれ8バイト、2バイトなので大丈夫とは思いますが。 > 上記コードではアドレス st1->bを書き換えてしまうと思ったからです。 st1->bに入っている値(不定値かも)をアドレスとみなして、そのアドレスのメモリを書き換えようとします。多くの場合、メモリ空間外のアドレスを指すので不正アクセスが発生しますが、たまたま有効なメモリだった場合、そこを破壊します。
WEjpon

2020/03/18 05:50

ありがとうございます。大変勉強になりました。
guest

0

割と理解に苦しむコードです
Luaのグルーコードで見たことありますけどね…
(UserData型を知らない人だったらしい)

((unsigned char *)st1->val)[0] = st2->data[cnt];

同じ構文で書き換えると、こういう事です。
つまり、st1->val というのは「メモリアドレス」が入ってます

ポインタが32bitの環境であれば、intとint*のサイズが完全に同じなので、キャストが成功します。
しかし、学習用やデバッグ用途としてメモリアドレスをみたい、という
特殊な用途でない限りこのような使い方は行いません。

doubleの場合、そもそもアドレスとして使う型ではないので、変換できません。
一度、intに変換すればキャストは成功します。

double型にメモリアドレスを入れているという無茶苦茶な設計なので、修正したほうがいいと思います

投稿2020/03/18 02:43

izmktr

総合スコア2856

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

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

WEjpon

2020/03/18 03:22

ありがとうございます。 もしst1->valにメモリアドレスが入っている場合、提示いただいたコードによりアドレスst1->valに格納された値が変更されると理解しました。
guest

0

st1->valの先頭アドレスを得るのが目的

ではないです

(double char *)&st1->val

としてみればどうですか?
この文の示す意味、そして元のコードの示す意味をも一度考え直しましょう

投稿2020/03/18 02:34

y_waiwai

総合スコア87800

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

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

WEjpon

2020/03/18 02:41

ありがとうございます。st1の前に&をつけることでエラーはなくなりました。理由がさっぱりわからないので、もう少し考えて見ます。
y_waiwai

2020/03/18 02:43

その変数のアドレスを得るなら、&をつけます。 基本的な文法のはなしなので、そこらへんしっかり調べましょう
y_waiwai

2020/03/18 02:50

そこらへんのことが理解できたなら、double値をポインタにキャストする、というのがいかに異常なことかはわかってくると思います。
WEjpon

2020/03/18 02:57 編集

後だしで申し訳ありません。 &でアドレスを得ることは知っていたつもりなのですが、実は上記コードの直前に下記のコードがあり、fは追記させていただいた通りunsigned int型なのですが、こちらは&をつけなくてもエラーになっていないため、それも含めてなぜかを考えて見ます。 *(((unsigned char *)st1->f) + 0) = st2->data[cnt]; cnt++; *(((unsigned char *)st1->f) + 1) = st2->data[cnt]; cnt++; *(((unsigned char *)st1->f) + 2) = st2->data[cnt]; cnt++; *(((unsigned char *)st1->f) + 3) = st2->data[cnt]; cnt++;
y_waiwai

2020/03/18 03:08

int型というのは整数型で、ナカミの構造に関してはポインタと同じようなものとなってます まあ、C言語の歴史上の経緯から、相互に代入可能となっている、というようなものなので、 その理由を考えるってのはちと無駄かと思います。 そういうもの、とおもっておきましょう
WEjpon

2020/03/18 03:17

ありがとうございます。やっと理解できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問