C言語関係掲示板

過去ログ

No.1015 グローバル変数を使うことについて

[戻る] [ホームページ]
No.1263

グローバル変数を使うことについて
投稿者---KAZ(2004/02/06 20:49:20)


また、お世話になります。よろしくおねがいします。最近、プログラムを書いていて
よく思うのですが、私はすごくグローバル変数をつかうような気がするのです。
グローバル変数を使うと簡単に行ける場合が多いからだと思うのですが、このような
コーディングスタイルはよくないのでしょうか?よくWEBをみているとグローバル変数
を多用するのはよくないみたいなことが書かれているのですが、理由がわかりません。
すごく便利だと思うのですが。。多分私がまだビギナーなのでその理由も見えないのだと
思うのですが、わかるかた御意見をください。
あと、以下にちょっとしたプログラムを示します。quitと入力されるまで入力された文字を
ソートして出力するものなんですが、今回二つのファイルに書いてみたところ
このグローバル変数のことを考えました。このメインで使っているstore_into_list
という関数に入力されていく文字をわたしていっているのですが、
わたされるたびにこの関数がよばれるためにstore_into_list()のなかで構造体の配列を
宣言することができませんでした。このような場合に構造体の配列をグローバルにせずに
受け取った文字をいれていくことはできるのでしょうか?

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
//メインファイルです。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void show_result(int counter);
void store_into_list(char *str,int counter);

int main(void)
{
  char str[100];
  int quitting=0;
  int counter=0;

  while(! quitting){
    scanf("%s",str);
   
    if(strcmp(str,"quit")==0){
      quitting =1;
    }
    else{
      store_into_list(str,counter);
      counter++;
    } 
  }

  show_result(counter);

  return 0;
}
ーーーーーーーーーーーーーーーーーーーーーーーーーー
//オブジェクトファイルです。
struct Word{
  char word[100];
};

int num=0;

struct Word words[100];

int cmp(const void *x, const void *y)
{
  return strcmp(((struct Word *)x)->word, ((struct Word *)y)->word);
}


void store_into_list(char *str,int counter)
{ 
 
  strcpy(words[counter].word,str);
}

void show_result(int counter)
{
  int i; 

  printf("\nBefore sorted:\n");
  for(i=0; i<counter; i++){
  printf("%s\n",words[i].word);
  }

  qsort(words,counter,sizeof(struct Word),cmp);
  printf("\nsorted:\n");
  for(i=0; i<counter; i++){
    printf("%s\n",words[i].word);
  }
}
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー



No.1265

Re:グローバル変数を使うことについて
投稿者---YuO(2004/02/06 21:47:21)


>また、お世話になります。よろしくおねがいします。最近、プログラムを書いていて
>よく思うのですが、私はすごくグローバル変数をつかうような気がするのです。
>グローバル変数を使うと簡単に行ける場合が多いからだと思うのですが、このような
>コーディングスタイルはよくないのでしょうか?よくWEBをみているとグローバル変数
>を多用するのはよくないみたいなことが書かれているのですが、理由がわかりません。

プログラムが大きくなったときに管理しきれなくなるのが一番の問題です。
その変数がどこでどのように使用されているのか管理しきれるのであれば,
大域変数を使ってもよいですが,そうでないならば使わない方がよいです。

あと,名前の衝突,という問題もあります。
これは関数でも起きますけどね……。
#外から使われない名前にはstaticを付けるべし。


C MAGAZINE 2000年4月号に特集されたプログラミングの禁じ手 C言語版にある,グローバル変数が多い引数を使わずにグローバル変数を使うの項も参考になると思います。


>このグローバル変数のことを考えました。このメインで使っているstore_into_list
>という関数に入力されていく文字をわたしていっているのですが、
>わたされるたびにこの関数がよばれるためにstore_into_list()のなかで構造体の配列を
>宣言することができませんでした。このような場合に構造体の配列をグローバルにせずに
>受け取った文字をいれていくことはできるのでしょうか?

関数の仕様を変更するとよいです。

wordsという配列は,元々main関数が持っているはずのものを,
他のファイルに隠している状態に過ぎません。


No.1277

グローバル変数を使うことについて
投稿者---KAZ(2004/02/08 00:40:31)


YUOさん、アドバイスありがとうございます。グローバル変数を多用す
ることがあまりよくないことが少しわかってきました。ですが、どうしても
このプログラムでグローバルで宣言せずに文字列をわたす方法がわかりませ
ん。いろいろやってみたのですが、アイディアがわきません。どのように関
数の仕様をかえるといいのでしょうか?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void show_result(int counter);
void store_into_list(char *str,int counter);

int main(void)
{
  char str[100];
  int quitting=0;
  int counter=0;

  while(! quitting){
    scanf("%s",str);
   
    if(strcmp(str,"quit")==0){
      quitting =1;
    }
    else{
      store_into_list(str,counter);
      counter++;
    } 
  }

  show_result(counter);

  return 0;
}

//オブジェクトファイル

struct Word{
  char word[100];
};

int num=0;

struct Word words[100];

int cmp(const void *x, const void *y)
{
  return strcmp(((struct Word *)x)->word, ((struct Word *)y)->word);
}


void store_into_list(char *str,int counter)
{ 
 
  strcpy(words[counter].word,str);

}

void show_result(int counter)
{
  int i; 

  printf("\nBefore sorted:\n");
  for(i=0; i<counter; i++){
  printf("%s\n",words[i].word);
  }

  qsort(words,counter,sizeof(struct Word),cmp);
  printf("\nsorted:\n");
  for(i=0; i<counter; i++){
    printf("%s\n",words[i].word);
  }
  

}



No.1278

Re:グローバル変数を使うことについて
投稿者---iijima(2004/02/08 01:32:36)


横から失礼します.
関数に,操作の対象とするデータも引数として渡すようにすれば良いです.
ご参考までに,単純化したサンプルを示します.

// word.h

struct Word
{
    char word[100];
};

void show( struct Word* words, int n );

// word.c
#include "word.h"
#include <stdio.h>

void show( struct Word* words, int n )
{
    int i;
    for( i = 0; i < n; ++i )
        printf( "%s\n", words[i].word );
}

// main.c
#include "word.h"
#include <stdio.h>
#include <string.h>

int main()
{
    struct Word words[100];
    char str[100];
    int counter=0;

    while( 1 )
    {
        scanf( "%s", str );   
        if( strcmp( str, "quit" ) == 0 )
            break;
        else
            strcpy( words[counter++].word, str );
    }
    show( words, counter );

    return 0;
}


No.1281

構造体をincludeすることについて
投稿者---KAZ(2004/02/08 14:27:07)


iijimaさんお返事ありがとうございます。iijimaさんに示して頂いたプログラムですが、構造体をヘッダファイルにいれてますよね。構造体をヘッダファイルに書くことはいいことなんでしょうか?どっかのWEBで構造体はヘッダファイルに書かないほうがいいみたいなことをみたもので。。よろしかったらその辺のところ聞かせてください。あと、やはりこのようにっ構造体をメインにincludeする以外方法はないのでしょうか?includeせずにできる方法があったら教えてください。

No.1282

Re:構造体をincludeすることについて
投稿者---おでん(2004/02/08 15:03:00)


>iijimaさんお返事ありがとうございます。>iijimaさんに示して頂いたプログラムですが、構造体をヘッダファ>イルにいれてますよね。構造体をヘッダファ>イルに書くことはいいことなんでしょうか?どっかのWEBで構造体はヘッダファ>イルに書かないほうがいいみたいなことをみたもので。。

構造体の型定義ではなく、構造体の実体宣言の事では?

>よろしかったらその辺のところ聞かせてください。あと、やはりこのようにっ
>構造体をメインにincludeする以外方法はないのでしょうか?
>includeせずにできる方法があったら教えてください。

構造体の型を他のコンパイル単位(ファイル)に知らせる方法は、
ヘッダファイルに定義を書いて、該当構造体を使用するファイルで
#includeするか、同じ構造体を他ファイルにも宣言をするしかないの
ですが、ファイルごとに宣言していたのでは、一箇所を修正すると、
同じ構造体を宣言している全てのファイルを修正しなくてはなりません。
効率も悪いし、バグの温床にもなります。

・・といった事で、構造体定義にしてもプリプロセッサ命令(#define等)
にしてもヘッダファイルで定義するのが一番いいことだと思います。

・・・小さなプログラムでは、さしたる問題ではない事が規模が大きく
なると収拾がつかなくなります。
50とか100とかの構造体がある場合の事を考えれば分かります。


No.1284

Re:構造体をincludeすることについて
投稿者---iijima(2004/02/08 17:41:59)


> 構造体をヘッダファイルに書くことはいいことなんでしょうか?
> どっかのWEBで構造体はヘッダファイルに書かないほうがいいみたいなことをみたもので。。

おでんさんがおっしゃっていることと同じことですが、複数のモジュールで同じ型の構造体を使うならば、ヘッダファイル(*.hファイル)で構造体を宣言し、それぞれのモジュールの定義ファイル(*.cファイル)でそのヘッダファイルをインクルードするのが普通です。
それを良くないとする理由は見当たりません。
そのウェブサイトで、どのような理由で何をヘッダファイルに書かない方が良いと言っているのか、もう一度調べてみてください。
まともなサイトならば、ヘッダファイルで構造体を宣言しない方が良いなどとはしないはずですよ。

前回書いたサンプルの補足:
実用的には、ヘッダファイルに書いたものが多重定義されることを防止しておくことが妥当です。

// word.h
#ifndef _WORD_H_
#define _WORD_H_

struct Word
{
    char word[100];
};

void show( struct Word* words, int n );

#endif


No.1286

Re:構造体をincludeすることについて
投稿者---YuO(2004/02/08 19:03:10)


>> 構造体をヘッダファイルに書くことはいいことなんでしょうか?
>> どっかのWEBで構造体はヘッダファイルに書かないほうがいいみたいなことをみたもので。。

通常ヘッダファイルに書くのは,
・前処理指令
・定義でない宣言
です。

なので,1278でiijimaさんが書いているコードは通常のコードです。


ただし,1284での補足は残念ながらそのまま適用してはいけません。

>#ifndef _WORD_H_
>#define _WORD_H_

これを書いた途端,このヘッダファイルを取り込んだプログラムの動作は未定義になります。
下線+英大文字で始まる識別子は,いかなる使用に対しても予約されていますから。

結論として,下線から始まる識別子は利用しないのが安全です。
#標準の規定はもう少し緩いが。


No.1287

Re:構造体をincludeすることについて
投稿者---iijima(2004/02/08 20:14:01)


>結論として,下線から始まる識別子は利用しないのが安全です。

C++を勉強し始めた頃に読んだ参考書(某IDEに附属していた「C++入門簡易プログラミングガイド」)に、ヘッダファイルの重複防止のサンプルとして"_XXX_H_"の書き方が載っていたのが気に入って、これまでずっと踏襲してきました。
改めて調べましたが、確かに、先頭がアンダースコアになっている名前は定義しないようにすべきだとされていますね(「プログラミング言語C++第3版」p.116)。
いままで実害がなかったのは運が良かっただけなのですね。
勉強不足を恥じております。
ご指摘ありがとうございました。> YuOさん

No.1288

Re:構造体をincludeすることについて
投稿者---かずま(2004/02/09 01:21:29)


> 構造体をヘッダファイルに書くことはいいことなんでしょうか?

構造体をヘッダファイルに書くことは、ごく当たり前のプログラミングです。
いいも悪いもありません。実際、<stdio.h> には FILE構造体が、<time.h> に
は struct tm 構造体が書かれていて、関数の引数として使われています。


> どっかのWEBで構造体はヘッダファイルに書かないほうがいいみたいなことをみたもので。。

これは、おそらく、オブジェクト指向プログラミングの元になった抽象データ
型(Abstract data type)を実現するために、不完全型の構造体だけを宣言した
ヘッダファイルをユーザに使わせ、その中身(メンバの宣言)は、ライブラリの
実装の中に閉じ込めたものを意味しているのだと思われます。

実例を挙げます。main() では、用意された API を使用しないで、Word構造体の
中身にさわることができません。
// -------- mani.c --------
#include <stdio.h>
#include <string.h>
#include "word.h"

int main(void)
{
    Word *word;  char str[100];

    word = create(50);
    while (scanf("%s", str) == 1 && strcmp(str, "quit"))
        add(word, str);
    puts("\n--- before sort ---");
    show(word);
    sort(word);
    puts("\n--- after sort ---");
    show(word);
    destroy(word);
    return 0;
}

// -------- word.h --------
#ifndef WORD_H
#define WORD_H

typedef struct Word Word;

Word *create(int capa);
void destroy(Word *);
void add(Word *, const char *);
void show(Word *);
void sort(Word *);
int  size(Word *);

#endif


// -------- word.c --------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "word.h"

struct Word {
    int capa;
    int size;
    char **word;
};

Word *create(int capa)
{
    Word *wp = malloc(sizeof(Word));
    wp->word = malloc(sizeof(char *) * capa);
    wp->capa = capa;
    wp->size = 0;
    return wp;
}

void destroy(Word *wp)
{
    free(wp->word);
    free(wp);
}

void add(Word *wp, const char *str)
{
    wp->word[wp->size] = strdup(str);
    wp->size++;
}

void show(Word *wp)
{
    int i;
    for (i = 0; i < wp->size; i++) puts(wp->word[i]);
}

static int compare(const void *x, const void *y)
{
    return strcmp(*(char **)x, *(char **)y);
}

void sort(Word *wp)
{
    qsort(wp->word, wp->size, sizeof(char *), compare);
}

int size(Word *wp)
{
    return wp->size;
}


No.1289

Re:構造体をincludeすることについて
投稿者---かずま(2004/02/09 01:42:51)


訂正。
void destroy(Word *wp)
{
    int i;
    for (i = 0; i < wp->size; i++) free(wp->word[i]);
    free(wp->word);
    free(wp);
}


No.1305

みなさん、ありがとうございます。
投稿者---KAZ(2004/02/12 01:49:28)


みなさん、たくさんのレスありがとうございます。よく理解できました。かずまさんにしめしてもらったプログラム大変勉強になります。ADTについてもしらべてみました。他のファイルから構造体の中味を見えなくしているんですね。でもは構造体の書いてあるファイルに関数を用意してあげといて、他のその関数をEXPORTして他のファイルからもあるていどのことをできるようにしているんですよね。なるほどです。ここ2日程、プログラムかいてなかったので今日はADTについてもっと勉強してみます。ありがとうございました。