Cycloneプログラミング言語は、C言語の安全な方言となることを意図して開発されました。[ 2 ]バッファオーバーフローや、Cプログラムで発生 する可能性のあるその他の脆弱性を設計上回避しつつ、システムプログラミングツールとしてのCのパワーと利便性を失っていません。現在は開発元によるサポートは終了しており、リファレンスツールは64ビットプラットフォームをサポートしていません。開発元は、 Rust言語がCycloneと同じようなアイデアを多く取り入れていると指摘しています。[ 3 ]
サイクロンの開発は、2001年にAT&Tラボ研究のトレバー・ジムとコーネル大学のグレッグ・モリセットのグループによる共同プロジェクトとして開始されました。バージョン1.0は2006年5月8日にリリースされました。[ 4 ]
Cycloneは、 C言語の一般的な落とし穴を回避しつつ、その外観とパフォーマンスを維持することを目指しています。そのため、Cycloneはプログラムに以下の制限を設けています。
NULLセグメンテーション違反を防ぐためにチェックが挿入されています。free()gotoスコープ内へのアクセスは許可されていませんswitch異なるスコープのラベルは許可されていませんreturnsetjmpまた、longjmpサポートされていませんC言語プログラマーが慣れ親しんだツールセットを維持するために、Cycloneは以下の拡張機能を提供します。
NULL必要としませんNULLvoid*setjmpおよびの使用の一部が置き換えられますlongjmpCycloneは、概してC言語によく似ているが、C言語に似た言語として捉えるべきである。
Cycloneは3種類のポインタを実装しています。
*(通常タイプ)@(決してNULLポインターではない)?(ポインタ演算が許可されている唯一の型、「ファット」ポインタ)。これらの新しいポインタ型を導入する目的は、ポインタを使用する際によく発生する問題を回避することです。例えば、fooint型へのポインタを受け取る関数を考えてみましょう。
int foo ( int * p );関数を作成した人はチェックfooを挿入することもできたはずですがNULL、パフォーマンス上の理由からそうしなかったと仮定しましょう。 を呼び出すと、未定義の動作foo(NULL);が発生します(通常は、必ずしもそうとは限りませんが、アプリケーションにシグナルが送信されます)。このような問題を回避するために、Cyclone はポインタ型を導入しており、これは にはなりません。したがって、 の「安全な」バージョンは次のようになります。SIGSEGV@NULLfoo
int foo ( int @ p );これは、Cycloneコンパイラに対して、引数が決してでfooあってはならないことを指示しNULL、前述の未定義動作を回避します。*をに単純に変更することで、@プログラマはNULLチェックを記述する必要がなくなり、オペレーティングシステムはNULLポインタの逆参照を捕捉する必要がなくなります。ただし、この追加の制限は、ポインタを算術演算で直接操作できることに慣れているほとんどのCプログラマにとって、かなり大きな障害となる可能性があります。これは望ましいことですが、バッファオーバーフローやその他の「オフバイワン」型のエラーにつながる可能性があります。これを回避するため、?ポインタ型は配列のサイズという既知の境界で制限されます。これにより、ポインタに関する追加情報が格納されるためオーバーヘッドが増加しますが、安全性とセキュリティが向上します。たとえば、strlenCで記述された単純な(そして素朴な)関数を考えてみましょう。
int strlen ( const char * s ) { int i = 0 ; if ( ! s ) { return 0 ; } while ( s [ i ] != '\0' ) { i ++ ; } return i ; }この関数は、渡される文字列が で終了していることを前提としています'\0'。このコードに終了していない文字列 が渡された場合、は文字列の末尾を超えて、文字列に関連付けられていないメモリ領域にまでアクセスしてしまいます。のような非ANSI C関数は、このような問題に対処することを目的としています。charbuf[6]={'h','e','l','l','o','!'};strlenstrnlen
サイクロンのものはstrlenCバージョンと似ています。
int strlen ( const char ? s ) { int n = s . size ; if ( ! s ) { return 0 ; } for ( int i = 0 ; i < n ; i ++ , s ++ ) { if ( * s == '\0' ) { return i ; } } return n ; }ここでは、strlen渡された配列の長さによって自身に制限されます。各ポインタ型は他のすべての型に安全にキャストでき、配列と文字列は?コンパイラによって自動的にキャストされます。(からへのキャストでは?境界チェック*が呼び出され、からへのキャストではチェックと境界チェックの両方が呼び出されます。からへのキャストではチェックは一切行われず、結果として得られるポインタのサイズは1になります。)?@NULL*??
C言語で書かれた以下のコードを考えてみましょう。
char * itoa ( int i ) { char buf [ 20 ]; sprintf ( buf , "%d" , i ); return buf ; }この関数はスタック上にitoa文字配列を割り当てbuf、その配列の先頭へのポインタを返しますbuf。ただし、スタック上で使用されているメモリはbuf関数が戻るときに解放されるため、返された値は関数の外で安全に使用することはできません。GNU Compiler Collectionやその他のコンパイラはこのようなコードに対して警告を発しますが、以下のコードは通常、警告なしでコンパイルされます。
char * itoa ( int i ) { char buf [ 20 ]; sprintf ( buf , "%d" , i ); char * z = buf ; return z ; }-O2GNU Compiler Collection は、オプションまたはの副作用として、このようなコードに対して警告を生成することができます-O3が、すべてのエラーが検出される保証はありません。Cyclone はコードの各セグメントを領域分析し、このバージョンの から返されるようなダングリング ポインタを防止しますitoa。特定のスコープ内のすべてのローカル変数は、ヒープや他のローカル領域とは別の同じ領域の一部とみなされます。したがって、 を分析するとitoa、Cyclone コンパイラは がzローカル スタックへのポインタであることを認識し、エラーを報告します。
ファットポインタは、ポインタ演算を可能にするために使用されます。ファットポインタは、 で宣言する必要があります@fat。たとえば、 は、argv型char**(文字へのポインタへのポインタ) として宣言されることが多く、または (文字配列へのポインタ) として考えられます。Cyclone では、これは(文字へのファットポインタへのファットポインタ)char*[]として表現されます。char*@fat*@fat
Cycloneでは、代わりに?を表現することができます*@fat。したがって、2つの宣言は同等です。
int main ( int argc , char ?? argv ); // より冗長な宣言int main ( int argc , char * @ fat * @ fat argv );と同等C++のテンプレートと同様に、Cycloneには汎用プログラミングの形式があります。
typedef struct LinkedList < ` a > { ` a head ; struct LinkedList < ` a >* next ; } LinkedList < ` a > ;// ... LinkedList < int >* ll = new LinkedList { 1 , new LinkedList { 2 , null }};実装型をカプセル化しつつ、定義がクライアントに漏洩しないようにする「抽象型」を使用することができます。
abstract struct Queue < ` a > { LinkedList < ` a > front ; LinkedList < ` a > rear ; };extern struct Queue < ` a > ;Cycloneには、C++と同様に名前空間が存在します。名前空間はコード内の名前の衝突を回避するために使用され、::C++と同様の表記法に従います。名前空間はネストすることも可能です。
namespace foo { int x ; int f () { return x ; } }namespace bar { using foo { int g () { return f (); } }int h () { return foo :: f (); } }Cycloneでは、パターンマッチングは次のように実行できます。
int g ( int a , int b ) { switch ( $ ( a , b - 1 )) { case $ ( 0 , y ) && y > 1 : return 1 ; case $ ( 3 , y ) && f ( x + y ) == 7 : return 2 ; case $ ( 4 , 72 ): return 3 ; default : return 4 ; } }宣言letは、パターンと式を照合するために使用されます。
typedef struct Pair { int x ; int y ; } Pair ;void f ( Pair p ) { let Pair ( first , second ) = p ; // 以下と同等: // int first = px; // int second = py; // ... }Cycloneでは、CやC++のように、あるいはJavaやC#のautoようにを使用する代わりに、型推論された変数を表すために(アンダースコア)を使用します。var_
_ x = ( SomeType * ) malloc ( sizeof ( SomeType )); // 代わりに: SomeType x = ( SomeType * ) malloc ( sizeof ( SomeType ));_ myNumber = 100 ; // int型に推論Cyclone には例外処理があります。捕捉されない例外が発生すると、プログラムは停止します。Java と同様に、Cyclone にはnull ポインタ例外があり、これは と呼ばれますNull_Exception。
typedef FILEファイル;File * f = fopen ( "/etc/passwd" , "r" ); try { int code = getc (( File * @ notnull ) f ); } catch { case & Null_Exception : printf ( "エラー: /etc/passwd を開けません\n " ); return 1 ; case & Invalid_argument ( s ): printf ( "エラー: 無効な引数: %s \n " , s ); return 1 ; }例外を手動でスローすることもできます。
throw new Null_Exception ( "これはnull例外です" );プレゼンテーション: