39671


C言語入門 (ASCII SOFTWARE SCIENCE Language)に

> できるだけ ANSI
> 規格に準拠して関数はプロトタイプ宣言をするようにしましょう。プロトタイプ宣言がされている関数の引数については、それが正しい型であるか否かをコンパイラがチェックするので、引数の型の不整合をコンパイル時に検出できます。

と書いてあったので

[code]

    int factorial(int);
    int main(void)
    {
      char x=5;
      printf("%d!=%d",x,factorial(x));
      return 0;
    }
    
    int factorial(int n)
    {
      if (n==1)
        return (1);
      else
        return (n*factorial(n-1));
    }
    
[/code]

と書いてgccでコンパイルしたんですが、コンパイル時にエラーが出ませんでした。  
(factorialはintなのに、実引数のxはchar型)

gccでは引数の型の不整合をチェックしないのでしょうか？  
(gccのバージョンは4.7.2です)


CC BY-SA 3.0
2017-11-17T23:47:30.930
39667
2017-11-29T06:00:34.630
2017-11-29T05:04:42.357
19110
25791
question
c
コンパイル時の引数の型の不整合のチェックについて


`char`→`int`とサイズを大きくする方向へは暗黙の型変換が認められているため特に警告されることはありません。

* * *

>
> 型が大きい方への変換であろうとも、暗黙の型変換ができるような場合は不整合と見なされず、エラーにならないという事‌​ですか。型の不整合のチェックというのは結構アバウトなんですね。びっくりしました。

[別質問](https://ja.stackoverflow.com/a/39923/4236)でC言語にはバージョンがあることが紹介されていますが、この中で「最初のC」ではプロトタイプ宣言の中に引数情報が含まれておらず、「関数が存在する」ことしか宣言されていませんでした。  
この場合、コンパイラーは引数をどのように扱うか分からないわけですが、そこで呼び出し規約に従って処理されます。具体的には呼び出しの際、整数は`int`に拡張され、実数は`double`に拡張されることが定められていました。もちろん呼び出された関数側としてもそのような引数しか受け取れないことになります。

>
[code]

>     void tekitou(void);
>     void tekitou();
>  
[/code]

と前者のようにプロトタイプ宣言の引数部に`void`を記述するのもこのためです。後者のように`void`を省略してしまうと、引数が存在しないのか、古いソースコードで引数が明示されていないのか判別できなくなります。  
なお、引数情報のないプロトタイプ宣言は古い概念ですが、現在においても可変引数をとる関数（の可変引数部分）については依然として引数情報が含まれていないため今なお有効な概念です。  
例えば`printf()`に`float`を渡しても呼び出し規約によって`double`に変換されているため`%lf`とフォーマット指定する必要があるのはこのためです。


CC BY-SA 3.0
2017-11-18T00:15:13.583
39669
2017-11-29T06:00:34.630
2017-11-29T06:00:34.630
4236
4236
39667
answer


関数の呼び出しについては[規格-リンクはＣ１１ドラフト-](http://www.open-
std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)に次のように書かれています。

> 6.5.2.2 Function calls  
>  7 If the expression that denotes the called function has a type that does
> include a prototype,the arguments are implicitly converted, as if by
> assignment, to the types of the corresponding parameters, (抜粋)

私訳（あくまで参考に）  
もし呼び出された関数を表す式がプロトタイプを含む型を持つ場合、引数は、対応する仮引数の型に代入されたかのように暗黙に型変換されます。

つまり、関数プロトタイプの引数の型が`int`の場合、

[code]

    char x=5;
    int n;
    n = x; 
    //あるいは
    n = (int)x;
    
[/code]

のように暗黙に変換が行われるということです。

余談ですが、  
例えば `x = n;` のような場合、表現できる範囲が小さい方向に代入が行われるような場合には、  
オーバーフローする（可能性がある）など（未検証で書いてます）の警告が出る場合があります。  
`double`などの場合も同じで、`double`を暗黙に`int`に変換した結果が引数として渡されます。  
いずれにせよ、（型の変換が行われて引数として渡されるので）型の不整合にはなりません。

これは、 **要するに数値としての互換性があると見なしている** からです。

例えば、引数として`char*`つまり`factorial(&x)`のように関数呼び出しした場合には、  
互換性のない型であるとか、キャストなしに（つまり暗黙に）ポインタから変換されたなどの警告（ウォーニング、警告であってエラーではありません。）がでます。

* * *
[code]

    test.c: In function 'main':
    test.c:7:31: warning: passing argument 1 of 'factorial' makes integer from pointer without a cast [-Wint-conversion]
       printf("%d!=%d",x,factorial(&x));
                                   ^
    test.c:3:5: note: expected 'int' but argument is of type 'char *'
     int factorial(int);
         ^
    
[/code]

`char *`を渡した場合  
ウチのＧＣＣでは **warning** でなく **note** になってました。


CC BY-SA 3.0
2017-11-18T03:54:17.017
39671
2017-11-18T04:16:44.850
2017-11-18T04:16:44.850
5044
5044
39667
answer