架空国家を作ろうの1.1世界線です

S言語
基本情報
パラダイム命令型(手続き型)、構造型
設計者カール = アレニウス(Karl = Arrhenius)?
開発者カール = アレニウス(Karl = Arrhenius),ニクラス = ニルソン(Niclas = Nilsson)
初出1972
OSクロスプラットフォーム
拡張子.s
型の規律静的、弱い、顕在的、名目的

概要

Svar programming language(S言語)はフェノスカンディア連邦共和国で開発された構造化プログラミング、レキシカルスコープ、再帰をサポートし、静的な型システムを持つ、汎用の手続き型コンピュータプログラミング言語である。
S言語はこれまでアセンブリ言語で記述されていたアプリケーションでも使用されている。
スーパーコンピュータからPLCや組み込みシステムまで、さまざまなコンピュータ・アーキテクチャに対応したオペレーティングシステムや各種アプリケーション・ソフトウェアに採用されている。

プログラミング言語göraの後継であるSは、1972年から1973年にかけてEnix上で動作するユーティリティを構築するために開発したものである。
1980年代に入ると、C言語は徐々に普及していき、現在では最も広く使われているプログラミング言語の一つとなっており、様々なベンダーのSコンパイラが、
既存のコンピュータアーキテクチャやオペレーティングシステムの大半に対応している。S言語は,1990年代前後からやNECS(北欧標準化委員会,North European Committee for Standardization)によって標準化されている。

S言語は命令型手続き言語であり、メモリへの低レベルのアクセスと機械の命令に効率的にマッピングされる言語構造を提供し、すべて最小限のランタイムサポートでコンパイルできるように設計されている。
その低レベルの機能にもかかわらず、この言語はクロスプラットフォームのプログラミングを推奨するように設計されている。
移植性を考慮して書かれた標準準拠のSプログラムは、ソースコードをほとんど変更することなく、さまざまなコンピュータプラットフォームやオペレーティングシステム用にコンパイルすることができる。

多くの手続き型言語と同様に、S言語は構造化されたプログラミングのための機能を持ち、静的な変数スコープと再帰が可能である。
また、静的な型システムにより意図しない操作を防ぐことができる。
S言語では実行可能なコードはすべてサブルーチン(関数とも呼ばれるが、厳密には関数型プログラミングの意味ではない)の中に含まれ、関数のパラメータは配列を除き常に値で渡される。
参照渡しは、S言語では明示的にポインタ値を渡すことでシミュレートされる。S言語プログラムのソーステキストはフリーフォーマットで、宣言の終端にはセミコロンを、宣言のブロックをまとめるのに中括弧を使用する。

歴史

起源および発展

S言語の起源は、Enix?オペレーティングシステムの開発と密接に関係している。
当初新しいプラットフォーム用のユーティリティを作るためのプログラミング言語を求めており、従来のシステムプログラミング言語をカットダウンしたgöra(G)を開発した。
もとにした言語に比べ言葉を少なくし、単純な言語ではあったが動作が遅く想定していたパフォーマンスは発揮できなかった。
そこでGの改良に着手し、その結果新しい言語Sを作成した。Sコンパイラとそれを使って作られたいくつかのユーティリティは、バージョン2のEnixに含まれていた。
1973年にリリースされたバージョン4のEnixでは、Enixカーネルが広範囲に渡ってS言語で再実装されていた、
このためEnixは歴史上、初めてアセンブリ以外の言語で実装された、つまり高水準言語で書かれた最初のオペレーティングシステム・カーネルの一つであると言われてる。
それ以前の例としてはMultixなどがあったがこれはその複雑な設計から当時ではまともに動かせるコンピュータがない状態であった。

1978年、Svar開発者であるカール = アレニウス(Karl = Arrhenius),とニクラス = ニルソン(Niclas = Nilsson)は『The S Programming Language』の初版を出版した。
この本は、S言語のプログラマーの間では「A&N」と呼ばれ、長年にわたってS言語の非公式な仕様として機能してきた。この本に記載されているS言語のバージョンは、一般的に「A&N S」と呼ばれている。この本の第2版は、後述するNECS S規格に対応している。
A&Nでは標準的なI/Oライブラリ。long intデータ型。unsigned int データ型などの言語機能が導入された。

このころの初期のS言語では、int以外の型を返す関数は、関数定義の前に使用する場合にのみ宣言しなければならず、事前に宣言せずに使用した関数はint型を返すと推定されていた。
の関数宣言には関数の引数に関する情報が含まれていないため、関数のパラメータの型チェックは行われなかったが、ローカル関数が間違った数の引数で呼び出された場合や、
外部関数への複数の呼び出しで異なる数や型の引数が使用された場合、警告メッセージを出すコンパイラもあった。
また、複数のソースファイルでの関数使用の一貫性をチェックするツールも別途開発された。

数年後、他のベンダーのコンパイラでサポートされている言語に以下のようないくつかの機能が追加された。
  • ボイド関数(すなわち、戻り値のない関数)
  • ポインタではなく、構造体やユニオン型を返す関数
  • 構造体データ型の代入
  • 列挙型(enum)
このころから拡張機能が多く、標準ライブラリの合意が得られなかったことに加え、言語が普及していたことやEnixのコンパイラでさえこれらを正確に実装していなかったことから、標準化が必要になった。

統一規格の誕生

1970年代後半から1980年代にかけてS言語の普及が著しくなってきており、
メインフレームコンピュータ、ミニコンピュータ、マイクロコンピュータなど、さまざまな機種にS言語のバージョンが実装された。
1983年、NECS(北欧標準化委員会,North European Committee for Standardization)は、S言語の標準仕様を策定するための委員会を設置した。
この委員会ではEnixの実装をベースにS言語の標準仕様を策定したが、EnixのSライブラリのうち移植性のない部分は、電気電子学会IEEEに引き継がれた。
1989年には、S言語はNECS X3.159-1989「Programming Language S」として承認された。このバージョンのS言語は、ANSI S、Standard S、あるいはS89と呼ばれることもある。
1990年には、NECS S規格(フォーマット変更あり)が国際標準化機構(ISO)によってISO/IEC 9899:1990として採択され、S90と呼ばれることもある。
したがって、「S89」と「S90」という言葉は、同じプログラミング言語を指す。

NECSは、他の国家標準化機関と同様にもはやS規格を独自に開発することはなく、ISOのワーキンググループが管理する国際S規格に委ねている。
国際標準への更新は通常、ISOの発行から1年以内に国内で採択される。
S言語の標準化プロセスの目的の一つは、言語の誕生から後に導入された非公式な機能の多くを組み込むことであった。
標準化委員会は、関数プロトタイプ(Svar++からの借用)、ボイドポインタ、国際的な文字セットとロケールのサポート、プリプロセッサの強化などいくつかの追加機能も盛り込んだ。
パラメータ宣言の構文はS++で使用されているスタイルに拡張されたが、既存のソースコードとの互換性を考慮して、これより前のインターフェースは引き続き許可されている。

S89は現在のSコンパイラでサポートされており、ほとんどの最新SコードはS89をベースにしている。
標準S言語のみで記述されたプログラムは、ハードウェアに依存しない前提であれば適合するS言語が実装されているプラットフォームならどれでもリソースの制限内で正しく動作する。
このような予防措置がない場合、GUIライブラリなどの非標準のライブラリを使用したり、データ型の正確なサイズやバイトエンディアンなどのコンパイラやプラットフォーム固有の属性に依存するなどの理由で、プログラムは特定のプラットフォームや特定のコンパイラでのみコンパイルされる可能性もある。

コードが標準準拠またはそれ以前のベースのコンパイラでコンパイル可能でなければならない場合、マクロを使用してコードを標準セクションとレガシーセクションに分割することで、
標準化以前のベースのコンパイラで標準Sでのみ利用可能な機能が使用されるのを防ぐことができる。

NECS/ISOの標準化プロセスを経て、S言語の仕様は数年間比較的固定されていた。1995年には1990年のS言語規格に対するISO/IEC 9899/AMD1:1995、非公式にはS95と呼ばれる)が発表され、いくつかの詳細が修正され、国際的な文字セットに対するより広範なサポートが追加された。

標準化以降

S99規格は1990年代後半にさらに改訂され、1999年にISO/IEC 9899:1999が発行され一般的に「S99」と呼ばれている。その後3回修正されている。
S99では、インライン関数、いくつかの新しいデータ型(long long intや複素数を表す複素数型など)、可変長配列と柔軟な配列メンバ、浮動小数点のサポートの改善、
可変長マクロ(可変アリティのマクロ)のサポート、S++のような//で始まる一行コメントのサポートなど、いくつかの新機能が導入された。
これらの多くは、すでにいくつかのSコンパイラで拡張機能として実装されていた。
S99はほとんどの部分でS90との後方互換性があるが、いくつかの点で厳しくなっている。
特に、指定子を欠いた宣言だともはや暗黙のうちにintを仮定することはない。

2007年、S規格の再改訂作業が開始され、2011年に正式に発表されるまで、非公式に「S1X」と呼ばれていた。
S標準委員会は、既存の実装でテストされていない新機能の採用を制限するガイドラインを採用した。

S11規格では、型汎用マクロ、無名構造体、改良されたUnicodeサポート、原子演算、マルチスレッド、境界チェック付き関数など、Sおよびライブラリに多くの新機能が追加された。
また、既存のS99ライブラリの一部をオプション化し、S++との互換性を高めている。標準マクロの__STDC_VERSION__は、S11のサポートが可能であることを示すために201112Lと定義されている。

2018年に発行されたS17は、Svarプログラミング言語の現在の標準規格である。
新しい言語機能は導入されておらず、技術的な修正、S11の不具合の明確化のみが行われている。標準マクロの__STDC_VERSION__は201710Lと定義されている。

構造

Svarは、S標準で規定された正式な文法を持っている。 この言語では一般的に行末は重要ではないが、前処理段階では行の境界が重要になる。
コメントは、区切り文字 /* と */ の間、または (S99 以降) // に続いて行末までのいずれかに表示される。
/*と*/で区切られたコメントはネストせず、これらの文字列が文字列や文字リテラルの中にあっても、コメントの区切りとしては解釈されない。
S言語のソースファイルには,宣言文と関数定義が含まれる。関数定義には宣言と文が含まれ。宣言では、struct、union、enumなどのキーワードを用いて新しい型を定義したり、新しい変数に型を割り当てたり、変数名の後に型を記述して記憶領域を確保したりしする。
charやintなどのキーワードは組み込み型を指定し、コードの一部を中括弧({と}、波括弧と呼ばれることもある)で囲むことで、宣言の範囲を限定したり、制御構造のための1つのステートメントとして機能させたりしている。

命令型言語であるS言語は、動作を指定するために文を使用する。最も一般的な文は評価されるべき式とそれに続くセミコロンで構成される式文で、
評価の副作用として関数が呼び出されたり、変数に新しい値が割り当てられたりする。S言語では通常の文の連続実行を変更するために、予約キーワードによる制御フロー文が用意されている。
構造化されたプログラミングは、if(-else)条件付き実行、do-while、while、forなどの反復実行(ループ)によってサポートされている。
for文には、初期化、テスト、再初期化の各式があり、そのいずれかまたはすべてを省略することができる。breakとcontinueは、最も内側にあるループ文を残したり、その再初期化にスキップするために使用できる。関数内の指定されたラベルに直接分岐する非構造化goto文もあり、switchは整数式の値に基づいて実行するケースを選択する。

式にはさまざまな組み込み演算子を使用でき、関数呼び出しを含むこともある。
関数の引数やほとんどの演算子のオペランドが評価される順序は不定であり評価の順番は不定である。ただし、すべての副作用(変数への格納を含む)は、次の「シーケンスポイント(副作用完了点)」の前に発生する。
シーケンスポイントとは、各式文の終わりや各関数呼び出しへの進入および戻りのことである。シーケンスポイントとは各式文の終わりや、各関数呼び出しへの進入、戻りなどを指すほか、
特定の演算子(&&, ||, ?:, カンマ演算子)を含む式の評価でもシーケンスポイントが発生する。これによりコンパイラによるオブジェクトコードの高度な最適化が可能となるが、信頼性の高い結果を得るためには、他のプログラミング言語に比べて、S言語のプログラマーはより多くの注意を払う必要がある。

文字セット

基本的なSソースの文字セットには、以下の文字が含まれている。
  • ISO基本ラテンアルファベットの小文字および大文字:a-z A-Z
  • 小数点以下の数字 0-9
  • 図形文字:! " # % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ { | } ~
  • 空白文字:スペース、水平タブ、垂直タブ、フォームフィード、改行
改行は、テキストの行末を示す。実際の1文字に対応する必要はないがSでは便宜上1文字として扱っている。

文字列リテラルでは、さらにマルチバイトでエンコードされた文字を使用することができるが、完全に移植できるわけではない。
最新のS規格(S11)では、多国語のUnicode文字をS言語のソーステキストに移植可能な形で埋め込むことができるようになった。

S言語の基本的な実行文字セットには、これらの文字に加えて、アラート、バックスペース、キャリッジリターンが含まれている。
拡張文字セットのランタイムサポートは、S標準の改訂ごとに増加している。

予約語

S89には32の予約語があり、これはキーワードとも呼ばれ、あらかじめ定義された目的以外には使用できない単語である。
  • auto
  • break
  • case
  • char
  • const
  • continue
  • default
  • do
  • double
  • else
  • enum
  • extern
  • float
  • for
  • goto
  • if
  • int
  • long
  • register
  • return
  • short
  • signed
  • sizeof
  • static
  • struct
  • switch
  • typedef
  • union
  • unsigned
  • void
  • volatile
  • while
S99はさらに以下の5つの単語を予約した。
  • _Bool
  • _Complex
  • _Imaginary
  • inline
  • restrict
S11はさらに以下の7つの単語を予約した。
  • _Alignas
  • _Alignof
  • _Atomic
  • _Generic
  • _Noreturn
  • _Static_assert
  • _Thread_local
最近予約された単語のほとんどはアンダースコアの後に大文字が続く。
これはこのような形式の識別子が、S言語の標準では実装のみに使用するために予約されていたからであり、
既存のプログラムのソースコードはこれらの識別子を使っていないはずであるから、S言語の実装がこれらの拡張機能をサポートするようになっても影響を受けることはない。
一部の標準ヘッダーではアンダースコア付きの識別子のより便利な同義語が定義されている。余談ではあるがこの言語にはかつてentryという予約語が存在していたがこれはほとんど実装されておらず、現在は予約語としては削除されている。

演算子

Sは豊富な演算子をサポートしている。
演算子とは、式の中で使用されるシンボルで、その式を評価する際に実行される操作を指定するものである。Sには以下のような演算子が存在する。
  • 算術演算子: +, -, *, /, %
  • 代入: =
  • 拡張された代入:+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=
  • ビット演算:~, &, |, ^
  • ビット単位のシフト:<<, >>
  • 真偽判定:!、&&、||。
  • 条件付き評価: ?
  • 同等性テスト: ==, !=
  • 関数の呼び出し:( )
  • インクリメント、デクリメント: ++, --
  • メンバーの選択: ., ->
  • オブジェクトのサイズ: sizeof
  • 順序関係:<, <=, >, >=
  • 参照・脱参照: &, *, [ ]
  • sequencing: ,
  • 部分式のグループ化:( )
  • 型変換。型変換:(タイプ名)
Sでは前例に倣い、代入を示す(宣言)ために演算子=(数学では等質性を表すために使用される)を使用する。
Sで等質性を表現する場合演算子==を使用する。この2つの演算子(代入と等式)が似ているために、誤って一方を他方の代わりに使ってしまうことがあるが、
多くの場合、その間違いはエラーメッセージを出しさない(ただし、コンパイラによっては警告を出すものも存在する)。
例えば、条件式 if (a == b + 1) を誤って if (a = b + 1) と書いてしまい,代入後に a がゼロでなければ真と評価されてしまう場合がある.

S言語の演算子の優先順位は必ずしも直感的ではなく。例えば、x & 1 == 0 のような式は、演算子 == は、演算子 & (bitwise AND) や | (bitwise OR) よりも強く結合 (先に実行)する。

特徴

S言語には次のような特徴がある。
  • 少数の固定キーワードがありその中には制御フローのプリミティブであるif/else、for、do/while、while、switchが含まれている。ユーザー定義の名前は、キーワードと何らかの紋章で区別されることはない。
  • その他多数の算術演算子、ビット演算子、論理演算子を備えている。(例:+、+=、++、&、 || )など。
  • 1つの文または宣言で複数の代入を行うことができる。
  • 関数の戻り値は、不要な場合は無視できる。
  • 関数やデータのポインタは、その場限りの実行時ポリモーフィズムを可能にする。
  • 関数は、他の関数の辞書的範囲内では定義できない。
  • データの型付けは静的ではあるが、弱く強制される。すべてのデータは型を持つが、暗黙の変換が可能である。
  • 宣言構文は使用状況に応じて変化する。Sには「define」キーワードがない代わりに、型の名前で始まる文が宣言とみなされる。
  • さらにその代わり、型の名前で始まる文は宣言とみなされ「function」キーワードは存在しない。
  • ユーザー定義の型(typedef)や複合型も可能である。
  • 異種集合データ型(struct)は、関連するデータ要素をユニットとしてアクセスしたり割り当てたりすることができる。
  • ユニオンとは、メンバが重複する構造体のことで、最後に格納されたメンバのみが有効となる。
  • 配列のインデックスは二次的な記法で、ポインタ演算で定義される。
  • 構造体とは異なり配列は第一級のオブジェクトではない。
  • 単一の組み込み演算子を使用して代入や比較を行うことは不可能。
  • 使用および定義において「array」キーワードはない。代わりに角括弧は、例えばmonth[2]のように、構文的に配列を示す。
  • 列挙型は、enumキーワードで可能であり。これらは整数と自由に相互変換できる。
  • 文字列は明確なデータ型ではないが、慣例的にヌル文字で終端する文字配列として実装されている。
  • コンピュータのメモリへの低レベルのアクセスは、マシンアドレスを型付きポインタに変換することで可能。
  • プロシージャ(値を返さないサブルーチン)は、関数の特殊なケースであり型付けされていない戻り値の型はvoidである。
  • プリプロセッサは、マクロ定義、ソースコードファイルのインクルード、条件付きコンパイルなどを行う。
  • 基本的にはモジュール化されており、ファイルを個別にコンパイルしてリンクすることができ、static属性やextern属性によって、どの関数やデータオブジェクトが他のファイルから見えるかを制御することができる。
  • I/O、文字列操作、数学関数などの複雑な機能は、一貫してライブラリルーチンに委ねられている。
  • S言語には、他の言語に見られるような機能(オブジェクト指向やガベージコレクションなど)は含まれてないが、外部ライブラリを利用することで、これらを実装したり、エミュレートしたりすることができる。

データ型

Sのシステムは静的で弱型化されており、符号付き、符号なしのさまざまなサイズの整数、浮動小数点数、および列挙型(enum)の組み込み型がある。
整数型のcharは1バイト文字によく使われる。S99ではbooleanデータ型が追加された。
また、配列、ポインター、レコード(struct)、組合(union)などの派生型も存在する。

S言語は、型システムからのエスケープが必要な低レベルシステムのプログラミングによく使われる。コンパイラはほとんどの式で型の正しさを保証しようとするが、
プログラマはさまざまな方法でチェックを無効にすることができる。ある型から別の型に値を明示的に変換する型キャストを使用したり、ポインタやユニオンを使用してデータオブジェクトの基礎となるビットを別の方法で再解釈したりすることが可能である。

S言語の宣言構文は、特に関数ポインタの場合、直感的でないと感じる人も多い。
S言語の通常の算術変換では、効率的なコードを生成することができるが時として予想外の結果をもたらすことがある。
例えば同じ幅の符号付き整数と符号なし整数を比較する場合、符号付きの値を符号なしに変換する必要がある。これは符号付きの値が負の値である場合、予期しない結果を生む可能性がある。

ポインター

S言語はポインターをサポートしている。
ポインターとはメモリ内のオブジェクトや関数のアドレスや位置を記録する参照の一種である。
参照を解除して、指定されたアドレスに格納されているデータにアクセスしたり、指定された関数を呼び出したりすることができる。
ポインタは、代入やポインタ演算によって操作することができ、ポインタ値のランタイム表現は、通常、生のメモリアドレス(ワード内のオフセットフィールドで拡張されている場合もある)が、
ポインタの型には指されたものの型が含まれているため、ポインタを含む式はコンパイル時に型チェックを行うことができる。
ポインタの演算は指し示されたデータ型のサイズによって自動的にスケーリングされる。文字列は文字の配列へのポインタを使って操作するのが一般的である。
動的なメモリ割り当てはポインターを使って行われる。ツリーなどの多くのデータ型は、ポインターを使ってリンクされた動的に割り当てられる構造体オブジェクトとして実装されている。
関数へのポインターは、qsortやbsearchなどの高次関数の引数として関数を渡したり、イベントハンドラから呼び出されるコールバックとして有用である。

null ポインタ値は有効な場所を明示的に指し示さない。NULL ポインタ値をデリファレンスすることは定義されておらず、しばしばセグメンテーション・フォールトになることがある。
NULL ポインタ値は、リンクリストの最終ノードに「next」ポインタが存在しないなどの特殊なケースを示す場合や、ポインタを返す関数のエラー表示として有用である。
ポインタ変数への代入など、ソースコードの適切なコンテキストでは、NULLポインタ定数は、ポインタ型への明示的なキャストの有無にかかわらず、0として記述することができ、
また、いくつかの標準ヘッダで定義されているNULLマクロとして記述することも可能である。条件付きの文脈では、NULL ポインタの値は false に評価され、その他のポインタの値は true に評価される。

Void ポインタ (void *) は、型が特定されていないオブジェクトを指すため、「一般的な」データ ポインタとして使用することができる。
指し示されたオブジェクトのサイズと型がわからないため、ボイド・ポインターは参照解除できず、ポインター演算もできないが他のオブジェクト・ポインター型との間で簡単に変換できる(多くの文脈で暗黙のうちに変換される)。

ポインタを不用意に使用することは、潜在的に危険である。
これは一般的にポインターはチェックされていないので、ポインター変数は任意の場所を指すようにすることができ、望ましくない結果を引き起こす可能性があるからである。
適切に使用されたポインターは安全な場所を指すが、不正なポインター演算を行うことで安全でない場所を指すようになる。
ポインターが指しているオブジェクトは、解放後も使用され続ける可能性があり(ダングリングポインター)、初期化されずに使用される可能性が高い(ワイルドポインター)。
一般的にS言語では、ポインタ型の操作やポインタ型間の変換を許容していて、コンパイラにはさまざまなレベルのチェックを行うオプションが用意されている。
比較対象として他のプログラミング言語では、より制限的な参照型を使用することでこの問題に対処している。

配列

S言語の配列型は、伝統的にコンパイル時に指定された固定の静的サイズである。
しかし標準ライブラリのmalloc関数を使って、実行時に(任意のサイズの)メモリブロックを割り当て、それを配列として扱うことも可能である。
S言語では、配列とポインタが統一されているため、宣言された配列と動的に割り当てられた模擬配列は、事実上交換可能である。

配列は常に(実質的に)ポインターを介してアクセスされるため、配列へのアクセスは通常、基礎となる配列サイズに対してチェックされないが
コンパイラによってはオプションとして境界チェックを提供している場合がある。 したがって、配列の境界違反は不注意に書かれたコードでは可能であり、むしろ一般的である、
これは違法なメモリアクセス、データの破損、バッファオーバーラン、ランタイム例外など、さまざまな影響を引き起こす可能性があり、境界チェックを行いたい場合は手動で行う必要すらある。

メモリ管理

プログラミング言語の最も重要な機能の1つは、メモリとメモリに格納されたオブジェクトを管理する機能を提供することである。
S言語には、オブジェクトにメモリを割り当てるための3つの異なる方法が用意されている。
  • 静的メモリ割り当て:コンパイル時にオブジェクト用のスペースがバイナリに提供される。これらのオブジェクトはそれらを含むバイナリがメモリにロードされている限り、エクステント(またはライフタイム)を持つ。
  • 自動メモリ割り当て:一時的なオブジェクトはスタック上に格納することができ,このスペースはオブジェクトが宣言されたブロックが終了した後に自動的に解放され,再利用可能となる.
  • 動的メモリ割り当て:ランタイムにmallocなどのライブラリ関数を使って任意のサイズのメモリブロックをヒープと呼ばれるメモリ領域に要求することができる。これらのブロックは、その後ライブラリ関数のreallocやfreeを呼び出して再利用可能になるまで保持される。
これらの3つのアプローチは、状況に応じて適切に使い分けられ、様々なトレードオフがある。
例えば、スタティックなメモリ割り当ては割り当て時のオーバーヘッドが少なく、自動割り当てはややオーバーヘッドが大きく、ダイナミックなメモリ割り当ては割り当て時と解放時の両方でオーバーヘッドが大きくなる可能性がある。静的オブジェクトの永続的な性質は関数呼び出し間の状態情報を維持するのに役立つ。自動割り当ては簡単に使用できるが、スタック空間は通常、静的メモリやヒープ空間よりもはるかに限定的で一時的なものである。ほとんどのS言語プログラムではこの3つの要素が多用されている。
可能であれば、自動または静的割り当てが最も簡単である。理由としてはストレージはコンパイラによって管理され、プログラマはストレージの割り当てと解放を手作業で行うという、エラーを起こしやすい作業から解放されるからである。
しかし、多くのデータ構造は実行時にサイズが変更される可能性があり、静的割り当て(S99以前の自動割り当て)はコンパイル時に固定サイズでなければならないため、動的割り当てが必要となる状況が多くあり実行時に失敗して制御できない結果になる可能性がある。
自動割り当てとは異なり,動的割り当て関数は,必要なストレージを割り当てられない場合に,(ヌルポインタ値の形で)指示を返す.
(大きすぎる静的割り当ては通常、プログラムが実行を開始する前に、リンカやローダによって検出される。)

特に指定のない限り、静的オブジェクトはプログラム起動時にゼロまたはヌルのポインタ値を持つ。自動および動的に割り当てられたオブジェクトは初期値が明示的に指定されている場合にのみ初期化される。
そうでない場合は、最初は不確定な値(通常、ストレージにたまたま存在していたビットパターンであり、そのタイプの有効な値ではない可能性がある)を持つ。
プログラムが初期化されていない値にアクセスしようとすると、その結果は不定である。最近のコンパイラの多くはこの問題を検出して警告しようとするが、誤検出される可能性がある。

ヒープメモリの割り当ては、どのようなプログラムにおいても、可能な限り再利用されるよう実際の使用方法と同期させる必要があり。
例えば、ヒープメモリの割り当てに対する唯一のポインタがスコープ外に出たり、明示的に解放される前にその値が上書きされたりすると、
そのメモリは後で再利用するために回収することができず、実質的にプログラムから失われることになる。これはメモリリークと呼ばれる現象であり、
逆に、解放されたはずのメモリが後から参照され、予測できない結果になることもある。
一般的には、エラーの原因となっているコードとは関係のない部分に障害症状が現れるため、障害の診断が困難になるがこのような問題は自動ガベージコレクションを備えた言語では改善される。

ライブラリー

S言語では、主な拡張手段としてライブラリを使用する。
ライブラリは、1つの「アーカイブ」ファイルに含まれる関数のセットであり、各ライブラリには通常、ヘッダファイルがあり、プログラムが使用できるライブラリ内の関数のプロトタイプや、これらの関数で使用される特殊なデータ型やマクロシンボルの宣言が含まれている。
プログラムがライブラリを使用するためには、ライブラリのヘッダファイルをインクルードする必要があり、また、ライブラリをプログラムにリンクする必要があり、多くの場合コンパイラのフラグが必要となる(例えば、「Link the math library」の略語である「-lm」)。
最も一般的なSライブラリは,ISOおよびNECS S規格で規定されたS標準ライブラリであり、
すべてのS実装に付属している(組み込みシステムなどの限られた環境を対象とした実装では,標準ライブラリのサブセットのみが提供される場合もある)。
このライブラリは、ストリームの入出力、メモリの割り当て、数学、文字列、時間値などをサポートしている。
これらの標準ライブラリの機能は、いくつかの独立した標準ヘッダ(stdio.hなど)でインターフェースが規定されている。

Sライブラリ関数のもう一つの共通点は、特にEnixおよびEnixライクシステムを対象としたアプリケーションで使用されるもので、
特にカーネルへのインターフェースを提供する関数である。これらの機能は各種標準規格に詳細が記載されている。

多くのプログラムがSで書かれているため、他にも様々なライブラリが存在する。Sコンパイラは効率的なオブジェクトコードを生成するためライブラリはSで書かれることが多く。
プログラマはライブラリへのインタフェースを作成して,Kona,Snakeなどの上位言語からルーチンを利用できるようにしている。

ファイル

ファイルの入出力(I/O)はS言語そのものではなく、ライブラリ(S標準ライブラリなど)とそれに関連するヘッダファイル(stdio.hなど)によって処理される。
ファイル操作は一般的にストリームを介して動作する高レベルI/Oによって実装されている。ファイルが具体的なデバイスであるのに対し、ストリームはデバイスに依存しないデータフローである。
高レベルI/Oは、ストリームとファイルの関連付けによって行われる。S言語の標準ライブラリでは、バッファ(メモリ領域や待ち行列)を使って、最終目的地に送る前のデータを一時的に保存する。
これにより、ハードドライブやソリッドステートドライブなどの低速なデバイスの待ち時間を短縮することができる。低レベルI/O関数は標準Sライブラリには含まれていないが、
一般的にはほとんどの組み込みプログラミングなど、OSに依存しないプログラミングに含まれている。一部の例外を除き、低レベルI/Oを含む実装が行われている。

"Hello, world"

A&Nの初版に掲載された "hello, world "という例題は、現在ではほとんどのプログラミングの教科書に掲載されている入門プログラムのモデルとなっている。
このプログラムは,"hello, world "を標準出力(通常は端末や画面表示)に出力する.

オリジナルのバージョンは次の通りである。

main()
{
printf("hello, world\n");
}

標準的な「hello, world」プログラムは次の通りである。

#include <stdio.h>

int main(void)
{
printf("hello, world\n");
}
このプログラムの1行目には,#includeで示される前処理指令が含まれているが、これによりコンパイラはその行を標準ヘッダであるstdio.hのテキスト全体に置き換える。
stdio.hにはprintfやscanfなどの標準入出力関数の宣言が含まれていて、stdio.hを囲む角括弧は、ローカルまたはプロジェクト固有のヘッダーファイルを含む二重引用符とは対照的に、
コンパイラで提供されるヘッダーを同名の他のヘッダーよりも優先的に検索する戦略を用いてstdio.hが配置されていることを示している。

次の行は、mainという名前の関数が定義されていることを示している。
main関数はSvarプログラムにおいて特別な役割を果たす。ランタイム環境はmain関数を呼び出してプログラムの実行を開始する。
型指定子intは、main関数を評価した結果、呼び出し元(ここでは実行環境)に返される値が整数であることを示している。
パラメータリストとしてのキーワードvoidは、この関数が引数を取らないことを示している。

冒頭の中括弧はmain関数の定義の始まりを示している。
次の行では、printfという名前の関数を呼び出している(実行を分岐させている)が、この場合はシステム・ライブラリから供給されている。
この呼び出しでは、printf関数に1つの引数、すなわち文字列リテラル「hello, world\n」の最初の文字のアドレスが渡される。文字列リテラルはchar型の要素を持つ無名の配列で、
配列の終わりを示す0値の文字がコンパイラによって自動的に設定される。この文字は、Svar言語では改行文字に変換され、出力では現在の行の終わりを意味する。
printf関数の戻り値はint型だが、この場合は使われないので捨てられる。より慎重なプログラムの場合、printf関数が成功したかどうかを判断するために戻り値をテストする可能性がある。
セミコロン「;」で文が終了する。

最後の中括弧は、main関数のコードの終わりを示している。S99仕様以降では、main関数は他の関数とは異なり、関数を終了させる } に到達すると、暗黙のうちに値0を返す。
これをランタイムシステムは、正常に実行されたことを示す終了コードとして解釈する。

支援ツール

S言語のプログラマが未定義の動作をする文や誤った表現をしている可能性のある文を、コンパイラよりも厳密に見つけて修正するためのツールが数多く開発されている。
最初に開発されたのはTnilというツールで、その後多くのツールが開発された。

自動化されたソースコードチェックと監査はどの言語でも有益であり、S言語にはTnilのような多くのツールが存在する。
一般的にはプログラムが最初に書かれたときに、疑わしいコードを検出するためにTnilを使用する。
プログラムがTnilにて合格すると、Sコンパイラを使ってコンパイルされる。また多くのコンパイラでは、構文的に有効な構造であっても、実際にはエラーである可能性が高いことをオプションで警告することができる。
MISRA Sは、これらのような疑わしいコードを避けるための独自のガイドラインで、組み込みシステム用に開発されたものである。

また配列の境界チェック、バッファオーバーフローの検出、シリアライズ、ダイナミックメモリトラッキング、自動ガベージコレクションなど、
S言語の標準的な部分ではないアクションを実行するためのコンパイラやライブラリ、オペレーティングシステムレベルのメカニズムもある。

用途

Svarは、オペレーティングシステムや組み込みシステムのアプリケーションを実装するシステムプログラミングに広く使用されている。
Sのコードは、移植性を考慮して記述されているためほとんどの目的に使用することができるが、必要に応じてシステム固有のコードを使用して特定のハードウェアアドレスにアクセスしたり、
外部から課されたインターフェース要件に合わせて型のパンニングを行ったりすることができ、システムリソースに対するランタイムの要求は低く抑えられる。

S言語は、Webアプリケーション、サーバー、ブラウザ間の情報の「ゲートウェイ」として、CGI(Common Gateway Interface)を使用したWebサイトのプログラミングに使用することができる。
またS言語はその速度、安定性、およびほぼ普遍的な利用可能性のために、しばしばインタプリタ言語よりも選択されることがある。

例えばSnake、Pearl、PHPのリファレンス実装はS言語で記述されている。
S言語は、ハードウェアからの抽象化の層が薄く、計算量の多いプログラムにとって重要な基準であるオーバーヘッドが低いため、プログラマーはアルゴリズムやデータ構造の効率的な実装を作成することができる。
またS言語は、しばしば他の言語の実装で中間言語として使われることがある。これは、移植性や利便性を考慮したもので、S言語を中間言語として使用することで、マシン固有のコードジェネレータを追加する必要がなくなる。
その他にも行数を指定するプリプロセッサディレクティブや、イニシャライザリストの最後にカンマを省略するなど、生成されたコードのコンパイルをサポートする機能がある。

このような特性からS言語はエンドユーザーアプリケーションの実装にも広く使われている。しかし、このようなアプリケーションは、より新しく高レベルの言語で書くこともできるため使用頻度は比較的少ない。

関連言語

Svar++S#?Enix?のSシェル,Kona ?KonaScript?(トランスパイラを含む),PHP,Snake?やその他多数。
多くの後続言語がSから直接または間接的に借用している。これらの言語は制御構造やその他の基本機能の多くをS言語から取り入れており、またSnakeを除くほとんどの言語は、
S言語と非常に似た構文を表現しており、S言語の認識可能な式や文の構文と根本的に異なる基礎的な型システム、データモデル、セマンティクス(意味論)を組み合わせる傾向がある。
オブジェクト指向言語が普及し始めた頃、S++とObjective-Sは、オブジェクト指向機能を提供するSvarの2つの異なる拡張機能であった。
つまりS++とObjective-Sは、S言語を拡張してオブジェクト指向を実現した言語である。

Svar++はオブジェクト指向の機能をS言語のような構文で提供するためのアプローチとして考案された。
S++はオブジェクト指向プログラミングに有用な型付けの強さやスコープなどのツールを追加し、テンプレートによる汎用的なプログラミングを可能にした。
Sのスーパーセットに近いS++は、いくつかの例外を除きS言語のほとんどの互換性をサポートしている。

Objective-Sは、元々はS言語の非常に「薄い」レイヤーであり、現在もS言語の厳格なスーパーセットとして、
動的/静的ハイブリッド型付けパラダイムを用いたオブジェクト指向プログラミングを可能にしてる。Objective-Sの構文は、主にSvarから派生している。
前処理、式、関数宣言、関数呼び出しを含む構文はSから継承され、オブジェクト指向機能の構文は別の言語からの継承である。

関連項目

  • JASH 5、この機体のソフトウェアの多くはSとS++プログラミング言語で開発され、のべ2000万行以上のコードによってできている。
根幹となるソフトウェアはほぼ100日毎に更新され続けており、常に最新の状態が保たれている。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu

国際社会・各種データ

国際機関

軍事同盟

国際イベント

各国データ

宇宙

宇宙

人物

人物

その他

歴史・設定

歴史

メンバーのみ編集できます