『Cプログラミング診断室』目次次(第6章 不慣れ その他)

第6章 不慣れ

類似パターン


今回のプログラムの特徴の1つは、類似パターンがいっぱい出てくることです。類似パターンは、 エディタでコピーし、ちょっと変更するとできるので、とりあえずの作成時間は短く、リストもど んどん長くなるので、作業が進んでいるように感じるでしょうが、後で「泣き」をみます。

■字下げ■

リスト6−11は、長ーいswitch文中の一部です。最初のcase中で、ifブロックの中が全然字下 げされていません。1行80字にこだわったため、自然な字下げを行うと、収まりがつかなくなって 字下げを止めたのではと思われます。こういうときは、半分の4文字分の字下げでその場を取り繕 うことができます。でも、問題は、1行80字なんてことをしているからです。

 
リスト6−11 深いcase

     1	                case PANEL_CHOICE_STRINGS:
     2	                        if ( item->win_type == MY_PANEL_CHOICE ) {
     3	                        len = -1; size = 10;
     4	                        while ( (ctemp = va_arg( ap, char* )) != 0 ) {
     5	                                len ++;
     6	                                if( len == 0 ) {
     7	                                        pointer->panel_choice_strings =
     8	                                                (char**) malloc ( size
     9	                                                        * sizeof(char*) );
    10	                                }
    11	                                if( len+2 > size ) {
    12	                                        size = size * 2;
    13	                                        old = pointer->panel_choice_strings;
    14	                                        pointer->panel_choice_strings =
    15	                                                (char**) realloc( old,size
    16	                                                            * sizeof(char*) );
    17	                                }
    18	                                *((pointer->panel_choice_strings)+len) = (char*)
    19	                                    malloc( (strlen(ctemp)+1)*sizeof(char) );
    20	                                strcpy( *((pointer->panel_choice_strings)+len),
    21	                                                ctemp );
    22	                        }
    23	                        len++; *((pointer->panel_choice_strings)+len) = NULL;
    24	                        item->attr = (caddr_t) pointer;
    25	                        }
    26	                        else
    27	                                while ( va_arg( ap, char* ) != 0 ) ;
    28	                        break;
    29	                case PANEL_CHOICE_FONTS:
    30	                        if( item->win_type == MY_PANEL_CHOICE ) {
    31	                                len = -1; size = 10;
    32	                                while ( ftemp = va_arg( ap, myPixfont* ) ) {
    33	                                        len++;
    34	                                        if( len == 0 )
    35	                                                pointer->panel_choice_fonts =
    36	                                                        (myPixfont**) malloc(
    37	                                                        size*sizeof(myPixfont*));
    38	                                        if( len + 2 > size ) {
    39	                                                size *= 2;
    40	                                                oldf = pointer->
    41	                                                        panel_choice_fonts;
    42	                                                pointer->panel_choice_fonts = 
    43	                                                        (myPixfont**) realloc(
    44	                                                        oldf, size*sizeof(
    45	                                                                myPixfont*));
    46	                                        }
    47	                                        *(pointer->panel_choice_fonts+len) = 
    48	                                                ftemp;
    49	                                }
    50	                                len++;
    51	                                *(pointer->panel_choice_fonts+len) = NULL;
    52	                        }
    53	                        else
    54	                                while ( va_arg( ap, myPixfont* ) != NULL );
    55	                        break;
    56	                case PANEL_TOGGLE_VALUE:
    57	                        pointer = (myPanel_Item_Choice*) item->attr;
    58	                        if( pointer->panel_toggle_val == NULL ) {
    59	                                pointer->toggle_size = 5;
    60	                                pointer->panel_toggle_val = (int*)
    61	                                        malloc( pointer->toggle_size*
    62	                                                                sizeof(int) );
    63	                                pointer->toggle_index = (int*)
    64	                                        malloc( pointer->toggle_size*
    65	                                                                sizeof(int) );
    66	                                pointer->toggle_count = 0;
    67	                        }
    68	                        else if ( pointer->toggle_count >=
    69	                                                pointer->toggle_size ) {
    70	                                pointer->toggle_size += pointer->toggle_size;
    71	                                itempa = pointer->panel_toggle_val;
    72	                                pointer->panel_toggle_val = (int*) realloc(
    73	                                     itempa, pointer->toggle_size*sizeof(int) );
    74	                                itempa = pointer->toggle_index;
    75	                                pointer->toggle_index = (int*) realloc( itempa,
    76	                                        pointer->toggle_size*sizeof(int) );
    77	                        }
    78	                        itemp = va_arg( ap, int );
    79	                        *(pointer->toggle_index + pointer->toggle_count) =
    80	                                                                        itemp;
    81	                        itemp = va_arg( ap, int );
    82	                        *(pointer->panel_toggle_val + pointer->toggle_count) =
    83	                                                                        itemp;
    84	                        pointer->toggle_count++;
    85	                        break;

どんどん字下げのネストが深くなってどうしようもないプログラムを見ることがあります。必要 以上にネストが深くなるのは、if文による枝分かれで、特にエラーチェックのときに深くなるよう です。

深くならない「こつ」の一つが、if文でエラーの場合を先に処理することです。エラー処理の最 後にbreakなどを置くことで流れが途切れるので、正常時の処理は字下げが不要になります。本来の 目的の処理の前で、エラーチェックがすべて行われると、以降は正常処理だけに専念すればよいの で、楽にもなります。修正版のリスト6−13では、異常なときの可変引数の読み飛ばしをしてい ます。

■malloc, realloc■

reallcのとき、第1引数のポインタは、必ず直前で別の変数oldに代入してから使っています。 これは、reallocの引数と、reallocの戻り値を入れるポインタが同一だと、何かが破壊されてしま うと思いこんでいるようです。これは、FORTRANをやっていたため、引数はアドレス渡しが身に染 み込んでしまっているからでしょう。Cの引数は、すべて値渡しです。アドレスを渡したいときは、 &をつけて本当にアドレスを渡すのです。アドレスという値を渡すので、やっぱり値渡しなのです。 なぜなら、この場合も値を返すには、引数の前に*をわざわざつけて、アドレスという値の指す場 所に代入させるからです。

3つのcaseの中をながめると、データを入れる領域を確保するためにmalloc、確保した領域内に 収まらなくなったときにreallocを使っていますが、3つとも似た使い方をしているので、共通化 が考えられます。最初の2つのcase中のif文で細工をしている領域はそれぞれ1つですが、最後の は、panel_toggle_valとtoggle_indexが指す2つの領域があります。これらをまとめて面倒をみよ うとして書いた関数がリスト6−12のunit_allocです。ここでは、領域がまったく確保されてい ないことの判定をcount==0で行っています。reallocでは、同じポインタを2回使いますが、ポイ ンタの記述が長くなってくると、プログラムがごちゃごちゃしてきます。そのため、ポインタのア ドレスを渡しています。領域のサイズは、chgsizeというフラグを設けて、真のときだけ変更され るようになっているので、複数の領域を確保するときは、最後だけTRUEで呼び出しています。もち ろん、1つしか領域を確保しないときもTRUEで呼び出します。

元の3つのreallocのときのif文の判定は同一ではありませんが、ゆとりのある方を採用するこ とで共通化しています。領域がデータ1個分余分になりますが、大したことではありません。プロ グラムがすっきりする方が優先されるべきですね。

 
リスト6−12 共通関数

     1	/********************************************************************************/
     2	/*      文字列を入れる領域を確保し、文字列をコピーする                          */
     3	/********************************************************************************/
     4	char    *alloc_strcpy( str )
     5	IN      char    *str;
     6	{
     7	        char    *ptr;
     8	
     9	        ptr = malloc( strlen(str) + 1 );
    10	        strcpy( ptr, str );
    11	        return  ptr;
    12	}
    13	
    14	/********************************************************************************/
    15	/*      1ユニット分の領域を追加確保する                                        */
    16	/********************************************************************************/
    17	void    unit_alloc( pptr, chgsize, size, count, initcount, unitcount )
    18	INOUT   void    **pptr;         /* allocした領域                */
    19	IN      int     chgsize;        /* sizeの値は変更されるか       */
    20	INOUT   int     *size;          /* 領域の大きさ                 */
    21	IN      int     count;          /* 領域内の実データ個数          */
    22	IN      int     initcount;      /* 初期allocのデータ個数 */
    23	IN      int     unitsize;       /* データ1個分のバイトサイズ   */
    24	{
    25	        int     n = 0;
    26	
    27	        if( count == 0 ) {
    28	                n = initcount;
    29	                *pptr = malloc( n * unitsize );
    30	        } else if( count+2 > *size ) {
    31	                n = *size * 2;
    32	                *pptr = realloc( *ptr, n * unitsize );
    33	        }
    34	        if( chgsize && n>0 )
    35	                *size = n;
    36	}

文字列を入れるためにぴったりのサイズの領域をmallocで確保し、その中に文字列をコピーする ことは、プログラム全体のあちこちで行われていました。簡単なことですが、3つもの関数 strlen, malloc, strcp を使っているので、長い同じ変数が2回現れても嫌になります。これを行 う関数を、リスト6−12にalloc_strcpyとして入れています。単純ですが、多数出現しているし、 関数にしてしまうと、ずいぶんすっきりしてきます。

2つの関数を作ったことで、ぐちゃぐちゃしていたものが、リスト6−13のように、短く、すっ きりしました。

sizeof(char)がありますが、これは1に決まっているので、省略する方がすっきりします。

 
リスト6−13 浅いcase

     1	                case PANEL_CHOICE_STRINGS:
     2	                        if ( item->win_type != MY_PANEL_CHOICE ) {
     3	                                while ( va_arg( ap, char* ) != 0 );
     4	                                break;
     5	                        }
     6	                        for( size=10,len=0; ctemp=va_arg( ap, char* ); len++ ) {
     7	                                my_alloc( &pointer->panel_choice_strings, TRUE, &size,
     8	                                                len, 10, sizeof(char*) );
     9	                                pointer->panel_choice_strings[len] = alloc_strcpy(ctemp);
    10	                        }
    11	                        pointer->panel_choice_strings[len] = NULL;
    12	                        item->attr = (caddr_t) pointer;
    13	                        break;
    14	                case PANEL_CHOICE_FONTS:
    15	                        if( item->win_type != MY_PANEL_CHOICE ) {
    16	                                while ( va_arg( ap, myPixfont* ) != NULL );
    17	                                break;
    18	                        }
    19	                        for( len=0; ftemp=va_arg( ap, myPixfont* ); len++ ) {
    20	                                unit_alloc( &pointer->panel_choice_fonts, TRUE, &size,
    21	                                                len, 10, sizeof(myPixfont*) );
    22	                                pointer->panel_choice_fonts[len] = ftemp;
    23	                        }
    24	                        pointer->panel_choice_fonts[len] = NULL;
    25	                        break;
    26	                case PANEL_TOGGLE_VALUE:
    27	                        pointer = (myPanel_Item_Choice*) item->attr;
    28	                        initsize = 5;
    29	
    30	                        unit_alloc( &pointer->panel_toggle_val, FALSE, &pointer->toggle_size,
    31	                                    pointer->toggle_count, initsize, sizeof(int) );
    32	                        pointer->toggle_index[pointer->toggle_count] = va_arg( ap, int );
    33	
    34	                        unit_alloc( &pointer->toggle_index, TRUE, &pointer->toggle_size,
    35	                                    pointer->toggle_count, initsize, sizeof(int) );
    36	                        pointer->panel_toggle_val[pointer->toggle_count] = va_arg( ap, int );
    37	
    38	                        pointer->toggle_count++;
    39	                        break;


Copyright1996 Hirofumi Fujiwara. No reproduction or republication without written permission
『Cプログラミング診断室』目次次(第6章 不慣れ その他)