C言語の引数

RL78
CS+ forCC
でソフトを作っています。
例えば、関数 void xxfunc(uint32_t, uint32_t) があったとします。

メインで、
  uint16_t  aa;
  uint16_t  bb;
 
  aa = 1000;
  bb = 6000;
 xxfunc(aa, bb);
でコンパイルします。

引数の型が合わないのでワーニングが出ると思いましたが出ませんでいた。
暗黙の型変換だと思います。
このような場合でもワーニングを出すことはできますか?

  • ega258さん、こんにちは。NoMaYです。

    試しに探してみたのですが、ソースコードが公開されているLintとして、以下の2つがありました。あくまで可能性のひとつですが、(1)そのまま試して警告が出るか?(2)出なければソースを修正する、という方策もあるかもと思うのです。(ちなみにLCLint/SPLintでは警告は出ませんでした。ADLintはRubyをインストールしないといけないようでしたのでちょっと億劫になってしまい試していません。もちろんソースを修正するということが簡単なことでは無いことは承知の上での、それでも可能性のひとつかも知れない、ということのものです。)

    ・LCLint/SPLint (Cソース)

    sourceforge.net/projects/lclint/files/splint/3.0.1.6/
    lclint.cs.virginia.edu/guide/

    ・ADLint (Rubyソース)

    sourceforge.net/projects/adlint/files/adlint/3.2.14/
    adlint.sourceforge.net/pmwiki/upload.d/Main/users_guide_ja.html

    [追記]

    LCLint/SPLintの実行例です。元がPC向けなせいかuint32_tもuint16_tもunsigned intみたいでしたので、ちょっと小細工しています。(LCLint/SPLintのソースというか定義ファイルを変更すれば小細工は不要になると考えています。)

    CASE1) このパターンは警告される

    ソース

    typedef unsigned long ccrl_uint32_t;
    typedef unsigned short ccrl_uint16_t;

    void xxfunc(ccrl_uint16_t, ccrl_uint16_t);

    int main(void)
    {
      ccrl_uint32_t  aa;
      ccrl_uint32_t  bb;

      aa = 1000;
      bb = 6000;
      xxfunc(aa, bb);

      return 0;
    }

    結果

    >set LARCH_PATH=../lib

    >splint main.c
    Splint 3.0.1.6 --- 11 Feb 2002

    main.c: (in function main)
    main.c(13,10): Function xxfunc expects arg 1 to be ccrl_uint16_t gets
                      ccrl_uint32_t: aa
      Types are incompatible. (Use -type to inhibit warning)
    main.c(13,14): Function xxfunc expects arg 2 to be ccrl_uint16_t gets
                      ccrl_uint32_t: bb

    Finished checking --- 2 code warnings

    CASE2) このパターンは警告されない

    ソース

    typedef unsigned long ccrl_uint32_t;
    typedef unsigned short ccrl_uint16_t;

    void xxfunc(ccrl_uint32_t, ccrl_uint32_t);

    int main(void)
    {
      ccrl_uint16_t  aa;
      ccrl_uint16_t  bb;

      aa = 1000;
      bb = 6000;
      xxfunc(aa, bb);

      return 0;
    }

    結果

    >set LARCH_PATH=../lib

    >splint main.c
    Splint 3.0.1.6 --- 11 Feb 2002

    Finished checking --- no warnings

     

  • IKUZOさん
    uint16_tで0xFFFFのデータをuint32_tにキャストすると、ちゃんと0x0000FFFFとなります。
    0xFFFFFFFFにはなりませんけど。

    私の例えがよくなかったです。
    例えば、足し算や引き算、掛け算、割り算で、
    uint8_tとint16_tを計算した場合、きちんとキャストしておかないと
    結果がおかしくなる場合があるのではと思っています。
    コンパイラーで型違いを指摘してくれれば、未然に防げるのかなと思った次第です。
  • ega258さん
    長年やっていると、いろいろありますよね、最近コンパイラーでは警告とか出してくれるので、それを注意するということでしょうか、でも「型の違うところに適当に合わせない」ということが無難な線ですよね
    別の話ですがprintfなんかで
    char b=50;
    printf("%02X",b);
    なんかですと値によってはFFFFとか4桁になったりするので
    printf("%02X",(int)b & 0xFF);とかいろいろわからないことがあるのですが
    やっぱりコンパイラーの警告のみに頼らず、いろいろな値で実際に動作させて確認することだと思います。
  • ega258さん

    > 例えば、足し算や引き算、掛け算、割り算で、
    > uint8_tとint16_tを計算した場合、きちんとキャストしておかないと
    > 結果がおかしくなる場合があるのではと思っています。
    > コンパイラーで型違いを指摘してくれれば、未然に防げるのかなと思った次第です。

    コンパイラやlintの設計者も同じことを考えていて、
    「きちんとキャストしておかないと結果がおかしくなる(可能性がある)場合」については、
    コンパイラやlintなどはきちんと警告を出します。

    引っかかっているのは、
    「値の保持が保証され警告を出す意味がないuint16_tからuint32_tへのキャスト」
    などについても警告を出して欲しい、という点です。

    そんなキャストにまで警告出しても冗長なだけなのでは、と皆さん色々な事例を出して
    説明しているのかと思います。
  • IKUZOさん、

    > 値によってはFFFFとか4桁になったりするので

    C99 以降の仕様であれば printf の第1引数に長さ修飾子 hh を指定することで引数が char であることを明示できます。

    
    #include <stdio.h>
    
    int main(void)
    {
        char b = 0xff;
        printf("%02X\n",b);
        printf("%02hhX\n",b);
    }
    
    
    FFFFFFFF
    FF
    

    Wandboxで実行

  • fujita nozomuさん
    教えてくださってありがとうございます
    前にもお聞きしたような覚えがあったように思います
    C99以降"%02X"を2文字追加して"%02hhX"なんですね
    こんどは忘れないように努力します。
  • printfはコンパイラの機能ではなく単なるファンクションです。高い移植性を実現するために他の言語とは異なり PRINT, WRITE, READなどがアプリケーションで作成するファンクションと同じレベルになってます。ここがC言語のキモの一つです。
    「きちんとキャストしておかないと結果がおかしくなる(可能性がある)場合でもコンパイラがあえて警告を出さないのがC言語の基本的なコンセプトです。もちろんターゲットや応用が広がって変化はありますが、過去を引きずるのは避けられません。
    この話は、激辛レストランに入って料理が辛いと文句を言っているようなヘンな話です。
  • imudakさん

    >「値の保持が保証され警告を出す意味がないuint16_tからuint32_tへのキャスト」
    >などについても警告を出して欲しい、という点です。
    型違いの同士の計算(代入)を言いたかったため、そう書いたのです。
    「uint8_tとint16_tの掛算や割算などの演算を無事に行う書き方はありますか?」
    とお尋ねすれば良かったと反省しております。
  • ega258さん、こんにちは。NoMaYです。

    > 型違いの同士の計算(代入)を言いたかったため、そう書いたのです。
    > 「uint8_tとint16_tの掛算や割算などの演算を無事に行う書き方はありますか?」
    > とお尋ねすれば良かったと反省しております。

    これはまた別の方向へ迷走するだけのような気がします。思うに、ega258さんは変数の値の実行時チェックを行わない言語と相性が悪い(好みに合わない)、と表現されるような方向へ向かって行ってしまうかな、という気がします。(さらには、そういうチェックを行う言語はマイコン向けの言語としては一般的では(というか業務案件向けでは)無かったりします。)

    、、、でも、それでは不十分なのかな、、、そういう言語に乗り換えても、今度は、実行時エラーが起きないようにする書き方はありますか?という問いに置き換わりそうですね、、、

    う~ん、なんでしょうか、ポイントは2つあるのかな、という気がしてきました、、、

    (1) C言語に限らず殆どの言語では、所詮、完璧な対策は無理、である。
    (2) 型違いの同士の計算(代入)には、安全なものと安全では無いものの2種類、がある。

    あるいは、このスレッドとしても、私が以前に別スレッドでega258さんへ返信したように、以下の返信とするのが良いのかも知れない、という気もしてきました、、、

    CS+やe2studioのコンパイル設定
    japan.renesasrulz.com/cafe_rene/f/forum21/6333/cs-e2studio/35087#35087

    ●CC-RL

    自分が認識している範囲ですと -refs_without_declaration がこのカテゴリのものですね。(正確には、ワーニングにするかどうかでは無く、エラーにするかどうかの設定になりますけれども。) ヘルプでは、コンパイラ編→コマンドリファレンス→オプション→コンパイルオプション→機能拡張のグループ分けの下にあります。

    宣言がない関数を呼び出す場合,または古いスタイル(K&R)の宣言が書かれた関数呼び出しをエラーとします。

    [指定形式]
    -refs_without_declaration
    - 省略時解釈
      宣言がない関数を呼び出す場合,または古いスタイル(K&R)の宣言が書かれた関数を呼び出す場合にメッセージを出力しません。
    [詳細説明]
    - 宣言がない関数を呼び出す場合,または古いスタイル(K&R)の宣言が書かれた関数を呼び出す場合にエラーとします。


    ●CC-RX

    自分が認識している範囲では以前に以下のスレッドに書いたような状況ですが、その後、Amazon FreeRTOSのCC-RXへの移植を行った時に多少更に理解が深まっていて、特に続報とか投稿していませんが、自分が投稿するサンプルプログラムは少し細かい設定をしています。(カットアンドトライで、緩やか過ぎず、厳し過ぎず、の落としどころを探したものです。)

    CC-RXでWarning Levelを上げるOptionはインフォメーションメッセージ出力オプションだと知ったので試してみました
    japan.renesasrulz.com/cafe_rene/f/forum21/4434/cc-rx-warning-level-option

    ●CC-RH

    最新版のV2.02.00のヘルプを調べてみたのですが、このカテゴリのオプションは無いようでした。たぶん、緩やか過ぎず、厳し過ぎず、の落としどころに設定されているのだろうなぁ、と思います。(あるいは、実務上は、MISRA-Cルールチェックを行うのがデフォルトなので、そちらで細かい設定をするから、といったことだったりするのかも知れません。)

  • >「uint8_tとint16_tの掛算や割算などの演算を無事に行う書き方はありますか?」

    C言語ではint同士の演算でもオーバーフローや 0除算をさせないことはプログラマの責任になります。assert() をまめに用いる等でそれを検出することも不可能ではありませんが自動でそれを行う方法は通常用意されていません。

    C Swift Rust