「C言語でのプログラミングの現代的な方法」----第7章基本的なタイプ

第7章基本タイプ

7.1整数型

指定子の順序は重要ではないため、unsignedshortintはshortunsignedintと同じです。

この標準では、int型をshort int型より短くすることはできず、longint型をint型セグメントより短くすることはできません。ただし、short int型の値の範囲はint型の値の範囲と同じである可能性があり、int型の値の範囲もlongintの値の範囲と同じである可能性があります。

注:タイプに関係なく、それらの値の範囲はC標準で義務付けられておらず、コンパイラーごとに異なります。特定の実装の場合、整数型の範囲を決定する1つの方法は、<limits.h>ヘッダーを調べることです。このヘッダーは標準ライブラリの一部であり、各整数型の最大値と最小値を表すマクロを定義します。

7.1.2整数定数

8進定数には、0から7までの数値のみが含まれ、0から開始する必要があります。

16進定数には、0から9までの数字と、aからfまでの文字が含まれ、常に0xで始まります。

注:16進定数の文字は、大文字でも小文字でもかまいません。

コンパイラに定数を長整数として処理させるには、その後ろに文字L(またはl)を追加するだけです。符号なし定数であることを示すために、文字U(またはu)、U、およびLを追加できます。定数の後組み合わせて使用​​できます(文字UとLの順序と大文字小文字は関係ありません)。

7.1.4整数のオーバーフロー

符号付き整数演算でオーバーフローが発生したときのプログラムの動作は定義されていません。

符号なし整数演算中にオーバーフローが発生した場合、結果は明確に定義されます。正解はモジュロ2 nです。ここで、nは結果の格納に使用されるビット数です。たとえば、符号なし16ビット65535に1を加算すると、結果は0になることが保証されます。

7.1.5整数の読み取り/書き込み

int変数の1つがオーバーフローしたために機能しないプログラムがあるとします。最初の反応は、変数の型をintからlongintに変更することでした。ただし、これだけでは不十分です。データ型の変更がプログラムの他の部分に与える影響、特に変数がprintf関数またはscanf関数の呼び出しで使用されているかどうかも確認する必要があります。%dはint型でのみ機能するため、使用する場合は、呼び出しのフォーマット文字列を変更する必要があります。

  • 符号なし整数の読み取り/書き込みを行う場合は、変換仕様でdの代わりに文字u、o、またはxを使用してください。u指定子を使用すると、数値は10進数で読み書きされ、oは8進数、xは16進数になります。

  • 短整数を読み書きするときは、d、o、u、またはxの前に文字hを付けてください。

  • 長整数の読み取りまたは書き込みを行う場合は、d、o、u、またはxの前に文字lを付けてください。

  • (C99)長整数の読み取りまたは書き込みを行う場合は、d、o、u、またはxの前に文字llを付けてください。

7.2浮動小数点の種類

long doubleは非常に高い精度の要件をサポートし、ほとんど使用されません。

7.2.1浮動小数点定数

デフォルトでは、浮動小数点定数は倍精度で格納されます。

単精度のみが必要であることを示すには、定数の末尾に文字Fまたはfを追加できます。定数を長いdouble形式で格納する必要があることを示すには、文字Lまたはlを定数の末尾に追加できます。絶え間ない。

7.2.2浮動小数点数の読み取り/書き込み

変換指定子%e、%f、および%gは、単精度浮動小数点数の読み取りと書き込みに使用されます。

C99では、printf関数呼び出しで%le、%lf、および%lgを使用できますが、文字lは効果がありません。

7.3文字タイプ

文字定数は、二重引用符ではなく、一重引用符で囲む必要があります。

7.3.2符号付き文字と符号なし文字

標準Cでは、signedおよびunsignedという単語を使用してchar型を変更できます。

unsigned char x;
signed char x;

移植性の秘訣char型がデフォルトでsignedまたはunsignedであると想定しないでください。違いが生じる場合は、charをsignedcharまたはunsignedcharに置き換えてください。

7.3.4転送シーケンス

名前 エスケープシーケンス
アラーム(ベル)記号 \ a
フォールバックキャラクター \ b
フォームフィード \ f
改行 \ n
キャリッジリターン \ r
水平タブ \ t
垂直タブ \ v
バックスラッシュ \\
疑問符
アポストロフィ \ '
二重引用符 \ "
  • 8進数のエスケープシーケンスは、文字\とそれに続く最大3桁の8進数で構成されます。(数値は符号なし文字で表す必要があるため、最大値は通常8進数で377です。)たとえば、エスケープ文字は\33または\033と書くことができます。8進数の定数とは異なり、エスケープシーケンスの8進数は0で始まる必要はありません。
  • 16進エスケープシーケンスは、\xとそれに続く16進数字で構成されます。標準Cには、16進数の桁数に制限はありませんが、符号なし文字として表す必要があります(したがって、文字長が8ビットの場合、16進数の値はFFを超えることはできません)。この表記では、エスケープ文字は\x1bまたは\x1Bと書くことができます。文字xは小文字である必要がありますが、16進数(bなど)では大文字と小文字は区別されません。xは省略できません。

7.3.5文字処理機能

toupper()関数は、呼び出されたときにパラメーターが小文字であるかどうかをチェックし、そうである場合は、パラメーターを対応する大文字に変換します。そうでない場合、関数はパラメーターの値を返します。

toupper関数を呼び出すプログラムは、前処理命令を追加し、対応するヘッダーファイル<ctype.h>をインクルードする必要があります。

7.3.6scanfおよびprintfを使用した文字の読み取り/書き込み

scanf関数は空白文字をスキップしません。scanf関数が文字を読み取る前に空白文字をスキップするように強制するには、フォーマット文字列の変換仕様%cの前にスペースが必要です。

注:scanf形式の文字列の空白は、「0個以上の空白文字をスキップする」ことを意味します。

7.3.7getcharおよびputcharを使用した文字の読み取り/書き込み

注:getcharは、char型の値ではなく、int型の値を漠然と返します。scanf関数と同様に、getchar関数は読み取り時に空白文字をスキップしません。

プログラムを実行するときに、getchar関数とputchar関数を使用すると、(scanfやprintfよりも)多くの時間を節約できます。2つの理由があります:

  1. これらの2つの関数は、scanf関数とprintf関数よりもはるかに単純です。これは、scanf関数とprintf関数が、さまざまな種類のデータをさまざまな形式で読み書きできるように設計されているためです。
  2. 速度を上げるために、通常、getchar関数とputchar関数はマクロとして実装されます。

慣用的な使用法:

while(getchar()!='\n')
···
while((ch = getchar())==' ')
···

7.4型変換

暗黙の変換:

  • 算術式または論理式のオペランドの種類が同じでない場合。(C言語はいわゆる一般的な算術変換を実行します)
  • 代入演算子の右側の式の型が左側の変数の型と一致しない場合。
  • 関数呼び出しの実際のパラメータータイプが、対応する仮パラメータータイプと一致しない場合。
  • returnステートメントの式の型が関数の戻り値の型と一致しない場合。

7.4.1一般的な算術変換

整数拡張:文字または短整数をint型(または場合によってはunsigned int)に変換します。

整数拡張には2つのケースがあります。

  • いずれかのオペランドの型が浮動小数点型の場合より狭いタイプのオペランドは、次のようにプロモートされます。

    画像-20220303161659570

注:二項演算子の左右のデータの1つが特定のタイプである限り、より低いレベルのもう1つのタイプがアップコンバートされます。つまり、一方のオペランドがlong double型の場合、もう一方のオペランドはlongdouble型です。

  • 両方のオペランドの型が浮動小数点型ではない場合。最初に、その2つのオペランドで汎整数拡張を実行します(どちらのオペランドもchar型またはshort型ではないことを保証します)。次に、次の図に従って、より狭いタイプのオペランドが昇格されます。

    画像-20220303162706100

    longintとunsignedintの長さが同じ(36ビットなど)の場合にのみ発生する特殊なケースがあります。この場合、一方のオペランドがlong int型で、もう一方がunsigned int型の場合、両方のオペランドがunsignedlongint型に変換されます。

    注:strlen()関数とsizeof()のオペランドは、unsignedint型です。

7.4.2割り当て中の変換

一般的な算術変換は、割り当て演算には適用されません。C言語は、代入の右側の式を左側の変数の型に変換するという、別の単純な変換規則に従います変数の型が少なくとも式の型と同じくらい「広い」場合、この変換に対する障壁はありません。もちろん、特定のタイプの値をより狭いタイプの変数に割り当てると、無意味な結果が得られます。

以下に例を示します。

int a = 0;
unsigned x = 5;
int y = 5;
a = x + y;
//上面这个表达式将会发生两次类型转换,第一次,y将从有符号类型转换为有符号类型,第二次,x+y表达式的结果,将从无符号类型转换成有符号类型。

7.4.3C99での暗黙の変換

変換ルールを定義するために、C99では各整数型に「整数変換レベル」を設定できます。以下に、高いものから低いものの順にリストします。

  1. long long int、unsigned long long int
  2. long int、unsigned long int
  3. int、unsigned int
  4. short int、unsigned short int
  5. char、signed char、unsigned char
  6. _Bool

C99は、C89の整数拡張を整数拡張に置き換えます。整数拡張は、intおよびunsigned intよりも低い任意のタイプのランクをitn(そのタイプのすべての値がintタイプで表すことができる限り)またはunsignedintに変換できます。

C99で一般的な算術置換を実行するためのルールは、次の2つの場合に分けることができます。

  • いずれかのオペランドの型が浮動小数点型の場合。どちらのオペランドも複雑でない限り、ルールは以前と同じです。
  • 両方のオペランドの型が浮動小数点型ではない場合。整数拡張は、最初に2つの数値に対して実行されます。この時点で、2つのオペランドが同じタイプの場合、プロセスは終了します。それ以外の場合は、次のルールが順番に試行され、該当するルールが検出されると、他のルールは考慮されません。
    • 両方のオペランドが符号付きまたは両方が符号なしの場合は、下位整数変換レベルのオペランドを上位レベルのオペランドの型に変換します。
    • 符号なし数値のランクが符号付きオペランドのランク以上の場合は、符号付きオペランドを符号なしオペランドの型に変換してください。
    • 符号付きオペランドタイプが符号なしオペランドタイプのすべての値を表すことができる場合は、符号なしオペランドを符号付きオペランドタイプに変換します。
    • それ以外の場合は、両方のオペランドを、符号付きオペランドの型に対応する符号なし型に変換します。

また、==すべての算術型を_Bool型に変換できます。==元の値が0の場合、変換の結果は0です。それ以外の場合、結果は1です。

7.4.4強制

  1. C言語は、(型名)を単項演算子として扱います。単項演算子は、二項演算子よりも優先されます。したがって、コンパイラは式を配置します

    (float)dividend / divisor
    

    として解釈

    ((float)dividend)/divisor
    
  2. オーバーフローを避けるためにキャストを使用する必要がある場合があります。

    long i;
    int j = 1000;
    i = j*j;
    

    2つのint型の値を乗算すると、結果もint型になるはずですが、j * jの結果が大きすぎて、マシンでint型として表すことができないため、オーバーフローが発生します。この場合、キャスト型変換を使用して回避できます。この問題の発生:

    i = (long)j*j;
    

7.5タイプ定義

タイプ定義は、新しいタイプを設定できます。

typedef int Bool;

==注:定義されているタイプの名前は最後に配置されます。(#defineは、新しい名前(使用する文字名)を前に付けることです)==同時に、定義された型名の最初の文字を大文字に設定することがよくあります。

7.5.1型定義の利点

  1. タイプ名に実際の意味を割り当てることができるため、タイプ定義が理解しやすくなります。

  2. タイプ定義により、プログラマーは理解しやすくなります。

  3. 単なる置換である#definesとは異なり、型定義は新しいデータ型を生成します以下に例を示します。

    #define Pint int*;
    typedef int* Ptr_int;
    Pint a,b;//被替换后是这样的 int *a,b;
    Ptr_int c,d;
    

    ここで、aはポインター型、bは整数型、cとdは両方とも整数ポインターデータ型です。

  4. Typedefという名前のオブジェクトには、変数と同じスコープ規則があります。関数内で定義されたtypedef名は、関数外では認識されません。

7.5.2型の定義と移植性

タイプ定義により、プログラムの移植性が向上します。

C99では、<stdint.h>ヘッダーはtypedefを使用して、特定のビット数を占める整数型名を定義します。たとえば、int 32_tは、正確に32ビットを占める符号付き型です。これは、プログラムを定義してより移植性の高いものにするための効率的な方法です。

7.6sizeof演算子

sizeof(表达式);

知らせ:

  1. sizeof()の値は、型名に属する値を格納するために必要なバイト数を表す符号なし整数です。

  2. sizeofの後に特定のデータ型を追加する場合は、括弧を追加する必要がありますが、変数名を追加する場合は、括弧を省略できます。

  3. sizeof()演算子は、コンパイラ自体が通常sizeof式の値を判別できるため、特殊な種類の演算子です。

  4. 一般式の操作は実行時に実行されますが、sizeofはコンパイル段階で実行される演算子であり、その中の操作は実行されず、式の結果の型のみがそのサイズを見つけるために推測されます。**したがって、sizeofの式は実際には実行されません。内部の式に副作用があったとしても、変数には影響しません。次に例を示します。

    画像-20220303191022527

    sizeof()括弧内の変数aに対して++操作が実行されますが、aは変更されません。

    注:C89では、通常、コンパイラー自体がsizeof式の値を判別できます。ただし、配列内の要素の数はプログラムの実行中に変化する可能性があるため、コンパイラーは可変長配列のサイズを判別できません。

Q&A

  1. Q:%oと%xは、それぞれ8進数と16進数で符号なし整数を書き込むために使用されますが、通常の(符号付き)整数を8進数と16進数でどのように書き込みますか?

    回答:符号付き整数の値が負でない限り、%oおよび%xで表示できます。これらの変換により、print関数は符号付き整数を符号なしとして扱います。つまり、printf関数は、符号ビットが数値の絶対値部分であると想定します。符号ビットが0であれば問題ありません。符号ビットが1の場合、印刷機能はプリフェッチを超えて多数を表示します。

  2. Q:しかし、数値が負の場合はどうなりますか?8進数または16進数で書く方法は?

    回答:数値が負であるかどうかを判断し、自分で負の符号を表示することができます。

    if(i < 0)
    	printf("-%x",-i);
    else
    	printf("%x",i);
    
  3. Q:なぜ%lfを使用してdouble型の値を読み取るのに、%fを使用して表示するのですか?

    A:これは答えるのが非常に難しい質問です。まず、scanf関数とprintf関数はどちらも、関数の引数を固定数に制限しないという点で珍しい関数であることに注意してください。scanf関数とprinf関数には、可変長のパラメーターリストがあります。可変長パラメーター・リストを使用して関数を呼び出す場合、コンパイラーは、floatパラメーターが自動的にdouble型に変換されるように調整します。その結果、printf関数はfloat型とdouble型パラメーターを区別できません。これは、%fを使用してprintf関数呼び出しでfloatパラメーターとdoubleパラメーターの両方を表すことができる理由を説明しています。

    一方、scanf関数は、ポインターを介して変数を指します。%fは、渡されたアドレスにfloat型の値を格納するようにscanf関数に指示し、%lfは、アドレスにdouble型の値を格納するようにscanf関数に指示します。ここでは、floatとdoubleの違いが非常に重要です。間違った変換仕様が指定されている場合、scanf関数は間違ったバイト数を格納する可能性があります(float型のビットパターンがdouble型のビットパターンと異なる可能性があることは言及されていません)。

  4. Q:エスケープシーケンスを使用する目的は何ですか?

    回答:3文字のシーケンスは??で始まるため、エスケープシーケンス?は3文字のシーケンスに関連しています。文字列に??を追加する必要がある場合、コンパイラはそれを3文字のシーケンスの開始として扱う可能性があります。2番目の?を?に置き換えると、問題が解決します。

  5. Q:getchar関数の方が読み取りが速いのに、なぜscanf関数を使用して1文字を読み取る必要があるのですか?

    回答:scanf関数はgetcharほど高速ではありませんが、より柔軟性があります。すでに見たように、フォーマット文字列 "%c"により、scanf関数は次の入力文字を読み取ります。 "%c"は、次の非空白文字を読み取ります。また、scanfは、他のデータ型と混合された文字を読み取るのに適しています。

  6. Q:整数拡張は、どのような状況で文字または短整数をunsigned int型に変換しますか?

    回答:int型の整数がプリミティブ型のすべての可能な値を含むのに十分な大きさでない場合、整数拡張はunsignedint型を生成します。文字は通常8ビット長であるため、ほとんどの場合、intに変換されます(少なくとも16ビット長であることが保証されています)。符号付きのショートパンツはいつでもintに変換できますが、符号なしのショートパンツには問題があります。短整数と通常の整数が同じ長さの場合(たとえば、16ビットマシンの場合)、最大のunsigned short(16ビットマシンの場合は65535)が最大値よりも大きいため、unsignedshortをunsignedintに変換する必要があります。 int整数(32767)。

  7. Q:変数の値の範囲外の値を変数に割り当てると、正確にはどうなりますか?

    答え:整数型で変数が符号なし型の場合、超過ビット数は破棄されます。変数が符号付き型の場合、結果は実装定義になります。整数または浮動小数点変数に符号付き数値を割り当てると、変数が小さすぎて処理できない場合、未定義の動作が発生します。プログラムの終了など、何かが発生する可能性があります。

おすすめ

転載: blog.csdn.net/m0_57304511/article/details/124060741