TabIe 4 P/ECE の主な API 関数名 pceLCDDispSta 「 t pceLCDDispStop pceLCDSetBuffe 「 pceAppSetProcPeriod pceFontP 「 intf pceFontSetPos pceLCDSetObject pceLCDD 「 awObject pceLCDT 「 ans pcePadGet pceWaveDataOut 液晶画面の表示開始 液晶画面の表示停止 仮想画面のバッフア・アドレス設定 pceAppP 「 OC を実行する間隔を設定 書式指定付きで文字列を仮想画面に書き込む 文字表示座標の指定 仮想画面へのイメージ描画設定 仮想画面へイメージ描画 仮想画面を液晶画面へ転送 ボタン情報の取得 WAVE テータの出力 内容 関数名 pceWaveStop pceAppReqExit pceFiIeC 「 eate pceFileOpen pceFiIeReadSct pceFiIeWriteSct pceFiIeCIose lnitMusic PlayMusic StopMusic List P / ECE での画面描画 * 画面描画の例 #incl ude <piece. h> #include く string. h> unsigned char vbuff [ 128 * 88 extern unsigned char A [ void pceAppInit( void ) pceLCDDispStop ( pceLCDSetBuffer( vbuff memset( vbuff, 0 , 128 * 88 ); pceAppSetProcPeriod ( pceLCDDispStart ( VOid pceAppProc( int cnt int x, y; unsigned char *p ー DRAW—OBJECT Ob PIECE—BMP pbmpl; 80 PBMP—FILEHEADER pbhead; / / テキスト出力 pceFontSetPos ( 0 , 0 / / イメージ出力 pceFontPrintf ( "He Ⅱ 0 , worl d ” BMP コンバータからテキスト形式で生成したテータを取り込む 初期化中は液晶表示を消す 仮想画面設定 配列をゼロクリア ( 画面クリア ) 書式設定付き文字列出力 テキスト出力位置設定 P/ECE 形式 BMP ファイル情報 P/ECE 形式 BMP 描画情報 液晶表示再開 pceAPPProc 呼び出し間隔 memcpy(&pbhead, A, sizeof (PBMP—FILEHEADER) pbmpl. header = pbhead; pbmpl. buf = A 十 sizeOf(PBMP—FILEHEADER); / / p / ECE 形式 B 情報設定 pbmpl. mask = A 十 size0f(PBMP—FILEHEADER) 十 (pbmpl. header. h * pbmpl. header. w) / pceLCDSetObject(&0bj , &pbmpl' 40 , 0 , 0 , 0 , 48 , 72 , DRW—NOMAL); / / 描画情報設定 pceLCDDrawObject(obj / / 仮想画面へ直接描画 p = &vbuff[80 * 128 for (y = の y く y 十十 ) for (x = の x く 12 x 十十 ) (x ” 4 ) 宅 * p 十十 = VOid pceAppExit ( VOid pceLCDTrans ( / / 仮想画面へ描画 仮想画面を液晶へ転送 P / ECE のプログラムは付属の gcc で行い ます。基本的には普通の C 言語ですが , ma 2002 3 リケーション内に必ず用意します注 1 ] 。 次の 3 つの関数をアプ in 関数の代わりに VOid pceApplnit(void) アプリケーション起動時に 1 回だけ呼び 出されます。ここでアプリケーションで利 内容 \ Ⅳ AVE 再生の停止 アプリケーションの終了要求 ファイルの新規製作 , サイズ変更 ファイルオープン ファイル読み込み ファイル書き込み ファイルクローズ 音楽ライブラリの初期化 音楽演奏の開始 音楽演奏の停止 用する変数や機能の初期化や , 仮想画面の 設定などを行います。 0 VOid pceAppProc(int cnt) アプリケーションの本体をこの関数内に 作ります。この関数はシステムから一定間 隔で呼び出されるので , この関数の中でメ インループを直接回すのではなくて , メイ ンループ 1 回ぶんの処理を行ったら , すみ やかにシステムにリターンしなければなり ません。 cnt には pceAppProc が呼び出され た回数が入ります。 VOid pceAppExit(void) アプリケーションが終了するときの処理 を行います。空の関数で十分なことも多い main 関数から逐次的に処理されるプログ いでしよう。 なるべくそのような事態は避けたほうがよ かかってもとくに問題はないみたいですが ProcPeriod で設定した間隔より長く時間が pceAppProc の処理が重くて , pceAppSet 使って設定します。 は , pceAppInit 内で pceAppSetProcPeriod を されます。 pceAppPrOC が呼び出される間隔 る ) → pceAppExit ( 終了 ) という順番で実行 eAppProc → (pceAPPPrOC が繰り返し呼ばれ 実際に実行されるときは pceAppInit → pc はできません。 のですが , pceAppExit 自体を省略すること ラムと比べると , 処理を細かい単位に分け 凵 6 C MAGMINE
指しているポインタ値 ) がインクリメント されます。この場合 , もとの b [ 2 ] の値がイ ンクリメントされ , b2 の先頭要素を指して いたものが , 第 1 要素を指すようになりま す。これが Fig. 16- ( c ) の状態です。この状 態で q [ 1 ] [ 1 ] とは b2 の第 2 要素になり , 90 は”十 d ″ が表示されます ( 【 c 】・・・ 90 ) 。 ・解説 【 d 】 6.0d ” 【 b 】 + d" また [ 問 8 」 変換指令 % フラグ幅精度サイズ p ⅱ n ばの変換指令の一般形式は , めています。 題と次の問 9 ではそれを紹介する意図も含 彩な出力整形機能を持っています。この問 いる人が多いと思うのですが , 実際には多 普段 , あまり深く考えずに p 血ばを使って Fig. 16 ポインタの配列 (a) List 5 の初期状態 となっています。よく使われる「 % d 」の「 d 」 は「変換指令」です。また , 「 % 06d 」の場合 , 「 0 」はフラグで , 「 6 」が「幅」に相当します。 たとえば , 「 % 10.2f 」という形式の場合 , 「 10 」 が「幅」 , 「 .2 」が「精度」になります。そして 「フラグ」ですが , 「 % 」の後ろに , 数字の「 0 」 , スペース , 符号「 + 」 , 「 # 」を ( 必要ならば複 数並べて ) 記すことで特別な働きを指定で きます。こうしたフラグの中で数字の 0 は , 先に示した「 % 06d 」の例のように「 0 サプレス しない」という意味でよく使われます。し かし , ほかのフラグ文字は使ったことがな いという人も少なくないでしよう。ぜひ資 料や man ページ , help などでフラグの働き を確認してください。 まず【 a 】に対応する出力結果を見ると , ・全体として左詰め ということに気づくはずです。したがって , とくに桁数を指定していないか , 左詰めで 桁数を指定しているかです。次に ・符号はマイナスのときのみ表示され , 正の値の場合はスペースが表示される ということも読み取れます。この働きはス ペースフラグによるものです。これらの条 件を満たすもっとも短い書式文字列として は , 「 " % d " 」が考えられます。 同様に【 b 】の場合 , 正のときにも符号 「 + 」が表示されています。これは「 + 」フラグ の働きによるものなので , 「 " % + d " 」が考え られます。なお , フラグ文字が複数ある場 合 , その順序は関係ないため , 「 " % + d " 」と してもかまいません。 次の【 c 】の場合 , 同様にスペースフラグ を使っているようですが , 3 桁以下の数値 については , 必ず 3 桁で表示されています これより「精度」指定を 3 桁で行っていると 思われます。それらの条件より「 " % .3d " 」と なります。 最後の【 d 】は , 一見すると一般的な 6 桁 の右詰め表示です。唯一普通ではないのは , 値ゼロに対応する表示で数字が何も表示さ れていないことです。このような出力は , 精度をゼロと指定した場合に特有の動作で す。このため , 答えは「 " % 6.0d " 」となります。 [ 問 9 」 ・” 8 .3f ・” 8.3f ” (b) + + q 後の状態 int * int * int * b0 int 10 int 20 int 30 bl i nt 40 int 50 int 60 b2 int 70 int 80 int 90 int * int int b0 int 10 int 20 int 30 bl int 40 int 50 int 60 b2 int 70 int 80 i nt 90 (c) + + q [ 1 ] 後の状態 int * int * i nt b0 int 90 i nt 80 int 70 b2 i nt 60 int 50 i nt 40 bl int 30 int 20 int 10 54 C MAGAZINE 2 2 3
List 3 int て ( vo 土 d ) 土 n し int 印垣 = 0 ー fo て (i = i く elemof(a); 十十 i ) 皿十 = a は ] 十 return 印」 m ー [ 問 4 」 List2 を実行すると Fig. 2 のような出力が得られた。出力結果 の【 a 】 ~ 【 f 】の空欄に当てはまる数字を答えよ。 ( 各 1 点 X6 ) 土北遍 ( vo 土 d ) fo て ( 土 = の i く十十 i) ( pr 土北 f ( 00 は d ] : 宅 d % 土 , foo()); p て in ( ″て物 d ] : 宅 d 跏 % 土 , て ( ) return EXIT—SUCCESS; List Fig. 2 #include く stdio. h 》 #include <stdlib.h> 0 1 ム ^ / 【 a 】 int 響 = 1 の int z = 1 void て ( ) printf に bar—0: printf に r ー 1 : printf に r ー 2 : z & 【 c 】 【 d 】 問 6 List4 を実行した結果が Fig. 4 になるように List4 中の空欄【 a 】 に関数 f00 の適切な仮引数の定義を記入せよ。 ( 5 点 ) int 土 n ( void ) f00 ( て ( て e してれ EXIT—StJCCESS; Li st 1 2 3 4 5 6 #include く 8 セ d 土 0. h> #include く s d け b. > int a [ 2 Ⅱ 3 ] = { ( 1 , void foo( 【 a 】 ) int 土 , for ( 土 = 土く十十土 ) { fo て (j = ゴ < + 十 ) printf に % p [ 幻 [ 引 printf に n ” 【 a 】 【 c 】 3 を } , 2 , { 4 , 5 , [ 問 5 」 List3 を実行すると Fig. 3 のような出力が得られた。出力結果 の【 a 】 ~ 【 d 】の空欄に当てはまる数字を答えよ。 ( 各 1 点 X4 ) int 土 n ( void ) foo(a); return EXIT—SUCCESS; Li st #include <stdio. h> #include <stdlib. h> #define 引 50f ( x ) (sizeof(x)/sizeof(*(x))) 土に a [ ] = { 1 , 2 , 3 , 4 , 5 , int f00 ( void ) 土 n セ a[ ] = ( 1 , 2 , 3 , 4 , 5 , 土北新 int 印 = fo て ( 土 = 土く 50f ( a 十 + 土 ) 日十 = a は ] 十 return gum ・ f00 [ 0 ] : f00 [ 1 ] : List5 を実行すると Fig. 5 のような出力が得られた。出力結果 の【 a 】 ~ 【 c 】の空欄に当てはまる数字を答えよ。 ( 各 1 点 X3 ) イ 4 c MAGAZINE 2002 3
List 5 Fig. 40 直前のフレーム フレームのアライメントボイントのサーチ範囲 RAN G E fR / = ( oa 日 n kMax kMin if (k = = kMin) { / / ループ初回 fRm = fR; km = k; } else { 迂 ( fRm く (R) { 〃これまでよりも大きい値が見つかったら fRm = fR; km = 今回のフレーム 〃確定したオーバーラップ領域の長さ int nLm = (m—nYCount - m—nYBasepos) / / 出力の合成 int nlndex; int oa し fTemp1; float fTemp2; 土 n セ nEnd; nEnd = nN; (nSs 十 S 5 時間軸変更 CTimeScal eUnit : : CTimeScal eUnit( ) m—bFirst = TRUE; ( 日 oat * ) ca Ⅱ oc ( 40000 , sizeof(float) m—pLeftX = ( 日 oat * ) ca Ⅱ oc ( 40000 , sizeof(float) m—pRightX = ( oat * ) ca Ⅱ oc ( 40000 , sizeof(float) m—pLeftY = = ( oat * ) ca Ⅱ OC ( 40000 , sizeof(float) m—nXCount = の m—nYCount = 5000 ー m—nYBasePos = 5000 ー m-fRate = 1 . 0 驫 缸 nlndex = m—nYBasePos 十 nSs 十 km 十 i; if ( 土く nLm) { / / フレーム同士が重なる場所 ( クロスフェード処理 ) fTemp1 = ( 0 ) 土 / (fIoat)nLm; fTemp2 = 1.0f - fTemp1; m—pLeftY[nIndex] = fTemp2 * m—pLeftY[nIndex] 十 fTemp1 * m—pLeftX[i]; m pRightY[nIndex] = fTemp2 * m—pRightY[nIndex] 十 fTemp1 * m—pRightX[i]; } 引 se { / / それ以外 ( 単なる値のコピー ) m-pLeftY[nIndex] = m—pLeftX[i]; m—pRightY[nIndexl = m—pRightX[i]; for (i = 土く nEnd; i 十十 ) { CTimeScaleUnit( ) CTimeSca ー eUnit : free(m-pLeftX); free(m—pRightX); free(m—pLeftY); free(m—pRightY); #define BLOCK-SIZE 2000 #define N (BLOCK-SIZE 十 800 ) #define RANGE 400 void CTimeScaleUnit: :opearate(float* pLeftInput, 日 oa セ * pRightInput' int nInSample, f ー oa し * pLeftOutput, float* pRightOutput, int* nOutSample) int nSa = (float)BLOCK—SIZE / m—fRate; int nSs = BLOCK—SIZE; int nN = N; int kMin - RANGE; int kMax = RANGE; / / 新しいインブットバッフアの連結 memcpy(&m—pLeftX[m—nXCount] , pLeftInput, sizeof (float) * nlnsample); memcpy(&m—pRightX[m—nXCount] , pRightInput' sizeof(float) * nlnsample); m—nXCount 十 = nInSample; int km = while (m—nXCount > nSa & & m—nXCount > (N) { 〃ベストアライメントボイント畑のサーチ int k ー float fR; oat fRm; / / k をずらしながら、その場所を評価していく fo て (k = kMin; k く kMax; k 十十 ) { / / その k でのオーバーラップサイズ int nL = ( m—nYCount - m—nYBasepos ) / / EM-TSM int fR = 0.0 新 for (j = の j くー十十 ) { fR 十 = (sign(m pLeftY[m—nYBasePos 十 nSs 十 k 十 j] ) * Sign(m-pLeftx[ j] ) m—nYCount = m—nYBasePos 十 nSs 十 km 十 nN; m—nYBasePos 十 = nSs ー / / m-pLeftX と m-pRightX を nsa だけ左にずらす memmove(m—pLeftX, &m—pLeftX[nSal , sizeof(float) * m—nXCount); memmove(m—pRightX, &m—pRightX[nSa] , sizeof(float) * m—nxcount); m—nXCount ー = nSa ・ / / 出力サンプル数の計算 int nOutputLength = m—fRate * (float)nlnsample; / / 出力バッフアに結果のコヒ。ー memcpy(pLeftOutput' m—pLeftY, sizeof(float) * nOutputLength); memcpy(pRightOutput, m—pRightY, sizeof (float) * nOutputLength); / / Y を前にずらす = nOutputLength ー m—nYCount = nOutputLength ー m—nYBasePos memmove(m—pLeftY, &-m—pLeftY[nOutputLength] , sizeof(float) * m—nYCount); memmove(m—pRightY, &m—pRightY[nOutputLengthl , sizeof(float) * m—nYCount); *nOutSample = nOutputLength; 第 int CTimeScaleUnit: :Sign(float fValue) if ( fVaI ue > = 0.0f ) { return 1 ー } 引 se ( return -1 ー 十 k); (nSs void CTimeScal eUnit : : SetRate ( oat fRate ) m—fRate = fRate ー 39 特集 1 デジタルフィルタ ソフトウェアによるオーティオテジタル信号処理のレシピ
002 プログラミング 期末試験 [ 問 2 」 「 i 猷型のデータがどれだけのテータサイズ ( ビット幅 ) を持つ か」は処理系に依存するが , そのビット幅の大小に依存するこ となく , int 型のテータ値として特定のヒットパターンを得たい 場合がある。以下のそれぞれの値を得るための表現として , も っともソースファイル上の文字数が少ない表現を答えよ。 ① int 型ですべてのビットが「 1 」であるような値を得る方法 ② int 型で最上位ビット (MSB) だけが「 0 」 , それ以外のビット がすべて「 1 」であるような値を得る方法 ( 各 2 点 X2 ) ~ 出題・解説 : きだあきら 0 すべての問題は , ANSI C に準拠した処理系を前提としてい る。実行結果を問う場合の前提として , ポインタのバイトサイ ズは 4 ( 32 ヒット ) で , int のサイズも 32 ビットであるものとする。 また , 型の宣言を伴う場合 , const や vo ぬ t ⅱ e の修飾子は , 必須 でない限り利用しないものとする。 問 1 ANSI C に関する次の① ~ ⑩の記述に対して , 正しければ〇 , 間違っていれば X をつけなさい。 ① f ( ) + g ( ) のように , 2 項演算子の両側の項は , 特別な 2 項演 算子を除けば , どちらを先に評価するかは定まっていない ②「 i = + + i + 1 ; 」のような , 自分のインクリメント結果を含む式 [ 問 3 」 の値を自分自身へ代入した結果は不定である ③関数呼び出しの実引数は , 右から左へと評価される List 1 を実行すると Fig. 1 のような出力が得られた。出力結果 ④標準ライブラリ関数を利用するときには , 定められた標準 の【 a 】 ~ 【 d 】の空欄に当てはまる数字を答えよ。 ヘッダを必ず #include しなければならない ( 各 1 点 X4 ) ⑤ int のヒット幅は最低でも 1 6 ビットはある ⑥ -11 / 2 を評価すると答えは -5 である ⑦ -10 > > 1 を評価すると答えは -5 である ⑧ ANSI 標準ライブラリで定義されている関数の中には , マク ロで実装されているものもあるが , 必ず関数で実装したも のもライブラリの中には用意されている ⑨ ANSI 標準ライブラリで定義されている関数の中で , マクロ で実装されているものを利用する場合には , 実引数が複数 回評価されるかもしれないので注意が必要である ⑩ const と vo ⅱ e という型修飾子を同じオブジェクト ( 変数 ) に 併用することは可能である ( 各 1 点 X10 ) 編 五ロ ① ② List #include <stdio. h> #include <stdlib.h> int x = 1 の int y = 2 void f00 ( void ) printf に f00 : %d d n 新 void bar ( void ) int t = x; int x = y; int y = t ー p て tf ( ″て : 記洋 d ” , + + x , 十 + y リ void q ( 土北 x, 加セ y) printf ( "qux : %dßd*n" 10 f00 : 10 て : 【 b 】 qux : 【 d 】 【 a 】 int main(void) foo ( て ( return EXIT—SUCCESS; ① ⑥ ③ ⑧ ② ⑦ ⑤ ④ ⑨ 【 c 】 【 d 】 43 特集 2 C ℃ + + / Java 実力チェックプログラミング期末試験
002 プログラミング 期末試験 Fig. 2 O ーエ 【 c 】 【 a 】 【 c 】 【 a 】 【 d 】 【 e 】 【 d 】 【 h 】 Fig. 3 が List6 の実行結果となるように【 a 】 ~ 【 c 】の空欄を正 しく埋めよ。また , 例外が発生する場合は , 最初に例外が発生 List5 を実行すると Fig. 2 のような出力が得られた。出力結果 の【 a 】 ~ い】の空欄を正しく埋めよ。 する場所を明示せよ。 ( 各 2 点 X3 ) ( 各 1 点 X12 ) List / / InitIJ.java class Bage { 土 n し x = 1 int y = 2 p は 0 int getx( ) ( return } public 土 n し getY( ) { return } p 面は 0 string getXY( ) ( return "hello, 響 0 d 第 class Midsub extends Base { int x = 3 の int y = 4 の p は 0 int getY( ) { て e セれ } P は 0 string getXY( ) { return getx( ) 十新 clasg Subsub extends Midsub { int x = 7 の 土 n に y = 8 の p は 0 int getx( ) ( return } List / / NulIRef. java class F00 { public final static int x = 1 public static int y; public static int static { y = 3 ) { z = 5 } 新十 getY( public class NuIIRef { public static void main(string[ ] args) { F00 f = nul 嶹 system. out.println("f. x:n 十 f. x); system. 0uしPて土nt朝にf. y:n 十 f. y); system. out.println("f. z:" 十 f. 2 public class InitIJ ( p 面は 0 8 セ at 土 0 void 遍 ( St て g [ ] ) { Base 土 1 = new Midsub( se 土 2 = new Subsub( ds jl = new ds 面 ( Subsub j2 = new Subsub( x:" 十土 1. x 十 , y:ø十土 1. getY ( ) sygtem. out. println( 新土 1 : 十 % : ' 十土 1. get ( ) ); system. out. println( 第土 2 : x:n 十土 2. x 十 % y:" 十土2. getY() 十 % ′ : ”十土2. getXY()); x:" 十 31. x 十 % y:ø十 }1 ・ getY() sygtem. out. println( 」 1 : 十 % : ' 十 jl. getXY()); x:n 十 j2. x 十” y:" 十ゴ2. getY() System. out. Pて土n凵れ( 十 % : 新十 j2. getXY()); 【 c 】 特集 2 C/C + + /Java 実力チェックプログラミング期末試験
[ 問 1 7 」 List15 を実行した結果が Fig. 12 になるように , List 15 中の空欄 【 a 】に配列変数ね b の宣言を , 空欄【 b 】にポインタ変数 p の宣 言のための適切なコードを記入せよ。ただし , 引数の型情報を ( 2 点 ) 記入するものとし , 配列の要素数はプログラムの動作に必要な 最低限の個数を宣言するものとする。 ( 各 3 点 X2 ) [ 問 1 引 List 13 に示す関数 ba 「中で「 % u 」によって変換表示される sizeof の値がいくつかあるが , この中で 1 つだけほかとは異なる値が ある。異なるものを 0 ~ 4 の番号で答えよ。 List 13 #include く s し d 土 0. h 〉 #include <stdlib. h> List int a [ 2 ] [ 3 Ⅱ 4 void て ( ) sizeof(a) printf ( ”て ー 1 : 宅 u % 8 土 zeof ( & a ) ー 2 : 宅 u n ″ , 8 土 zeof ( [ (I) printf ( ″て p て tf ( ”て一 3 : 宅 u 跏 % zeof ( & a [ 0 Ⅱ (]) p て土 ntf ( ”て一 4 : % 日土 zeof ( & a [ 0 Ⅱ 0 Ⅱ 0 ] ) int 遍 ( vo 土 d ) return EXIT—SUCCESS; #include く虻 dio. い #include <gtdlib. h> / * ARGSUSEDI * / void foo(int x, char * 面 y0 , char *dmyl) printf( ″ f00 : %d*n", x); / * ARGSUSED2 * / void bar(int x, char * 町 char *dmyl) p て土 ntf ( ″て : 宅 8 : 宿 d 蕚 % 8 , void 名 ( 土 nt x, char * 8 , char *t) s : 8 : 宅 d 蕚れ新 / * 配列 t 曲の宣言 * / f00 , baz, [ 問 16 List 1 4 を実行すると Fig. 1 1 のような出力が得られた。出力結 果の【 a 】 ~ 【 d 】の空欄に当てはまる数字を答えよ。 ( 各 1 点 X4 ) int 加 ( ) 土れセ ch 矼 * 8 [ ] = { も L , "howdy", "hello", c て *t[l = { NULL, L nworld", / * ポインタ p の宣言 * / 【 b 】 void (**p)(int, char * , char * p = table; fo て ( 土 = *p NULL; 十十土 ) return EXIT—SUCCESS; List Fig. 11 #incl ude «gtdio .h> #include <gtdlib. h> a 】 ma1n ・ f00 : 【 b 】 c 】 d 】 ZO セ int at101; void foo(int a [ 20 ] ) p てれセ f に f00 :%u*n", Bizeof(a) void て ( ) extern int a [ 10 p て土 ntf ( ”て : 宅 u % gizeof(a) Fig. 12 f00 : 0 bar: howdy: 1 baz : he Ⅱ 0 : wo d : 2 void 20t ( ) int a [ 10 p て in セ f げ要 0 し :%u%nn, 日土 zeof ( a ) int main(void) セ at201; 土北 f に土 n : % 28f ( a ) f00 ( a リ て ( zot( て e して n EXIT—SUCCESS; 【 c 】 イ 8 C MAGAZINE 2 側 2 3
値の変更が不可能なことを指定します。値 を変更することができなくなるため , 初期 値を伴って宣言されます。以下のように宣 言します。 const int x = 123 ー int const x = 12 あるいは , const と型指定の int はどちらを先に指定 しても同じです。このように宣言すると , 以下のようなプログラム内での代人文は , コンパイルエラーとなります。 x = 45 const 宣言とポインタ変数 const キーワードはポインタ変数に指定 することも可能ですが , 指定方法により Fi g. 7 に示す 2 通りの意味があります。 Fig. 7- ①では const キーワードが最初に指 定されています。この指定方法ではポイン タそのものに変数のアドレスを代入するこ とは可能ですが , そのポイント先の値を変 更することができません。 それに対して Fig. 7- ②では , const キーワ ードが * の後に指定されています。この指 定方法ではポインタ変数にほかの変数のア ドレスを設定し直すことができなくなりま す。ただしポインタが指し示す先の値は変 更することが可能です。 以上をまとめてみると , ポインタ変数に 対する const キーワードの指定方法は , 以 下に示す 3 種類の方法があります。 ( 1 )const int * pi ー (2)int const * pi; (3)int * const pi; Fig. 7 ポインタ変数に対する const 指定方法 ①ポインタの指し示す先の値が変更不可 ( 1 ) と②は同じ意味になります。 Fig. 7 の①と②の違い ( ポインタそのものが変更 不可か , あるいはポインタの指し示す先が 変更不可か ) は , const キーワードが * の前 に指定されるか , 後に指定されるかにより ます。また Fig. 7 の① , ②を同時に指定し て , const int * const pi = &i; のように宣言することも可能です。この場 合 , ポインタ変数そのものも , ポインタの 指し示す先も両方変更することができませ ん。 const 宣言と関数の引数 さらに const キーワードは関数の引数に も指定することができます。 2 つの文字列 を比較する標準関数 strcmp の正式な関数プ ロトタイプを例に見てみましよう。 土 n し strcmp ( const char * stringl , const char * string2 これは stringl および string2 が , 関数 strc mp 内では変更されないという意味になり ます。 この例を見た限りではそれほどの効果は ないように思われるかもしれませんが , co nst キーワードがない場合の例も見てみま しよう。文字列をコビーする標準関数 strc py のプロトタイプは以下のとおりです。 char * st て cpy( char * stringl , const char * string2 第 1 引数 stringl は第 2 引数がコピーされ るため , const キーワードが指定されてい ません。つまり , const キーワードが省略 int i = 1 0 : const int * pi; * pi = 10. / * const なポインタを宣言 * / / * コンバイルエラー * / ②ポインタそのものが変更不可 int i = 10. j = 20; int * const pi = &i; / * const なポインタを宣言 * / された場合 , コンパイラはその関数内で値 が変更されるとみなします。そのため以下 の例は Warning ( 警告 ) が出力されます。 const char * str ー strcpy(str, nabcd" 以下のような場合は , myfunc の呼び出 myfunc ( s して const char * s て一 / * プロトタイプ宣言 * / myfunc ( char * str しで warning ( 警告 ) が出力されます。 おっくう ならない変数を誤って変更してしまうとい 大規模なプロジェクトでは , 変更しては したほうがよいことはいうまでもありませ 当はめんどうでもすべてをきっちりと指定 ードを削除するといったこともあります ( 本 となる場合もあり , すべての const キーワ す。中規模のプログラムではかえって億劫 ーワードを指定しなければならなくなりま して , 変更しない仮引数は厳密に const キ ると , プロジェクト内のすべての関数に対 このように const キーワードは 1 つ指定す しなければなりません。 c2 の仮引数宣言に c 。 nst キーワードを追加 出しで同様の警告が発生するため , myfun たとします。すると今度は my 血 nc2 の呼び 数 str をさらに内部関数 my 血 nc2 に渡してい し , たとえばこのとき myfunc 内では仮引 ーワードを追加することになります。しか ②の場合は関数の仮引数宣言で c 。 nst キ で const キーワードの指定を忘れた 行わないが , myfunc の仮引数の宣 ( 2 ) myfunc では仮引数 st 「に対する変更は り誤りを防ぐことができた ったが , const キーワードの機能によ 変更する関数の引数に指定してしま ( 1 ) 変更してはならない変数を , 誤って て , 以下の 2 つが考えられます。 しています。この誤りが発生した原因とし 能性があるということをコンパイラが警告 れた ) 変数が , m 可 unc により変更される可 これは変更してはいけない ( const 指定さ / * wa て n 土 ng が出力される * / 90 * pi = 1 0 : pi = &j; C MAGAZINE 2002 3 / * コンバイルエラー * /
002 プログラミング 期末試験 0 0 0 0 0 0 1 C) 8 《 1 一 8 1 8 8 8 フ / 8 8 8 「 / 8 8 ( 0 ()D ()D (D C.D C-D ( 0 8 フ′ ( 0 5 ) 【 0 LO LO 一 -0 5 ) ロ 0 ロ 0 ( 0 ( 0 広 ) っ CO っ ") 00 00 っ 0 っ 0 っ 0 ロ ) (C) 4 1 つな CN っ乙っ 0 0 っ 0 肢 キアイウェオカキクケコサ [ 問 4 」 List3 のようなクラス A と B , main 関数が定義されている。 のプログラムについて , 以下の問いに答えよ。 ① List 3 の空欄【 a 】に予約語 pub ⅱ c が入るとき , 1 ~ 10 のコメン トをつけたいくつかの行でエラーとなる。工ラーとなるすべ ての組み合わせを以下の選択肢 ( ア ) ~ ( サ ) の中から選択せよ。 ( 3 点 ) ② List 3 の空欄【 a 】に予約語 pro cted が入るとき , 1 ~ 10 のコメ ントをつけたいくつかの行でエラーとなる。工ラーとなるす べての組み合わせを選択肢 ( ア ) ~ ( サ ) の中から選択せよ。 ( 3 点 ) 「問 5 」 List 4 を実行するとどのように表示されるか。以下の選択肢 の ( ア ) ~ ( 工 ) から正しいものを選んで回答せよ。 List 選択肢 0 ね 88 A { void f0 ( ) 0 public : void fl( ) 0 protected void f2( ) 0 private : void f3 ( ) 0 ( ア ) 5 , 5 , 7 , 8 ( イ ) 6 , 6 , 7 , 8 ( ウ ) 6 , 7 , 7 , 8 ( 工 ) 6 , 7 , 8 , 9 class B void g0(){} public ・ void gl(){ fl ( protected void g2(){ f2() private : void g3(){ f3 ( } ( 4 点 ) List int main() { A a; B b; a. f0 ( a. fl ( a. f2 ( b. fl ( b. f2 ( b. gl ( b. g2 ( return 0 ー #include <iostream> using namespace std; clasg A { static 土 n セ 土 n セ public : int geti( ) { return } void seti( int 土 ) ( A : : 土 = } int getj( ) { return } void se 切 ( int ゴ ) { this->j = 8 旦 0 void f ( ) ( 土 + ) void 9 ( ) ( 十 } / / 10 int A::i = 特集 2 c ℃ + + / Java 実力チェックプログラミング期末試験 59
ったことが多々発生します。実際の業務で このようなプロジェクトに参加する場合は , 「転ばぬ先の杖」と思って多少めんどうでも しつかりと const キーワードを指定するよ うにしましよう。 vo ⅱ e キーワード volatile キーワードも const キーワードと 同様に型修飾子で , 直訳すると「揮発性」と いう意味があります。 MSDN のヘルプに は「プログラム中で変更可能なオプジェク トであることを宣言する」とありますが これだけでは何のことかよくわかりません。 List 2 を見てください。 4 行目でグローバル変数 x ( 1 行目で定義 ) に 0 を代入した後に , 5 行目の while ループ で変数 x が 0 以外の値になるまで何らかの 処理を繰り返します。しかし , このループ 内では変数 x の値を変更していないので , 無限ループに陥ることになります。コンパ イラも同様に考え , 6 行目の判定がムダで あると認識します。その結果 , 6 行目の判 定処理に関するオプジェクトコードの作成 が省略され , 無限ループにしかならないコ ードが生成されます ( このあたりの最適化 のレベルは処理系に依存しますが , 事実Ⅵ sual C + + 6.0 では最大限の最適化を行うオプ ション ( / Ox ) を指定すると 6 行目の判定部 分のコードが作成されなくなりました ) 。 しかし現実にはこの変数 x の値が変化す る要因をいくつかあげることができます。 マルチスレッドと呼ばれる 2 つ以上の関数 が並行して動作する機能を利用して , exte rn 宣言されたグローバル変数 x が更新され るかもしれません。また組み込み系のシス テムでは , 特殊なハードウェアを利用して 割り込みハンドラなどにより変更される場 合も考えられます。 このように未知の方法で更新される可 能性のある変数に対して , 評価を省略せず に正しく対応させるための指定が vo 川 e キーワードです。 List2 の 1 行目を , volatile int x; と定義することによって , 最大限の最適化 を指定しても 6 行目の処理が省略されなく なります。 volatile ( 揮発性 ) 変数とは , のように処理系に未知の方法で値がリフレ ッシュ ( 変更 ) されることを意味するようで す。このような考え方は , 事務処理系の開 発ではあまり必要ないかもしれませんが 組み込み系の開発では重要な概念となりま す。 ポインタ変数に対する volatile キーワー ドの指定方法は , const キーワードと同様 で以下のとおりです。 にならないように注意しなければなりませ ことです。このような処理では無限ループ 再帰呼び出しとは , 自分自身を呼び出す 再帰呼び出し ・・ポインタそのものが vo ⅱ e (3)int * vo ねセ e pi; (2)int volatile * pi; ・・・ ( 1 ) と同等 ・・・ pi の指し示す先が vo は t ⅱ e ( 1 ) vo ねし i 尾土 n セ *pi; 6 ロロロロ ん。簡単な再帰呼び出しの例 ( 1 から n まで の総和を求めるプログラム ) を List3 に示し ます。 List 3 では 14 行目から始まる関数 souwa 内の 17 行目で , 再び自分自身である souwa を呼び出しています。関数 souwa の考え方 としては以下のようになります。 ・ 1 7 行目・・・関数 so uwa は引数で指定し た数までの総和を求めてくれるのだか ら , n -1 までの総和を関数 souwa によ り求め , それに n を加えた数を戻す ・ 1 6 行目・・・ 1 から 1 までの総和は 1 なの で , 引数 n が 1 のときは戻り値として 1 を 戻す 本来 1 から n までの総和は , ループによ り求めることが可能です。一般的にループ 処理はすべて再帰呼び出しで表現すること が可能だといわれています。とくに階層デ ィレクトリ配下のすべてのファイルを走査 するような処理は , 再帰呼び出しが必要に なります。しかし , 入門レベルのプログラ マには混乱の元になりかねません。現在の 時点では , このような処理が可能だという ことを覚えておいてください。 では , 今回の内容を Fig. 8 にまとめてお きます。 本連載を振り返って 日本においてここ数年 , 急激な勢いでパ ソコンが各家庭にも普及したと思います。 しかし , おそらくその大半がインターネッ 1 : 3 : 4 : 5 : 8 : Li st 無限ループになる例 int x ー 2 : void myfunc( ) { int 土 = x = の while(l) ( if(x ! = 0 ) break; 土 + / * 任意の処理 * / 1 : 2 : 5 : 6 : 8 : 9 : 10 : 11 : 12 : } 13 : 14 : 16 : 17 : 18 : } ist 3 : void main ( ) int souwa(int n); #include く stdio . h> 簡単な再帰呼び出し return n 十 souwa(n -1 if(n = = 1 ) return int souwa(int n) p て intf ( ” 1 から記までの総和はです . ” goukei =souwa(n); scanf( ” d ” , &n); p て土 ntf ( ”整数を入力してください = っ” int n, goukei; n, goukei) ・ Getlnto C World ! ! 91