第4回 第3回へ 第5回へ 目次へ
2006 / Aug / 5
はじめに
 このサイトは、プログラミングが始めての方やC言語を学習されていない方を対象に、Macのプログラミングの手法の中から「 Cocoa (ココア)」と呼ばれるものを紹介しています。
 プログラムは、プログラミング言語によって書かれているということはご存知だと思います。Cocoaでは、Objective-C言語というC言語を発展させたプログラミング言語を使用しています。この「Objective-C Primer」では、Objective-C言語を理解する上で必要となるC言語の基礎を学習して、Objective-CとCocoaの入り口までご案内することを目指しています。

ご注意  このサイトに書かれているサンプルコードの実行はあなたの自己責任において行ってください。このサイトおよび私は、このサイトのサンプルコードの実行によって発生するいかなる損害にも責任がなく保証もいたしません。

2006年9月8日修正
2006 / Aug / 5
Objective-C言語への移行
 今まで勉強してきたものはC言語と呼ばれているものです。なぜ C言語を勉強したのかと言えば、Objective-C言語は、C言語のサブセットだからです。ここでサブセットと、わざとまぎらわしい言葉を使ってみました。通常「サブ」は補欠・補充員という意味で、あまりメジャーな感じは受けません。しかしここで第1回の「オブジェクト指向プログラミングの特徴」を思い出してください。継承という特徴の中で親クラスのことをスーパークラス、子クラスのことをサブクラスと呼んでいました。
 ここでいう「Objective-Cは、Cのサブセットである」という意味は、Objective-Cは、Cのすべての機能を継承し、さらに新しい機能(オブジェクト指向)を取り入れたものである。という意味です。前回のコラムで書いたようにプログラミングにはまぎらわしい言葉が多くでてきます。それに慣れてもらうために、サブセットという言葉を使ってみました。なおサブセットの意味はその時々によって様々に変わっていくように思います。

 では、前回に作った func (もしくは、あなたが名付けた)プロジェクトを立ち上げてください。左側の「グループとファイル」から func(もしくは、あたなが名付けたプロジェクト名) をクリックして右側の「ファイル名」にプロジェクト内のすべてのファイルを表示させます。その中から main.c をクリックして「ファイルメニュー」から「名称変更」を選んでください。 func.m という名前に変えます。「ファイル」メニューからではなくて main.c を Control + クリック、もしくは右ボタンクリックで現れるコンテキストメニューからでも「名称変更」を選べます。「 .c 」というのは C言語のファイルに付けられる拡張子です。「 .m 」がObjective-C言語ファイルの拡張子です。ファイル名の左側の小さなアイコンも に変わったことだと思います。これでもう Objective-C言語 を使いはじめたことになります。また プロジェクト内のファイルが一つだけのときには、main 関数が書かれているファイルを必ずしも main.m (もしくは、main.c) にする必用はありません。

 前回現れていた Warning, return type defaults to 'int'という警告をださないために main( )関数に int という返り値を書き加えます。


#include < stdio.h >
/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!");
    return 0;
}



では「ビルドして実行」をクリックします。実行結果は次の通りになると思います(図18)

図18

返り値 int を明記しましたので前回と違い赤丸の中に警告の数が表示されていません。そしてもうこのプログラムは C言語ではなく、Objective-C言語でコーディングされたプログラムということになります。
実際には、現段階でfunc.mのなかに書かれているソースコードはすべてC言語のものです。これでObjective-CがC言語のすべての機能を受け継いでいるということが分かると思います。

次にもっとCocoaらしいコードにしてみましょう

4行目と5行目の間に、NSLog(@"\nViva Cocoa!!!");というコードを書き加えます。
4行目も、printf(@"Viva Cocoa!\n");と「 \n 」を追加してください。
 NSLog( )関数は、printf( )関数に代わり、Objective-Cでログ画面へ文字を表示させるために新しく作られた関数です。「 \n 」というのは改行を意味しています。printf( )関数とNSLog( )関数で「 \n 」の位置が違うのは、2つの関数で改行のポイントが違うからです。「 \n 」の位置をいろいろと移動させて試してみるのも良いでしょう。また NSLog( )関数で引数の最初に付いている @は、そのあとに続く "   " で囲まれた文字列が NSString というオブジェクトであることを表しています。一方、printf( )関数の "   " は純粋なC言語の文字列であることを表しています。NSLog( )関数については、次回で詳しく説明いたします。
 なお、ご覧のブラウザによっては、「 \ 」がバックスラッシュではなく、円マーク「 ¥ 」に見えているかもしれません。このサイトでは「 \ 」がバックスラッシュに見えても、円マークに見えてもまったく問題はありません。実際に日本語キーボードにはバックスラッシュのキーはなく、代わりに「 ¥ 」マークが刻まれています。通常のプログラミングでも入力モードが英数モード(直接入力モード)でさえあれば、バックスラッシュでも円マークでも、どちらで入力しても同じ働きをしてくれます。
 しかし残念ながら、Cocoaプログラミングでは、「 \ 」を明確にバックスラッシュとして入力したほうが良いみたいです。下記の図を参考にして「ことえり環境設定」の「入力文字」で「 ¥ 」キーがバックスラッシュを入力するように変更しておくほうが良いでしょう(図19)。
 ただし「ことえり環境設定」で変更してもホームページ上での見え方は変わりません。サンプルコードで「 ¥ 」マークが出ていましたら、バックスラッシュと読み替えてコーディングを行ってください。

図19



#include < stdio.h >
/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    return 0;
}


これで実行してみると、次のようなログ画面になります(図20)

図20


「Viva Cocoa」と表示されていません。ステータスも0ではない数字が返ってきています。どうやらまともに実行できなかったみたいです。これは NSLog のヘッダーを読み込まなかったせいです。fumc.m の2行目に #import  < Foundation/Foundation.h > と書き加えます。


#include < stdio.h >
#import  < Foundation/Foundation.h >
/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    return 0;
}


しかしこれで実行してもさきほどと同じ結果になります。「Viva Cocoa」という文字は表示されませし、ステータス0も返ってきません。
これは、 Foundation.h が、printf( )関数のために読み込んだ stdio.h と違い、単なるヘッダーファイルではなくフレームワークであるからとしか言えません。フレームワークの場合はソースコードに書くだけではなく明示的にプロジェクトに追加しなければなりません。
Xcode「プロジェクト」メニューから「プロジェクトに追加...」をクリックします。現れるオープンダイアログで、
Macintosh HD / システム / ライブラリ / Framewarks / Foundation.framework と選択していき、
Foundation.framework を「追加」します。ここは間違いやすいポイントなのでゆっくり探してください。最後に次のようなダイアログが表示さますので、デフォルトのまま「追加」をクリックします(図21)。

図21


これで、プロジェクトウィンドウは下記のようになると思います(図22)。

図22


これで「ビルドして実行」をクリックしてみましょう。次のような実行結果になると思います(図23)。

図23


お疲れさまでした。これでこの項は終ります。
2006 / Aug / 8
コードを見ていきましょう
では、コードを詳しく見ていきましょう。


#include < stdio.h >
#import  < Foundation/Foundation.h >
/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    return 0;
}


1行目 #include
 #includeで、printf( )関数の定義が書かれているstdio.hというヘッダファイルを読み込んでいます。これによりprintf( )関数がソースコードの中で正しく書かれているかどうか(正しく使われているかどうか)をコンパイル(ソースコードを機械語に変える作業)のときにヘッダファイルと照合されてチェックされます。正しく書かれていないようならエラーか警告が出ます。なお、stdio.hが <    > で囲まれているのは、開発環境がすでにstdio.hが保管されている場所を最初から知っていることを表しています。

2行目 #import
 Objective-Cで新しく追加された言葉です。働きは #include と同じですが、ヘッダファイルの2重読み込みをいたしません。サンプルのプログラムではソースコードファイルが1つしかありませんが、通常は複数になるほうが多いでしょう。その場合、各ソースコードファイルが#includeでstdio.hを読み込みますと、コンパイルの時に「同じ名前の関数が複数定義されている」とコンパイラが文句を言ってきます。
 これに対して #import を使うと、このようなヘッダファイルの2重読み込みをしないことになっています。2重読み込みに気を使うことなくヘッダファイルの読み込みが行えます。すでに、このサンプルプログラムでは、どちらのヘッダファイルも #include、#import ともに使えますので試してみても良いでしょう。

3行目 /*    */
 コメントを書くところです。コンパイル時には無視されます。プログラマーの覚え書きとして使います。複数行コメントとして使える */ 〜 */ と 単数行コメントの // 〜 があります。なお、コメントは出来るだけ書くように推奨されています。実際に自分で書いたソースコードでも数ヶ月すると「はて、ここは何を意味しているんだろう?」と思うことがしばしばあります。そういう意味でコメントはできるだけ書くようにいたしましょう。

4行目 int main( ) {
 main関数です。(    ) 間の引数を受け取り、 {    } 間の命令(式)を実行して、int という整数の値を返します。このサンプルでは引数は受け取らないことを ( ) と未記入で表していますが、明示的に ( void ) と表すこともできます。void と書き加えて試してみてください。
なお、この
    返り値    関数名( 引数 ) { 実行式 }
という関数の定義の仕方は、main関数だけではなく、すべての関数で同じです。

5行目 printf("Viva Cocoa!\n");
 C言語で最初から用意されている printf( )関数を呼び出しています。関数の呼び出しは通常このように、関数名( );と記述します。最後のセミコロン「;」を忘れないようにしましょう。セミコロンは一つの実行式の終わりを意味します。( )のなかに書かれているのが引数です。この場合は、"Viva Cocoa!\n" という引数を渡していることなります。
 この "    " は、あいだに書かれているのが文字列であることを表しています。改行を表す \n も文字列の中に書かれていることに注意してください。例えばスペース(空白)は実際に文字はありませんが、1文字分空けるという働きを果たしています。それと同じように改行も一つの文字の働きをしている、という考え方です。
 なお、このようにコード中に直接書かれた値(あたい)のことをリテラル(もしくはリテラル定数)と呼びます。プログラミングでは文字・文字列も値です。リテラルについては次の項で説明いたします。

6行目 NSLog(@"\nViva Cocoa!!!");
 Cocoaフレームワークで新しく追加された関数です。Cocoaフレームワークで追加された関数やクラスにはNSという接頭語(せっとうご)が付いています。NSとは、Mac OS X や Cocoaフレームワークの前身である NeXTSTEP というOSから由来しています。この関数の働きは printf( )関数とほぼ同じですが、引数のリテラル文字列の前に @が付いています。これは @に続く文字列が Cocoaフレームワークで定義されている NSString というクラスのオブジェクトであることを表しています。
 このように Objective-C で新しく追加された機能には NSという接頭語や @が付きます。これらの接頭語や @が付いていないものは、C言語のときからある機能だと思ってほぼ良いでしょう。

7行目 return 0;
 return は、C言語のときからある 予約語 の一つです。
この return に出会うと、そこでその関数を終了して呼び出しもとに戻ります。通常、関数は、最後の「 } 」に出会うことで終りますが、途中で return に出会うとそこで強制的にその関数を終了して呼び出し元に戻ります。その際に、このサンプルのように値を指定すれば、それを返り値として呼び出し元に返してから終了します。なお、このサンプルの返り値 0 も、コードに直接書かれた値、リテラル定数です。
 なお、 return 文については、 C言語 予約語 一覧 で詳しく説明しています。ご欄になってください。

8行目 }
関数は通常、このブロック括弧の閉じる「 } 」で終了するのが一般的です。ただしANSI(アンシ。American National Standards Institute。アメリカの国家規格協会。日本のJISに相当) では現在、main関数は int値 (整数値)を返す事になっていますので、このサンプルでは7行目の return文で int値を返して終っています。なお、このサンプルのように {    } のあいだに複数の式を書いた文のことをブロック文と呼びます。

最後に
 printf( )関数や、NSLog( )関数の場合、それぞれ stdio.h や Foundation.h でその定義を読み込んでから使用していました。では、main( )関数の場合はどうなっているのでしょうか?
 実は、関数は、その関数が始めて使われる(呼び出される)以前に定義されていなければなりません。ところが main関数はプログラムの中で最初に呼び出される関数ですので関数の定義と呼び出し(実行)が同時に行われている、ということになります。
2006 / Aug / 9
リテラル定数
リテラル (literal) という言葉を説明するために、辞書で色々と調べてみました。すると、エクシード英和辞典で【コンピュータ用語】として直定数となっていました。直定数 (たぶん「じかていすう」と呼ぶのでしょう) なんという直感的かつ端的な説明だろう、と少し感激してしまいました。しかし Google で検索してみますと、直定数という言葉を使って説明されているプログラミング言語が結構あることを知りました (私自身ははじめて出会った言葉でしたが、)。
 直定数は、言葉通りソースコード中に直(じか)に書かれた定数です。前項のサンプルでは、
"viva Cocoa!\n""\nviva Cocoa!!!"、および 0 がリテラル (直定数) になります。
もう、お気づきのことだろうと思いますが、ソースコード中に記入される値 (数、数値、文字、文字列) を、リテラル (直定数) を用いて記述されることは多いです。しかし一方、リテラル (直定数) ではなく、定数変数 という形で記述される場合も他数あります。
 定数は、字のごとく定まった (不変の) 数 (値) です。しかし、この説明だけでは、リテラルとの違いは一向に分からないと思います。定数は、変数対義語として理解するのが一番良いように思います。なお、定数(リテラルではない)の定義を辞書で調べてみようとしても、多すぎてかつあいまいだと言わざるえないように思います。では変数はなんと定義されているのでしょうか。大辞林 第二版よりますと、

 数を代表する文字がその値をいろいろとり得るとき、その文字をいう。x・y・z などで示されることが多い。
 と、なっています。上記の表現は少しむずかしい言い回しになっているかもしれませんが、ようするに変数とは値そのもののことではなくて、ある値に付けた名前である。ということです。そしてその対義語としての定数は不変の値に付けた名前である。ということになります。そしてこの変数や定数はあくまでも値ではなくて名前ですから、これらの変数や定数の値を指定するときにはリテラル定数が使われることも決して少なくありません。

 では、変数と定数の具体例はどこにあるのでしょうか。実はそれが、最初にmain( )関数の引数として指定されていた、
    int argc, const char * argv[]
にあたります。
2006 / Aug / 9
Column
スペース、改行、タブ

C言語および Objective-C言語のソースコードは、改行という概念を持っていません。複数行に渡る文(式)もセミコロン「 ; 」に出会うまでは一つの文(式)として解釈されます。
 改行やタブは単に人間がコードを読みやすくするために使われます。そしてコンパイラは、改行もタブもすべてスペース(英数字の空白)1つ分として解釈します。複数のスペースであっても1つのスペースとして解釈されます。ただし、日本語入力モードでの空白は予想外の意味に解釈されますので、文字列以外で日本語入力モードの空白を誤って入力しないように注意してください。
 では、スペースはどこで使用して良いのでしょうか。例えば、int  main( ) { では、int と mainの間のスペースをなくすわけには行きません。もしスペースを入れなければ、intmain という意味不明の言葉になってしまうからです。そして逆に ma    in と、不要なスペースを入れても意味が通じなくなりエラーとなります。しかし main と ( ) の間には、スペースがいくつあっても、あるいはまったくなくても意味は変わりません。main と ( ) のあいだに改行を入れてもエラーになりません。
 (    ) も、 () と、スペースなしで記入して大丈夫です。同じく ( ) と { の間もスペースがなくても大丈夫です。逆にスペースやタブや改行を入れてもエラーになりません。

関数の自作とプロトタイプ

 今までに、main( )関数、printf( )関数、NSLog( )関数などが登場してきましたが、このような関数は自分で作ることができます。早速一つ作ってみましょう。さきほどのソースコードを利用します。赤色の文字の部分が新たに書き加えるソースコードです。区別しやすいように赤色にしていますが、実際に赤色で入力する必要はありません。

サンプルコード1

#include < stdio.h >
#import  < Foundation/Foundation.h >

/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    myFunc();
    return 0;
}

/* 自作の myFunc 関数です */
void myFunc( ) {
    NSlog(@"myFunc is here.");
}


これで「ビルドと実行」ボタンを押すと、エラーが出ます、ただしエラーが出ても正常に起動したりします。これはもう単にXcodeが優秀なだけであって、本来このコードではまともに起動できません。そこでエラーが出ないように自作関数を main関数の前で定義するように変更いたします。

サンプルコード2

#include < stdio.h >
#import  < Foundation/Foundation.h >

/* 自作の myFunc 関数です */
void myFunc( ) {
    NSlog(@"myFunc is here.");
}

/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    myFunc();
    return 0;
}


このようにするとエラーもでずに正常に起動されます。これは関数の定義は、それが使われる(呼び出される)以前に定義されていなければならないからです。この例では、最初に呼び出される関数はmain関数です。そしてその中で自作の関数 myFuncの呼び出しが行われていますが、myFuncの定義はすでにされているのでエラーもなくプログラムが動きました。
 しかし、関数の数が少数であればこのように呼び出される以前に定義していくことも可能かもしれませんが、通常はすぐに無理がくるのは目に見えています。そこで考えられたのが関数のプロトタイプです。

サンプルコード3

#include < stdio.h >
#import  < Foundation/Foundation.h >
void myFunc( ) ;

/* 引数を持たない main 関数です */
int main( ) {
    printf("Viva Cocoa!\n");
    NSLog(@"\nViva Cocoa!!!");
    myFunc();
    return 0;
}

/* 自作の myFunc 関数です */
void myFunc( ) {
    NSlog(@"myFunc is here.");
}


サンプルコード3の3行目が自作関数 myFunc( ) のプロトタイプになっています。プロトタイプの書き方は関数の1行目「 void myFunc( ) { 」から最後の { を削除して、代わりに ; (セミコロン) を付けた形になっています。記述する場所はすべての関数の前、#include や、#import の後に続けて記述するのが良いでしょう。このプロトタイプによって、printf( )関数や、NSLog( )関数のヘッダファイルと同じように、関数の返り値の型や引数で何を受け取るのかなどをコンパイル時(機械語に変換するとき)に照合が行われて、コード中の関数が正しく使われているかチェックされます。

図24   サンプルコード実行画面

関数の作成をコラムで紹介したのは、Objective-C言語では新たに作成するのはクラスであって、関数を自作する機会はそれほどないからです。
第3回へ 第5回へ 目次へ