
C構文は、テキストがCプログラミング言語コードとなるために必要な形式です。言語構文規則は、簡潔で、結果として得られるオブジェクトコードと密接な関係を持ちながら、比較的高レベルのデータ抽象化を提供するコードを可能にするように設計されています。Cは、移植可能なオペレーティングシステム開発のための最初の広く成功した高レベル言語でした。 [ 1 ] C構文は、最大マンチ原則を利用します。自由形式の言語として、Cコードは構文の性質に影響を与えることなく、さまざまな方法でフォーマットできます。C構文は、C++、Java、C#などの後続の言語の構文に影響を与えました。
C言語のコードは、プリプロセッサディレクティブ、コア言語の型、変数、関数から構成され、1つ以上のソースファイルとして整理されています。コードのビルドは通常、各ソースファイルをプリプロセス処理した後、コンパイルしてオブジェクトファイルを作成する工程で行われます。その後、オブジェクトファイルをリンクして実行可能イメージを作成します。
変数と関数は、定義とは別に宣言することができます。宣言では、ユーザー定義要素の名前と、実行時にその要素がどのように使用されるかに関する情報の一部(すべてではない場合もある)が示されます。定義は、宣言の内容に加えて、要素を完成させるための追加情報を含む、要素の完全な記述です。たとえば、関数宣言では、関数の名前と、オプションで引数の型と数を指定します。関数定義には、同じ情報(引数の情報は必須)に加えて、関数のロジックを実装するコードが含まれます。

ホスト環境の場合、プログラムはという名前のエントリポイント関数から開始しますmain。この関数には 2 つの引数が渡されますが、関数の実装ではこれらを無視できます。この関数は、以下のプロトタイプのいずれかに従って宣言する必要があります (示されているパラメータ名は典型的なものですが、任意の名前を使用できます)。
int main (); int main ( void ); int main ( int argc , char * argv []); int main ( int argc , char ** argv ); int main ( void ); int main ( int argc , char * argv [], char * envp []); int main ( int argc , char ** argv , char ** envp ); int main ( int argc , char * argv [], char ** envp ); int main ( int argc , char ** argv , char * envp []);最初の2つの定義は同等であり、関数は2つの引数を使用しません。次の2つも同等であり、関数はコマンドライン引数を読み取るために必要な2つの引数にアクセスできます。最後の4つも同等であり、環境変数とコマンドライン引数の両方にアクセスできます。
戻り値は 型で表されint、ホスト環境へのステータスインジケータとして機能します。 で定義されているように<stdlib.h>、標準ライブラリは標準ステータス値 と のマクロを提供しますEXIT_SUCCESS。EXIT_FAILUREただし、プログラムは任意の値を使用してステータスを示すことができます。たとえば、killコマンドはシグナルの数値に 128 を加えた値を返します。
最小限のプログラムはmain、次のような空の関数で構成されます。
int main () {}他の関数とは異なり、この言語[ a ]では、プログラムがreturn文で終了していなくても、0を返すかのように動作することが要求されます。[ 2 ]
オペレーティングシステムを持たないシステムなど、独立した(ホストされていない)環境では、標準規格では異なる起動処理が認められています。特定の関数を必須とする必要はありません。main
プログラムを起動するためのコマンドラインに含まれる引数は、2つの値としてプログラムに渡されます。1つは引数の数(通常、 という名前)、もう1つはプログラム名を最初の要素とするヌル終端文字列argcの配列(通常、 という名前)です。argv
以下のコードは、コマンドラインパラメータの値を表示します。
#include <stdio.h>int main ( int argc , char * argv []) { printf ( "argc \t = %d \n " , argc ); for ( int i = 0 ; i < argc ; ++ i ) { printf ( "argv[%i] \t = %s \n " , i , argv [ i ]); } }$ ./a.out abc def argc = 3 argv[0] = ./a.out argv[1] = abc argv[2] = def以下の単語は予約語であり、識別子として使用することはできません。その数は43語です。
alignasalignofautoboolbreakcasecharconstconstexprcontinuedefaultdodoubleelseenumexternfloatforgotoifinlineintlongregisterrestrictreturnshortsignedsizeofstaticstatic_assertstructswitchthread_localtypedeftypeoftypeof_unqualunionunsignedvoidvolatilewhile以下のキーワードは、マクロまたは上記のリストにある適切なキーワード(全部で14個)の代わりによく使用されます。以下のキーワードの一部は、C23以降非推奨となっています。
_Alignas(非推奨)_Alignof(非推奨)_Atomic_BitInt_Bool(非推奨)_Complex_Countof_Decimal32_Decimal64_Decimal128_Generic_Noreturn(非推奨)_Static_assert(非推奨)_Thread_local(非推奨)このキーワードはC2Y_Imaginaryで削除されました。
以下の単語は、その言語で使用される文字通りの値を指し、3つあります。
nullptrtruefalse実装によっては他のキーワードを予約している場合もありますが、通常はアンダースコアが 1 つまたは 2 つで始まる非標準キーワードが提供されます。以下のキーワードは拡張機能として分類され、条件付きでサポートされています。その数は 2 つです。
以下はプリプロセッサへの指示であり、全部で19個あります。
#if#elif#else#endif#ifdef#ifndef#elifdef#elifndef#define#undef#include#embed#line#error#warning#pragma#__has_include#__has_embed#__has_c_attributeこの_Pragma演算子は、によって提供される機能の代替構文を提供します#pragma。
コメント( プログラマー向けの情報テキストで、言語翻訳ツールでは無視される)は、行コメントまたはブロックコメントの構文でコードに含めることができます。行コメントは で始まり//、同じ行の末尾で終わります。ブロックコメントは で始まり/*、 で終わります*/。ブロックコメントは、任意の行数(または1行)にまたがります。
状況によっては、コメントマーカーは無視されます。文字列リテラルのテキストは、コメントの開始とはみなされません。また、コメントはネストできません。たとえば、/*行コメント内の は、ブロックコメントの開始とはみなされません。また、//ブロックコメント内の は、行コメントの開始とはみなされません。
行コメント構文(C++スタイルとも呼ばれる)はBCPLで生まれ、 C99で有効な構文となった。オリジナルのK&R版やANSI Cには含まれていない。
以下のコードはコメントの例を示しています。1行目には行コメントが、3行目と4行目にはブロックコメントが含まれています。4行目は、ブロックコメントを、その前後にコードがある行に埋め込むことができることを示しています。
int i ; // 行コメント/* ブロック コメント */ int ii = /* 常にゼロ */ 0 ;以下は、コメント構文における潜在的な問題を示しています。本来は除算演算子/と逆参照演算子であるはずのものが*、ブロックコメントの開始として評価されてしまいます。
x = * p /*q;以下のテキストは、コメントがネストされないため、有効なC言語の構文ではありません。3~5行目は、1~7行目のコメントブロックの中にネストされたコメントのように見えます。しかし実際には、5行目で1行目から始まったコメントが終了します。そのため、6行目はコードとして解釈されてしまいますが、これは明らかに有効なC言語の構文ではありません。
/*コメントブロックの最初の行/*内部ブロックとして意図されている行の最初の行*/コンパイラはこの行をコードとして扱いますが、これは無効です!*/構文はユーザー定義の識別子をサポートしています。識別子は文字(AZ、az)またはアンダースコア(_)で始まり、後続の文字は文字、数字(0~9)、またはアンダースコアにすることができ、予約語であってはなりませんfoo。識別子は大文字と小文字を区別するため、、、FOOおよびはFoo区別されます。
数学表記に準拠した式を評価する方法は複数存在する可能性があります。たとえば、は、、、の順、または、、、の順で評価される場合があります。(1+1)+(3+3)(1+1)+(3+3)(2)+(3+3)(2)+(6)(8)(1+1)+(3+3)(1+1)+(6)(2)+(6)(8)
評価順序による実行時の問題を軽減しつつ、ある程度の最適化を図るため、規格では、式は以下のいずれかとして定義されるシーケンスポイント間で任意の順序で評価できると規定している。
&&読み替え可能)と論理OR( 、 or else と読み替え可能)||?:):この演算子は、まず最初の部分式を評価し、次に最初の値に基づいて2番目または3番目(両方ではない)を評価します。シーケンスポイントより前の式は、シーケンスポイントより後の式よりも常に先に評価されます。短絡評価の場合、最初の式の結果によっては、2 番目の式が評価されないことがあります。たとえば、式 では(a() || b())、最初の引数がゼロ以外 (真) と評価された場合、式全体の結果は真以外になり得ないため、b()は評価されません。同様に、式 では、最初の引数がゼロ (偽) と評価された場合、式全体の結果は偽以外になり得ないため、は評価されません。(a()&&b())b()
関数呼び出しの引数は、関数が実行されるまで全て評価されていれば、どの順序で評価されても構いません。例えば、次の式は未定義の動作をします。
printf ( "%s %s \n " , argv [ i = 0 ], argv [ ++ i ]);コードは、ディレクティブを使用して他のファイルからインクルードされます。#includeこのディレクティブは、ファイルの内容をテキストとしてその場所にコピーします。従来、C コードはヘッダー ファイル (拡張子.h ) とソース ファイル (拡張子.c ) に分割されていました。ヘッダーにはシンボルの宣言が含まれ、ソース ファイルには完全な実装が含まれています。この分離は、ライブラリがインクルードされるたびにコンパイラが同じソース コードを繰り返し再コンパイルしてコンパイル時間を肥大化させるのを防ぐために強制されています。ヘッダーと個別の翻訳単位には名前空間の概念がないため、すべてのシンボル名はグローバルであり、同じ翻訳単位に複数のオブジェクトに対して同じ名前が存在すると衝突します。
ヘッダーがファイルに複数回含まれるのを防ぐには、#includeガードまたは#pragma once を使用できます。
C言語のヘッダーファイルは、コンパイラによる処理が高速化されるプリコンパイル済みヘッダーファイルにコンパイルできます。一般的に、変更頻度の低いライブラリ(C標準ライブラリのヘッダーファイルなど)は、プロジェクトでのコンパイル速度を向上させるためにプリコンパイルしておくことができます。
インクルードディレクトリ内の検索と相対パス内の検索を区別するには、インクルードディレクトリには山括弧を、相対パスには引用符を使用します。
#pragma once// 標準ライブラリのインクルード#include <stdio.h> #include <stdlib.h>// ローカル#include "Personalities.h" #include "Math/SpecialFunctions.h"// 外部ライブラリ#include <sqlite3.h>Clangには、 C++のモジュールに似ているものの、意味論的に異なる「モジュール」と呼ばれる非標準機能があります。どちらも、翻訳単位を一度コンパイルすることでコンパイル時間を短縮するために使用されます。
この#embedディレクティブを使用すると、有効なCコードではない場合でも、バイナリコンテンツをファイルに埋め込むことができます。
constexpr char ICON_DISPLAY_DATA [] = { #embed "art.png" };// 整数定数式から初期化できる任意の型を指定しますconstexpr char RESET_BLOB [] = { #embed "data.bin" };// 属性も同様に機能しますalignas ( 8 ) constexpr char ALIGNED_DATA_STRING [] = { #embed "attributes.xml" };int main () { return #embed </dev/urandom> limit(1) ; }この言語は、整数値と実数値のための基本的な数値型をサポートしており、これらは通常、中央処理装置(CPU)の命令セットアーキテクチャに直接対応します。整数データ型は整数のサブセットに値を格納し、実数値データ型は浮動小数点数の実数のサブセットに値を格納します。複素数データ型は2つの実数値を格納します。
整数型にはsignedと のunsignedバリアントがあります。どちらも指定されていない場合はsigned、ほとんどの場合 が想定されます。ただし、歴史的な理由から、 はと のchar両方とは異なる型です。コンパイラと文字セットによっては、符号付きまたは符号なしになります (標準では、基本文字セットのメンバーは正の値を持つ必要があります)。また、として指定されたビットフィールド型も、コンパイラによっては符号付きまたは符号なしになります。signed charunsigned charint
整数型にはさまざまな固定サイズがあり、さまざまな範囲の数値を表現できます。この型はchar正確に 1バイト(アドレス指定可能な最小の記憶単位) を占め、通常は 8 ビット幅です。 ( は任意のchar「基本」文字を表現できますが、国際文字セットにはより広い型が必要になる場合があります。) ほとんどの整数型には、とキーワードで指定される符号付きと符号なしの両方の種類があります。符号付き整数型は、 C23 [ 3 ]以降(実際にはそれ以前も)常に2 の補数表現を使用します。C23 より前のバージョンでは、表現は1 の補数または符号と絶対値であった可能性がありますが、実際には現代のハードウェアでは数十年間そのようにはなっていません)。多くの場合、この型を指定するには複数の同等の方法があります。たとえば、と は同義です。signedunsignedsigned short intshort
一部の型の表現には、未使用の「パディング」ビットが含まれる場合があります。これは記憶領域を占有しますが、幅には含まれません。以下の表は、整数型を可能な限り短い名前で示し、最小幅をビット単位で示しています。
型は と の両方とはchar異なりますが、いずれか一方と同じ表現を持つことが保証されています。と型は 1999 年以降標準化されており、古いコンパイラではサポートされていない可能性があります。 型は通常、標準ヘッダー<stdbool.h>で定義されている名前でアクセスされますが、C23 以降、型は に名前が変更され、は非推奨になりました。signed charunsigned char_Boollong long_Booltypedefbool_Boolbool<stdbool.h>
一般的に、特定のプラットフォームに実装される型の幅と表現方式は、マシンアーキテクチャに基づいて選択され、他のプラットフォーム向けに開発されたソースコードのインポートの容易さも考慮されます。型の幅はint特に翻訳者によって異なり、多くの場合、プラットフォームにとって最も「自然な」ワードサイズに対応します。標準ヘッダーファイル<limits.h>は、特定のプラットフォームに実装される標準整数型の最小値と最大値を表すマクロを定義します。
標準の整数型に加えて、標準ヘッダーの s に使用できる「拡張」整数型が存在する場合があります。幅をより正確に指定するには、プログラマは標準ヘッダー<stdint.h>typedefの sを使用できますし、使用すべきです。typedef
整数定数は、ソースコード内でいくつかの方法で指定できます。数値は、10 進数(例: 1022)、8 進数 ( )0を接頭辞として ( 01776)、または16 進数0x( 0 x ) を接頭辞として ( ) で指定できます0x3FE。シングルクォーテーションで囲まれた文字 (例: 'R') は「文字定数」と呼ばれ、実行文字セットにおけるその文字の値を表し、型は ですint。文字定数を除き、整数定数の型は、指定された値を表すために必要な幅によって決まりますが、常に 以上の幅になりますint。これは、明示的な長さおよび/または符号付き修飾子を追加することで上書きできます。たとえば、12luの型は ですunsigned long。負の整数定数はありませんが、多くの場合、単項否定演算子 " を使用することで同じ効果が得られます-。
キーワードで指定され、しばしば単に「enum」(通常は / ˈ iː n ʌ m / EE -numまたは/ ˈ iː n uː m / EE -noomと発音される)と呼ばれる列挙型は、一連の名前付き定数にわたる値を表現するように設計された型です。列挙定数のそれぞれは型を持ちます。各型自体は、または符号付きまたは符号なし整数型と互換性がありますが、各実装は型を選択するための独自のルールを定義します。enumintenumchar
列挙型を持つオブジェクトに定数以外の値が代入された場合、コンパイラによっては警告を発するものがあります。しかし、そのようなオブジェクトには互換性のある型の範囲内の任意の値を代入でき、enum定数は整数が期待される場所であればどこでも使用できます。そのため、名前付き定数を作成する際には、enumプリプロセッサ#defineディレクティブの代わりに値が使用されることがよくあります。このような定数は、特定の識別子名前空間内に存在するため、一般的にマクロよりも安全に使用できます。
列挙型は、enum指定子と列挙型のオプションの名前(またはタグ)で宣言され、その後に中括弧で囲まれカンマで区切られた 1 つ以上の定数のリストと、オプションの変数名のリストが続きます。特定の列挙型への以降の参照では、enumキーワードと列挙型の名前を使用します。デフォルトでは、列挙型の最初の定数にはゼロが割り当てられ、以降の各値は前の定数より 1 ずつ増加します。宣言で定数に特定の値を割り当てることもでき、特定の値が割り当てられていない以降の定数には、その時点から増加した値が割り当てられます。たとえば、次の宣言を考えてみましょう。
enum Color { RED , GREEN , BLUE = 5 , YELLOW } paint_color ;これはenum Color型、int定数RED(値が 0 )、GREEN(値が より 1 大きいRED1 )、BLUE(値が指定された値 5 )、およびYELLOW(値が より 1 大きいBLUE6 )、そしてenum Color変数を宣言しますpaint_color。定数は のコンテキスト外(任意の整数値が許容される)で使用でき、定数以外の値をまたは 型の他の変数enumに代入できます。paint_colorenum Color
C++とは異なり、C言語の列挙型にはスコープがありません。C言語には名前空間の概念がないためです。C言語では、enum列挙型は暗黙的に数値型に変換されることがありますが、これは型安全ではありません。
typedef enum Color { RED , ORANGE , YELLOW , GREEN , BLUE , INDIGO , VIOLET } Color ;Color c = RED ; // C言語の場合Color d = Color :: RED ; // C++の場合(ただしC言語では不可)enumC23以降では、 C++のように、の基となる型を手動で指定することが可能です。
enum CardSuit : char { HEARTS , CLUBS , SPADES , DIAMONDS };浮動小数点形式は、小数部分を持つ数値を表現するために使用されます。ただし、浮動小数点形式はほとんどの有理数を正確に表現するものではなく、近似値として扱われます。実数値には、指定子によって示される 3 つの標準型があります ( C23以降はさらに 3 つの小数型が追加されています)。単精度 ( float)、倍精度 ( double)、倍精度拡張 ( long double) です。これらのそれぞれは、異なる形式で値を表現することができ、多くの場合、IEEE 浮動小数点形式のいずれかになります。
浮動小数点定数は、10 進数表記1.23で記述できます。例: 。 10 進数の科学表記はe、またはを追加し、Eその後に 10 進数の指数を付けることで使用できます。これはE 表記とも呼ばれます。例: 1.23e2(値は 1.23 × 10 2 = 123.0 です)。小数点または指数のいずれかが必要です (そうでない場合、数値は整数定数として解析されます)。16 進数の浮動小数点定数も同様の規則に従いますが、 を接頭辞として付け、または0xを使用して2 進数の指数を指定する必要があります。例: (値は 2.5 です。A h × 2 −2 = 10 × 2 −2 = 10 ÷ 4 です)。 10 進数と 16 進数の浮動小数点定数の両方に、型の定数を示すためにまたはを接尾辞として付け、型を示すために(文字) またはを付け、定数の場合は接尾辞を付けずに残します。pP0xAp-2fFfloatllLlong doubledouble
標準ヘッダーファイル<float.h>は、実装の浮動小数点型、、およびの最小値と最大値を定義します。また、浮動小数点数の処理に関連するその他の制限も定義します。floatdoublelong double
C23では、3つの追加の10進数(2進数とは対照的)実数浮動小数点型、、、_Decimal32および_Decimal64が導入されます_Decimal128。
float注:C は、、、doubleおよびの基数を指定していません。実装では、、、およびlong doubleの表現を10 進浮動小数点型と同じにすることができます。[ 4 ]floatdoublelong doubleそれにもかかわらず、基数は歴史的にバイナリ(基数2)であり、1/2や1/4のような数値は正確ですが、1/10、1/100、1/3は正確ではありません。10進浮動小数点では、同じ数値はすべて正確ですが、1/10や1/100のような数値も正確ですが、1/3などは依然として正確ではありません。既知の実装では、以前はバイナリ型として知られていた数値に対して10進基数を採用しているものはありません。ほとんどのコンピュータは10進数型用のハードウェアさえ持っておらず、持っているごく少数のコンピュータ(例えば、IBM System z10以降のIBMメインフレーム)は明示的に10進数型を使用できます。
次の表は、期間(静的(グローバルのデフォルト)、自動(ローカルのデフォルト)、動的(割り当て済み))など、さまざまなストレージ属性を定義する指定子について説明しています。
malloc()ライブラリ関数を使用してメモリを割り当て、解放しますfree()。ブロック内で宣言された変数は、デフォルトで自動ストレージを持ちます。[注2 ]またはストレージクラス指定子を使用して明示的に宣言された変数も同様です。および指定子は、関数および関数引数宣言内でのみ使用できます。そのため、指定子は常に冗長です。すべてのブロックの外で宣言されたオブジェクト、およびストレージクラス指定子を使用して明示的に宣言されたオブジェクトは、静的ストレージ期間を持ちます。静的変数は、デフォルトでコンパイラによってゼロに初期化されます。autoregisterautoregisterautostatic
自動ストレージを持つオブジェクトは、宣言されたブロック内でローカルであり、ブロックが終了すると破棄されます。さらに、registerストレージクラスで宣言されたオブジェクトは、コンパイラによってレジスタへのアクセスに対してより高い優先順位が与えられる場合がありますが、コンパイラは実際にレジスタに格納しない場合もあります。このストレージクラスを持つオブジェクトは、アドレス&演算子 ( ) では使用できません。静的ストレージを持つオブジェクトは、プログラムの実行期間全体にわたって保持されます。このようにして、同じオブジェクトに関数が複数回呼び出してもアクセスできます。ストレージ期間が割り当てられたオブジェクトはmalloc、、freeおよび関連関数を使用して明示的に作成および破棄されます。
ストレージexternクラス指定子は、オブジェクトのストレージが別の場所で定義されていることを示します。ブロック内で使用される場合、ストレージはそのブロック外の宣言によって定義されていることを示します。すべてのブロックの外側で使用される場合、ストレージはコンパイル単位の外側で定義されていることを示します。externストレージクラス指定子は、関数宣言で使用される場合は冗長です。これは、宣言された関数がコンパイル単位の外側で定義されていることを示します。
(C23より前、およびヘッダーファイルが含まれている以前のバージョンの C では)ストレージクラス指定子は、C11で導入され、スレッドローカル変数を宣言するために使用されます。リンケージを決定するために、またはと組み合わせて使用thread_localできます。_Thread_local<threads.h>staticextern
ストレージ指定子は関数とオブジェクトにのみ適用されることに注意してください。型や列挙型の宣言などは、それらが記述されているコンパイル単位内でのみ有効です。一方、型には修飾子があります(下記参照)。
C23以降、Cでは型推論auto変数を宣言するために使用できます。
型は、そのデータの特別な特性を示すために修飾できます。型修飾子は、const値が初期化されると変更されないことを示します。修飾constされた値を変更しようとすると未定義の動作が発生するため、一部のコンパイラはそれをrodataまたは(組み込みシステムの場合は)読み取り専用メモリ(ROM)に格納します。同様に、constexprはの「より強力な」形式と考えることができconst、コンパイル時に値がわかっている必要があります(マクロ定数の型安全な代替品となります)。constexpr同様に、関数もコンパイル時に評価できる必要があります。型修飾子は、最適化コンパイラvolatileに対して、式やステートメントによって変更されていなくても値が変わる可能性があるため、またはメモリマップドI/Oのように複数の書き込みが必要になる可能性があるため、明らかに冗長な読み取りまたは書き込みを削除できないことを示します。
不完全な型とは、メンバーがまだ指定されていない構造体または共用体型、次元がまだ指定されていない配列型void、または型がvoid完成できない型のことです。このような型はインスタンス化できません(サイズが不明なため)。また、メンバーにアクセスすることもできません(メンバーも不明なため)。ただし、派生したポインタ型は使用できます(ただし、逆参照はできません)。
これらはポインターと共によく使用され、前方宣言または外部宣言として用いられます。例えば、コードでは次のような不完全型を宣言できます。
struct Integer * pt ;これは、(不完全な構造体型も含む)ptへのポインタとして宣言されますstruct Integer。すべてのポインタは (指す対象に関係なく) 同じサイズであるため、コードは をptポインタとして使用できますが、 のフィールドにはアクセスできませんstruct Integer。
不完全な型は、同じスコープ内で後から再宣言することで完成させることができます。例:
struct Integer { int num ; };不完全型は再帰構造を実装するために使用され、型宣言の本体は翻訳単位の後半に延期される場合があります。
typedef struct Bert Bert ; typedef struct Wilma Wilma ;struct Bert { Wilma * wilma ; };struct Wilma { Bert * bert ; };不完全型はデータ隠蔽にも使用されます。不完全型はヘッダーファイルで定義され、完全な定義は単一の本体ファイルで隠蔽されます。
変数宣言では、アスタリスク(*)は「ポインタ」を意味すると考えることができます。たとえば、は int 型の変数を宣言し、は整数へのポインタである変数を宣言します。言語定義に基づくと、は型よりも変数に密接に関連していると主張する人もおり、そのため、コードをまたは のようにフォーマットします。これは、アスタリスク(*)が宣言子の一部であり、型指定子ではないためです[ 5 ] 。このことによるもう 1 つの結果は、が整数へのポインタであり、が整数自体であるような 2 つの変数を宣言することです。int xint* pxpx*int *pxint * pxint* a,b;ab
ポインタ値は、メモリアドレスとデータ型という2つの情報を関連付けます。
非静的ポインタが宣言された時点では、その値は未定義です。値を代入せずに逆参照すると、未定義の動作が発生します。
演算子&は、その後に続くデータオブジェクトのアドレスを指定します。次の例では、ptrには のアドレスが割り当てられますa。
int a = 0 ; int * ptr = &a a ;変数名の前にアスタリスク(*)を付けると(宣言や数式でない場合)ポインタが逆参照され、ポインタが指す値にアクセスできるようになります。次の例では、整数変数 にb整数変数 の値 が設定されますa。その値は です10。
int a = 10 ; int * p ; p = &a a ; int b = * p ;a配列には、同じ型の連続する要素が格納されます。次のコードは、型 の という名前の 100 個の要素を持つ配列を宣言しますint。
int a [ 100 ];関数外(グローバル)で宣言する場合、サイズは定数でなければなりません。関数内で宣言する場合、配列サイズは定数以外の式でも構いません。
要素数は として取得できますが、値が別の関数に渡された場合、仮引数変数を介して要素数を取得することはできません。sizeof(a)/sizeof(int)
配列要素にアクセスするための主要な手段は、配列添え字演算子です。たとえば、 は配列 のa[i]インデックス にある要素にアクセスします。配列のインデックスは0 から始まるため、最後の配列インデックスは要素数から 1 を引いた値になります。標準では配列インデックスの境界チェックが提供されていないため、範囲外のインデックスを指定すると、未定義の動作になります。ia
配列とポインタは互換性があるため、各要素のアドレスはポインタ演算で表現できます。以下の表は、同じ配列に対して両方の方法を示しています。
この式a[i]は と意味的に同等であり*(a + i)、さらに は と同等であるため*(i + a)、この式は とも書くことができますi[a]が、この形式はめったに使用されません。
C99では、ブロックスコープ内の可変長配列(VLA)が標準化され、実行時情報(定数ではなく)に基づいてサイズが決定される配列が生成されましたが、ブロックの終わりまでサイズは固定されました。[ 2 ] C11以降、この機能はコンパイラによる実装が不要になりました。
int n = 20 ; // n は任意の値にすることができますint a [ n ]; a [ 3 ] = 10 ;この言語は、複数の次元の配列をサポートしています。これらの配列は行優先順で格納され、実質的には要素が配列である1次元配列です。ROWSとCOLUMNSは定数であるため、次の例では、長さROWSの2次元配列を宣言します。この配列の各要素は、 COLUMNS個の整数の配列です。
int array2d [ ROWS ][ COLUMNS ];以下は、整数要素にアクセスする例です。
array2d [ 4 ][ 3 ]左から右に読むと、これは5行目にアクセスし、その行の4番目の要素にアクセスします。この式array2d[4]は配列であり、添え字[3]を使用して4番目の整数にアクセスします。
高次元配列も同様の方法で宣言できます。
多次元配列は、配列へのポインタの配列(イリフベクトル、あるいは配列の配列とも呼ばれる)と混同してはならない。前者は常に長方形(すべての部分配列は同じサイズでなければならない)であり、メモリの連続した領域を占める。後者はポインタの1次元配列であり、各ポインタはメモリ内の異なる場所にある部分配列の最初の要素を指すことができ、部分配列は同じサイズである必要はない。
言語にはテキスト文字データ用の型が用意されていますが、言語自体にも標準ライブラリにも文字列型は定義されていません。しかし、ヌル終端文字列は一般的に使用されます。文字列値とは、末尾がゼロで示される連続した文字の並びです。標準ライブラリにはヌル終端文字列を扱うための多くの文字列処理関数が含まれていますが、文字列操作はカスタムコードで処理することも可能であり、実際によく行われます。
文字列リテラルは、二重引用符で囲まれたコードテキストです。例えば、`{{ }` のようになります。リテラルは、指定された値"Hello world!"の配列にコンパイルされ、文字列の終わりを示すためにヌル終端文字が付加されます。char
この言語は文字列リテラルの連結をサポートしており、隣接する文字列リテラルはコンパイル時に結合されたものとして扱われます。これにより、長い文字列を複数行に分割したり、プリプロセッサマクロからの文字列リテラルをコンパイル時に文字列に追加したりすることが可能になります。例えば、ソースコードは次のようになります。
printf ( __FILE__ ": %d: Hello " "world \n " );プリプロセッサによる展開後、以下のようになります__FILE__。
printf ( "helloworld.c" : %d: Hello " "world \n " );これは以下と同等です。
printf ( "helloworld.c: %d: Hello world \n " );文字リテラルは文字定数と呼ばれ、シングルクォートで囲まれ、例えば のようになり'A'、型は ですint。文字列リテラルと文字定数の違いを説明するために、"A"は と の 2 文字'A'である'\0'のに対し、は'A'1 文字 (ASCII では 65) を表すことを考えてみましょう。
文字定数は空にすることはできません(つまり、''構文が無効です)。複数文字定数(例'xy':)は有効ですが、あまり実用的ではありません。複数文字定数を使用すると、整数に複数の文字を格納できます(例:32ビット整数には4つのASCII文字、64ビット整数には8つのASCII文字を格納できます)。文字がに格納される順序はint指定されていないため(実装で定義されます)、複数文字定数の移植性は低くなります。
しかしながら、特定のプラットフォームとコンパイラの実装に限定された状況では、複数文字定数はシグネチャの指定に利用されます。一般的な使用例の 1 つはOSTypeで、Classic Mac OS コンパイラとその固有のビッグエンディアンの組み合わせにより、整数のバイトはリテラルで定義された文字とまったく同じ順序で出現します。一般的な「実装」による定義は実際には一貫しており、GCC、Clang、Visual C++では、 ASCII で と'1234'なります。[ 7 ] [ 8 ]0x31323334
文字列リテラルと同様に、文字定数も接頭辞によって変更できます。たとえば、L'A'型を持ちwchar_t、ワイド文字エンコーディングの「A」の文字値を表します。
制御文字は、文字列や文字リテラルに直接含めることはできません。代わりに、バックスラッシュ(\)で始まるエスケープシーケンスを使用してエンコードする必要があります。たとえば、のバックスラッシュは"This string contains \"double quotes\"."、内側の引用符が文字列の区切り文字(終点)としてデフォルトで読み取られるのではなく、文字列の実際の一部として意図されていることを示します。
脱出シーケンスには以下が含まれます。
他のバックスラッシュエスケープの使用は標準では定義されていませんが、コンパイラは言語拡張機能として追加のエスケープコードを提供することがよくあります。たとえば、 ASCII 16 進数値 1B のエスケープ文字\eのエスケープシーケンスは、他の文字セット( EBCDICなど)での表現が不足しているため標準には追加されませんでした。これはGCC、clang、tccで使用できます。
標準ライブラリ関数は、リテラル文字を表すためにprintf()を使用することに注意してください。%%%
型は 1 バイト幅であるためchar、単一のchar値で表現できる文字コードは通常最大 255 種類であり、世界中で使用されているすべての文字に対応するには到底足りません。国際文字をより適切にサポートするために、最初の標準 (C89) ではワイド文字(型でエンコードwchar_t) とワイド文字列が導入され、これらは次のように記述されます。L"Hello world!"
ワイド文字は、一般的には 2 バイト ( UTF-16などの 2 バイト エンコーディングを使用) または 4 バイト (通常はUTF-32 ) ですが、標準 C では の幅は指定されておらずwchar_t、実装者に選択を委ねています。Microsoft Windows は一般的に UTF-16 を使用するため、上記の文字列は Microsoft コンパイラでは 26 バイト長になります。Unixの世界では UTF-32 が好まれるため、GCC などのコンパイラでは 52 バイトの文字列が生成されます。2 バイト幅のにはwchar_tと同じ制限があり、特定の文字 ( BMPcharの外側の文字) は単一の で表現できず、サロゲート ペアを使用して表現する必要があります。wchar_t
当初の規格では、ワイド文字文字列を扱うための最小限の機能しか規定されていませんでしたが、1995年に規格が改訂され、char文字列に対するサポートと同等の、より広範なサポートが含まれるようになりました。関連する関数は、ほとんどの場合char、同等の関数名に「w」を追加したり、「str」を「wcs」に置き換えたりして命名されています。これらの関数は<wchar.h>、<wctype.h>ワイド文字の分類およびマッピング関数を含む で規定されています。
現在一般的に推奨されている国際文字のサポート方法[注3 ]はUTF-8によるもので、配列に格納されchar、UTF-8エディタを使用している場合はソースコードに直接記述できます。これはUTF-8が直接ASCII拡張であるためです。
の一般的な代替手段として、可変長エンコーディングをwchar_t使用する方法があります。これにより、論理文字が文字列の複数の位置にまたがる場合があります。可変長文字列は、コンパイラを混乱させるリスクを冒して、そのままリテラルにエンコードすることも、数値バックスラッシュエスケープ(UTF-8 の" é " など)を使用することもできます。UTF -8エンコーディングは、標準ライブラリの文字列関数との互換性のために特別に設計されました(プラン 9の下で)。このエンコーディングのサポート機能には、埋め込みヌル文字の欠如、部分シーケンスの有効な解釈の欠如、および簡単な再同期が含まれます。これらの機能を持たないエンコーディングは、標準ライブラリの関数と互換性がないことが判明する可能性が高く、このような場合は、エンコーディング対応の文字列関数がよく使用されます。"\xc3\xa9"
構造体(struct)は、他の言語のレコードと同様に、型が異なる名前付きメンバーのシーケンスで構成されるコンテナです。最初のフィールドは構造体のアドレスから始まり、メンバーはメモリ内の連続した場所に格納されますが、コンパイラは効率化のため、またはターゲットアーキテクチャによる適切なアライメントに必要なパディングとして、メンバー間またはメンバー後にパディングを挿入することができます。構造体のサイズにはパディングが含まれます。
構造体は、structキーワードに続いてオプションの識別子名(構造体の形式を識別するために使用される)を指定して宣言されます。本体はフィールド宣言で構成され、各フィールド宣言は型名、フィールド名からなり、セミコロンで終了します。
以下は、MyStruct3 つのメンバーを含む構造体を宣言しています。また、インスタンスも宣言していますtee。
struct MyStruct { int x ; float y ; char * z ; } tee ;構造体メンバーは、不完全な型または関数型を持つことはできません。したがって、メンバーは宣言中の構造体のインスタンスにすることはできません(その時点では構造体は不完全であるため)。ただし、フィールドは宣言中の型へのポインタにすることができます。
一度宣言されると、構造体型の変数を宣言できます。以下は、MyStructという名前の構造体の新しいインスタンスを宣言する例ですr。
struct MyStruct r ;キーワードを使用して構造体変数を宣言することを好む人もいますがstruct、typedef構造体型をメインの型名前空間にエイリアスするために を使用する人もいます。次の例では、型を として宣言し、Integerのように使用できますInteger n。
typedef struct { int i ; } Integer ;メンバーにはドット表記を使用してアクセスします。たとえば、tee上記の宣言が与えられた場合、メンバーにyはとしてアクセスできますtee.y。
構造体には、ポインタを介してアクセスするのが一般的です。 はという名前のへのポインタを定義しているとします。のメンバーには、 を逆参照し、その結果を左オペランドとして使用して としてアクセスできます。[ b ]この操作は一般的であるため、言語では、ポインタからメンバーに直接アクセスするための省略構文(たとえば、) を提供しています。structMyStruct*ptee=&teeteepteeyteeptee(*ptee).y->ptee->y
メンバーに値を代入することは、変数に値を代入することと似ています。唯一の違いは、代入式の左辺の値( lvalue)が、上記の構文に従ったメンバー名である点です。
構造体は、同じ型の別の構造体に全体を割り当てることも、コピーによって関数の引数または戻り値として渡すこともできます。たとえば、 は構造体teeのxtee.x = 74という名前のメンバーに値 74 を割り当て、 はpteeに対して同じことを行います。ptee->x = 74
構造体に対してサポートされる操作は、初期化、コピー、アドレスの取得、およびフィールドへのアクセスです。なお、この言語では、各フィールドを比較するカスタムコードを使用する場合を除き、2つの構造体の値を比較することはできません。
この言語では、ビットフィールドと呼ばれる特殊なタイプのメンバが提供されます。ビットフィールドは、指定されたビット数を持つ整数です。ビットフィールドは、型 ( signed/ unsigned) int、bool、または_BitInt(N)のメンバとして宣言されます。[注 4 ]メンバ名の後に、コロンとビット数からなる接尾辞が付きます。単一のビットフィールドのビット総数は、その基本型のビット総数を超えてはなりません。通常の C 構文規則とは異なり、明示的に指定しない限り、ビットフィールドが符号付きか符号なしかは実装依存です。したがって、ベストプラクティスはまたはを指定することです。signedunsigned
名前のないフィールドはパディングを示し、コロンの後にビットが続くだけです。名前のないフィールドの幅をゼロに指定すると、新しいワードにアライメントが強制されます。 [ 9 ]共用体のすべてのメンバーは同じメモリを占有するため、幅がゼロの名前のないビットフィールドは共用体では何もしませんが、幅がゼロでない名前のないビットフィールドは共用体に収まる必要があるため、共用体のサイズを変更する可能性があります。
ビットフィールドは、アドレス指定演算子(&)やsizeof演算子がサポートされていない点で、通常のフィールドに比べて制限があります。
以下では、という名前の構造体型FlagStatusと、という名前のそのインスタンスを宣言しますg。最初のフィールドである flag は、1 または 0 のみをとることができる 1 ビットのフラグです。2 番目のフィールドである num は、符号付き 4 ビットのフィールドで、範囲は -7...7 または -8...7 です。最後のフィールドは、構造体を 8 ビットに丸めるために 3 ビットのパディングを追加します。
struct FlagStatus { unsigned int flag : 1 ; signed int num : 4 ; signed int : 3 ; } g ;C言語自体は、 C++やJavaとは異なり、名前空間をネイティブにサポートしていません。そのため、C言語のシンボル名は名前の衝突を起こしやすいという問題があります。しかし、匿名構造体を使用することで、名前空間をエミュレートすることは可能です。
Math.h :
#pragma onceconst struct { double PI ; double ( * sin )( double ); } Math ;Math.c :
#include <math.h>static double _sin ( double arg ) { return sin ( arg ); }const struct { double PI ; double ( * sin )( double ); } Math = { M_PI , _sin };Main.c :
#include <stdio.h> #include "Math.h"int main ( ) { printf ( "sin(0) = %d \ n " , Math.sin ( 0 ) ); printf ( "pi is %f \n " , Math.PI ) ; }共用体は、大まかに言えば構造体に似ていますが、異なる型の値を格納できるように、メモリ上でフィールドが重なり合っている点が異なります(ただし、同時に格納することはできません)。共用体は、他の言語のバリアントレコードに相当します。各フィールドは、メモリ内の同じ場所を参照します。共用体のサイズは、最大の構成要素型のサイズにパディングを加えたものになります。
共用体はunionキーワードで宣言されます。以下は、という名前の共用体MyUnionと、という名前のそのインスタンスを宣言していますn。
union MyUnion { int x ; float y ; char * z ; } n ;変数を宣言する際に初期化するには、等号を付けてから、そのデータ型に対応した構文を記述します。以下はint型の変数を初期化する例です。
int x = 12 ;言語の文法上、スカラー初期化子は任意の数の波括弧で囲むことができます。ほとんどのコンパイラは、そのような波括弧が複数ある場合に警告を発します。以下は、おそらく一般的ではないものの、有効な例です。
int y = { 23 }; int z = { { 34 } };構造体、共用体、配列は、宣言後に初期化リストを使用して初期化できます。
一致しない要素は0に設定されるため、空のリストはすべての要素を0に設定します。たとえば、次の例では、配列aのすべての要素とsのすべてのフィールドを0に設定します。
int a [ 10 ] = {}; struct MyStruct s = {};配列が明示的なサイズなしで宣言された場合、その配列は不完全な型となります。初期化子の数によって配列のサイズが決まり、型が完成します。例:
int x [] = { 0 , 1 , 2 };デフォルトでは、初期化リストの項目は、定義された順序で要素に対応します。値が多すぎるとエラーになります。次のステートメントは、構造体MyStructのインスタンスをpiという名前で初期化します。
struct MyStruct { int x ; float y ; char * z ; };struct MyStruct pi = { 3 , 3.1415 , "Pi" };指定初期化子を使用すると、メンバーを名前で、任意の順序で、かつ先行する値を明示的に指定することなく初期化できます。次の初期化は、前の初期化と機能的に同等です。
struct MyStruct pi = { . z = "Pi" , . x = 3 , . y = 3.1415 };初期化子で指定子を使用すると、初期化の「カーソル」が移動します。以下の例では、MAXが 10 より大きい場合、 の途中にゼロ値の要素がいくつか存在しますa。 が 10 より小さい場合、最初の 5 つの初期化子によって提供される値の一部が、次の 5 つの初期化子によって上書きされます。 がMAX5 より小さい場合は、コンパイル エラーが発生します。
int a [ MAX ] = { 1 , 3 , 5 , 7 , 9 , [ MAX - 5 ] = 8 , 6 , 4 , 2 , 0 };C89では、共用体は最初のメンバーに単一の値を適用して初期化されていました。つまり、上記で定義した共用体MyUnionでは、 xメンバーのみが初期化されていました。
union MyUnion の値= { 3 };指定初期化子を使用する場合、初期化するメンバーは最初のメンバーである必要はありません。
union MyUnion の値= { . y = 3.1415 };複合指定子を使用すると、装飾のない初期化子リストが誤解される可能性がある場合に、明示的な初期化を提供できます。以下の例では、wは構造体の配列として宣言され、各構造体はメンバーa(3つの配列int)とメンバーb(int)で構成されます。初期化子はのサイズをw2に設定し、各の最初の要素の値を設定しますa。
struct { int a [ 3 ], b ; } w [] = { [ 0 ]. a = { 1 }, [ 1 ]. a [ 0 ] = 2 };これは以下と同等です。
struct { int a [ 3 ], b ; } w [] = { { { 1 , 0 , 0 }, 0 }, { { 2 , 0 , 0 }, 0 } };初期化手法を借用して、複合構造体や配列リテラルを生成することが可能です。
// 配列リテラルから作成されたポインタ。int * ptr = ( int []){ 10 , 20 , 30 , 40 };// 配列へのポインタ。float ( * foo )[ 3 ] = & ( float []){ 0.5f , 1.f , -0.5f };struct MyStruct pi = ( struct MyStruct ){ 3 , 3.1415 , "Pi" };複合リテラルは、宣言をより読みやすくするために指定初期化子と組み合わされることが多い。[ 2 ]
pi = ( struct MyStruct ){ . z = "Pi" , . x = 3 , . y = 3.1415 };関数へのポインタは次のように宣言できます。
型名(*関数名)(パラメータリスト)
以下のプログラムコードは、加算と減算を選択するために関数ポインタを使用する方法を示しています。12行目では、関数と関数のoperation両方と同じインターフェースをサポートする関数ポインタ変数を定義します。条件式(argc)に基づいて、にはまたはのアドレスが割り当てられます。14行目では、を指す関数が呼び出されます。addsubtractoperationaddsubtractoperation
これは、 CおよびC++プログラミング言語の演算子の一覧です。
記載されている演算子はすべてC++のものであり、特に明記がない限りC言語でも使用できます。一部の表には、演算子がC言語でも使用できるかどうかを示す「C言語でも使用可能」列があります。なお、C言語は演算子オーバーロードをサポートしていません。
オーバーロードされていない場合、演算子&&、||、および,(コンマ演算子) については、最初のオペランドの評価後にシーケンスポイントがあります。
C言語とC++で使用できる演算子のほとんどは、 C#、D言語、Java、Perl、PHPなどの他のC系言語でも、同じ優先順位、結合規則、意味論で使用できます。
一連の記号で指定される多くの演算子は、それぞれの記号の名前を組み合わせた名前で呼ばれるのが一般的です。例えば、 `+` と `+`+=は-=、より冗長な「加算による代入」や「減算による代入」ではなく、「プラス等号」や「マイナス等号」と呼ばれることがよくあります。
複合文(または文ブロック)とは、複数の文を挟んだ一対の波括弧のことです。例えば、次のような文が挙げられます。
{ {声明} }複合文は文の一種であるため、文を使用する際には、単一の文(括弧なし)または複合文(括弧あり)のいずれかになります。関数本体および単一の文ではない制御構造の分岐には、複合文が必要です。
ブロック内で宣言された変数は、そのブロック内(および宣言より下の内側のブロック内)のコードから参照できます。ブロックが閉じられた後に、ブロック内で宣言された変数に使用されているメモリにアクセスすると(つまり、ポインタを介してアクセスすると)、未定義の動作が発生します。
if (式) {文} else {文}式がゼロでない場合、制御は最初のステートメントに移ります。そうでない場合、制御は2番目のステートメントに移ります。else部分が存在しない場合、式がゼロと評価されると、最初のステートメントは単純にスキップされます。 はelse常に、直前の一致していない に一致しますif。必要に応じて、または明確化のために、中括弧を使用してこれを上書きできます。
特に、2番目の文は別のif文でも構いません。例えば:
if ( i == 1 ) { printf ( "it's 1" ); } else if ( i == 2 ) { printf ( "it's 2" ); } else { printf ( "it's something else" ); }整数型式switchに一致する値を持つケースラベルに制御が移ります。そうでない場合は、ラベル(存在する場合)に制御が移ります。制御は、ラベルに続くステートメント、またはステートメントの終了まで続きます。構文は次のようになります。defaultcasebreakswitch
switch ( expression ) { case label-name : 声明 ケースラベル名: 声明 。 。 。 デフォルト: 声明 }caseステートメント内の各値は一意でなければなりません。defaultラベルは最大で1つまでです。
ステートメントが見つからない場合、実行はラベルからcaseラベルへと継続されますbreak。これはフォールスルーと呼ばれ、状況によっては有用ですが、多くの場合望ましくありません。
case内部制御構造のサブブロックにラベルを配置することは可能ですが、これは一般的ではありません。例としては、Duff のデバイスやSimon TathamによるPuttyのコルーチンの実装などがあります。[ 10 ]
switch以下は、の上に を配置した例ですint。
#include <stdio.h>// ... int num = 2 ;switch ( num ) { case 1 : printf ( "数値は1です\n " ); break; case 2 : printf ( "数値は2です\n " ); break ; case 3 : printf ( "数値は3です\n " ); break ; default : printf ( "数値は1、2、または3ではありません\n " ); }C2Y以降では、(省略記号)caseを使用して 2 つの整数定数の間に「範囲」を指定できます...。[ 11 ]値と省略記号の間にはスペースが必要です。この範囲は両端を含みます。例:
#include <stdio.h>// ... int num = 2 ;// 新しいスタイルのswitch ( num ) { case 1 ... 3 : printf ( "数値は 1、2、または 3 です\n " ); break default : printf ( "数値は 1、2、または 3 ではありません\n " ); }// 旧式、case フォールスルーを使用switch ( num ) { case 1 : case 2 : case 3 : printf ( "数値は 1、2、または 3 です\n " ); break default : printf ( "数値は 1、2、または 3 ではありません\n " ); }反復文には3つの形式があります。
while (式) {ステートメント}do {ステートメント} while (式)for ( init ; test ; next ) { statement }whileおよびステートメントの場合、式の値がゼロでないdo限り、サブステートメントは繰り返し実行されます。 の場合、副作用を含むテストは各反復の前に実行されます。の場合、テストは各反復の後に実行されます。したがって、 ステートメントは常に少なくとも 1 回はサブステートメントを実行しますが、 はサブステートメントをまったく実行しない可能性があります。whiledodowhile
の論理は、次のforように説明できる。while
for ( e1 ; e2 ; e3 ) { s ; }は以下と同等です。
e1 ; while ( e2 ) { s ; cont : e3 ; }ただし、ステートメントの動作は例外ですcontinue;(ループ内で の代わりforに にジャンプします)。 が空白の場合は、 に置き換える必要があります。e3e2e21
ループ内の3つの式は、いずれもfor省略可能です。2つ目の式が省略されると、whileテスト結果は常にゼロ以外となり、無限ループを表します。
C99以降、最初の式は、サブステートメントに限定されたスコープを持つ宣言の形式をとることができます。例:
for ( int i = 0 ; i < limit ; ++ i ) { // ... }C言語には、JavaやC++にあるようなforeachループは存在しません。しかし、マクロを使ってそれをエミュレートすることは可能です。
ジャンプ文は4 つあります(無条件に制御を移します): goto、continue、break、return。
このgotoステートメントは、プログラムの制御をラベル付きステートメントに渡します。構文は次のとおりです。
ラベル名へ移動
continue単に という単語で構成されるステートメントは、continue最も内側の、囲んでいる反復ステートメントのループ継続ポイントに制御を移します。これは反復ステートメントで囲む必要があります。例:
while ( true ) { // ...続行; }do { // ...続ける; } while ( true );for (;;) { // ...続行; }break単に という単語で構成されるステートメントは、 、、、またはステートメントbreakを終了します。制御は、囲んでいる制御ステートメントの次のステートメントに移ります。forwhiledoswitch
このreturnステートメントは、関数呼び出し元に制御を移します。returnこのステートメントの後に式が続く場合、その値は呼び出し元に返されます。関数の終了に到達することは、return式のないステートメントに到達することと同じです。この場合、関数が値を返すように宣言されており、呼び出し元が返された値を使用しようとすると、動作は未定義となります。
ラベルは、コード内で制御を移すことができる箇所を示します。ラベルは、識別子の後にコロンが続く形式です。例:
if ( i == 1 ) { goto END ; } // その他のコードEND :標準規格ではラベルのアドレスを取得する方法は定義されていませんが、 GCC は&&ラベルのアドレスを返す単項演算子で言語を拡張しています。このアドレスはvoid*変数に格納でき、後で と組み合わせて使用できます。この機能はジャンプ テーブルをgoto実装するために使用できます。
例えば、以下はhi繰り返し出力されます。
void * ptr = && J1 ; J1 : printf ( "hi" ); goto * ptr ;C2Y以降、C言語にはJavaと同様のラベル付きループが導入されました。これにより、ループにラベルを付けたりfor、ラベルを使用してループbreakとループを介して制御を移したりすることが可能になります(多段階のブレーク)。[ 12 ]continue
// break を使用: outer : for ( int i = 0 ; i < n ; ++ i ) { switch ( i ) { case 1 : break ; // 1 にジャンプcase 2 : break outer ; // 2 にジャンプdefault : continue ; } // 1 } // 2// continue: outerを使用する場合: for ( int i = 0 ; i < m ; ++ i ) { for ( int j = 0 ; j < n ; ++ j ) { continue ; // 1 にジャンプcontinue outer ; // 2 にジャンプ// 1 } // 2 }値を返す関数の定義は、戻り値の型名、コードベース内で一意な関数名、括弧で囲まれたパラメータのリスト、および文で終わる文ブロックで構成されます。ブロックには、ブロックの終了前に関数を終了する文returnを含めることができます。構文は次のようになります。return
型名関数名(パラメータリスト) {ステートメントリスト戻り値; }値を返さない関数はvoid、型名の代わりに次のように宣言されます。
void function-name ( parameter-list ) { statement-list }パラメータリストは、カンマで区切られた仮パラメータ宣言のリストであり、各項目は型名と変数名で構成されます。
型名変数名{ ,型名変数名}戻り値の型は配列または関数であってはなりません。例:
int f ()[ 3 ]; // エラー: 配列を返す関数int ( * g ())[ 3 ]; // OK: 配列へのポインタを返す関数void h ()(); // エラー: 関数が関数を返すvoid ( * k ())(); // OK: 関数が関数ポインタを返す関数が引数を受け取らない場合、引数リストはキーワードvoidまたは空白にすることができますが、それぞれ意味が異なります。引数リストvoidにキーワードを指定して関数を宣言した場合、引数付きで関数を呼び出すのは無効な構文です。引数リストが空白の関数を宣言した場合、引数付きで関数を呼び出すのは無効な構文ではありませんが、未定義の動作が発生する可能性があります。したがって、キーワードを使用するのがベストプラクティスです。void
...関数は、引数リストの末尾にを含めることで、可変個の引数を受け入れることができます。この宣言でよく使われる関数はprintf、プロトタイプが次の標準ライブラリ関数です。
int printf ( const char * , ...);可変長引数の処理は、標準ライブラリ関数を使用して実現できます<stdarg.h>。
ライブラリの関数は、宣言と定義の両方がなされていれば、コードからアクセスできます。多くの場合、ライブラリ関数の宣言は、利用コードが#includeディレクティブを介して使用するヘッダーファイルによって提供されます。あるいは、利用コードが独自のファイルで関数を宣言することもできます。関数の定義は、リンク時に利用コードに関連付けられます。標準ライブラリは通常デフォルトでリンクされますが、他のライブラリはリンク時の設定が必要です。
別のファイルで定義されたユーザー定義関数にアクセスする方法は、ライブラリ関数を使用する方法と似ています。関数を使用するコードは、ヘッダーファイルをインクルードするか、または関数ファイル内で直接関数を宣言します。オブジェクトファイルがリンクされる際に、別のファイル内の定義へのリンク処理が行われます。
同じファイル内で定義されている関数を呼び出すのは比較的簡単です。関数の定義または宣言は、呼び出し文よりも上に記述する必要があります。
引数は値渡しで関数に渡されます。つまり、呼び出された関数は引数のコピーを受け取り、引数変数自体を変更することはできません。関数が変数の値を変更するには、呼び出し元は変数のアドレス(ポインタ)を渡します。これは、他の言語で参照渡しとして提供されるものをシミュレートするものです。呼び出された関数は、渡されたアドレスを逆参照することで変数を変更できます。
以下のコードでは、呼び出し時に指定することでのアドレスxが渡されます&x。呼び出された関数はとしてアドレスを受け取り、としてyアクセスします。x*y
void incInt ( int * y ) { ( * y ) ++ ; }int main ( void ) { int x = 7 ; incInt ( & x ); return 0 ; }以下のコードは、ポインタのより高度な使用例、つまりポインタへのポインタの受け渡しを示しています。9a行目でint型のポインタが定義され、10行目でそのアドレスが関数に渡されます。関数はint型のポインタへのポインタを受け取りますa_p。そして、a(として*a_p)を代入します。呼び出し後、11行目でアドレスに割り当てられ、代入されたメモリがa解放されます。
#include <stdio.h> #include <stdlib.h>void allocate_array ( int ** const p , const int count ) { * p = malloc ( sizeof ( int ) * count ); }int main ( void ) { int * a ; allocate_array ( &a a , 42 ); free ( a ); return 0 ; }配列型の関数パラメータは、一見すると値渡しルールの例外のように見えるかもしれません。次のプログラムは、1ではなく123を出力します。
#include <stdio.h>void setArray ( int a [], int index ) { array [ index ] = 123 ; }int main ( void ) { int a [ 1 ] = {1} ; setArray ( a , 0 ) ; printf ( "a[0]=%d \n " , a [ 0 ]); return 0 ; }しかし、この動作には別の理由があります。配列パラメータはポインタとして扱われます。以下のプロトタイプは、上記の関数プロトタイプと同等です。
void setArray ( int * a , int index );同時に、式における配列の使用に関する規則により、 の値はa最初の要素へのポインタとして扱われます。したがって、これは依然として値渡しですが、値渡しされるのは配列の内容ではなく、配列の最初の要素のアドレスであるという注意点があります。
C99以降、プログラマはキーワードを使用することで、関数が特定のサイズの配列を受け取るように指定できますstatic。最初のパラメータには、長さが4以上の配列の最初の要素へのポインタを指定する必要があります。また、配列が変換されるポインタ型に対して修飾子( 、および)を使用することも可能です。voidsetArray(intarray[static4],intindex)constvolatilerestrict
C23 で追加され、C++11 に由来する C は、属性指定子シーケンスをサポートしています。[ 13 ]属性は、関数や変数など、属性をサポートする任意のシンボルに適用でき、属性でマークされたシンボルは、コンパイラによって必須として特別に扱われます。これらは、コンパイラに追加情報を提供するJava の注釈に似ていると考えることができますが、C の属性はリフレクションを使用してアクセスすることを意図したメタデータではないという点で異なります。さらに、Java では標準の注釈に加えてカスタム注釈を定義できるのとは異なり、C ではカスタム属性を作成することはできません。ただし、C には非標準の実装/ベンダー固有の属性があります。これらには通常、名前空間が関連付けられています。たとえば、GCC と Clang にはgnu::名前空間の下に属性があり、そのような属性はすべて の形式ですが、C 言語では名前空間がサポートされていません。[[gnu::*]]
関数に属性を使用する構文は次のとおりです。
[[ nodiscard ]] bool satisfiesProperty ( const struct MyStruct * s );この規格では、以下の属性を定義しています。
C言語の動的メモリ割り当てとは、C標準ライブラリの関数群(主に、、、、および)を介して、Cプログラミング言語で動的メモリ割り当てのための手動メモリ管理を実行することを指します。[ 14 ] [ 15 ] [ 16 ]mallocrealloccallocaligned_allocfree
C ++プログラミング言語にはこれらの関数が含まれていますが、 new演算子とdelete演算子は同様の機能を提供し、その言語の開発者によって推奨されています。[ 17 ]ただし、ガベージ コレクション コードやパフォーマンスに敏感なコードなど、 / の使用が適用できない状況がいくつかあり、高レベルの演算子の代わりに、との組み合わせが必要になる場合があります。newdeletemallocnewnew
mallocで使用される実際のメモリ割り当てメカニズムには、さまざまな実装が存在します。それらのパフォーマンスは、実行時間と必要なメモリの両面で異なります。