私は新しくCプログラミングを学びます。
多くの関数、パラメーターの受け渡し、呼び出し、およびすべてを備えたCプログラムがある場合、どの変数をどこで宣言する必要があるかということは大きな問題になります。
私の質問は、すべての変数(または少なくとも私を混乱させる変数)をグローバル変数として宣言した場合、私の問題は解決されますが、それは良い習慣ですか?
他の人はすでにグローバル変数は解決策ではないと述べました。ローカル変数を管理するためのヒントをいくつか示します。
一部の人々は、可能な限り最新の行(最初に使用する直前)で変数を宣言します。他のものは、それらをすべて関数本体の上部で宣言します(非常に古い標準との互換性が高くなります)。どちらでもかまいませんが、1つのスタイルを選択し、プログラム全体でそれを使用してください。
1つの関数で多くのローカル変数が必要であることがわかった場合は、この関数を分割する必要があることを示しています。変数が多すぎますか? 5つ以上は不快だと思います。許されない10以上。
多くの引数(4つ以上)を関数に渡す場合、それも悪い兆候です。多くの場合、これは関数を2つ以上の小さな関数に分割することで解決できます。場合によっては、引数を構造体に集約してこれを渡すことができます。
関数と変数に適切な名前を付けてください!私はこれを十分に強調することはできません。 hlwrld
やac
などの名前は使用しないでください。 helloWorld
およびaccumulator
を使用します。書くのに時間がかかりすぎると感じても心配はいりません。タイピングが速くなります。そうでない場合でも、デバッグに費やす時間は、タイピングの追加時間をはるかに上回ります。
いいえ、これは解決するよりもはるかに多くの問題を引き起こします。それは非常に悪い習慣であり、古典的なアンチパターンです。しないでください。
主な問題の1つは、グローバル変数がどのように変更され、変更の影響が何であるかを発見することは、それらを関数に渡すことによってコードの残りの部分から分離した値がある場合よりもはるかに難しいことです。
プログラミングを学ぶことの大部分wellは、お互いを知る必要のある部分が機能し、お互いを知る必要のない部分が機能しないようにプログラムを設計することを学ぶことです。
会社を始めることを想像してください。 3人または4人の従業員がいる場合、誰もが自然に他の誰もを知っていて、物事はうまくいきます。しかし、それは100人の従業員にまで拡大することはできません。ある時点で、ビジネスの特定の側面をそれぞれ担当する部門に従業員を編成する必要があります。経理部外の従業員は本をいじってはいけません。また、出荷部外の従業員は製品を出荷してはなりません。 印象的な構造ビジネス上で物事がうまく機能します-それは組織化された予測可能な方法でビジネスを機能させます。そのような構造は、すべてのことを少しずつ行うことに慣れている古参者を苛立たせるかもしれませんが、それは会社の成長のために必要です。
同じことがプログラムにも当てはまります。 Hello、world!またはFizzBuzzまたはTic Tac Toeを記述している場合、すべてのコードが1つのファイルに収まり、構造について多くを考える必要があります。しかし、それはスケーリングしません。より複雑なプログラムを書き始めるときは、コードに構造を課す必要があります。タスクをグループ化する方法、各モジュールがそのタスクを実行するために必要な情報、およびさまざまなモジュールがどのように通信するかについて考える必要があります。
すべてにグローバル変数を使用することは、すべての従業員がお互いを知り、ビジネスについてすべてを知っている会社を運営しているようなものです。しばらくの間は機能しますが、成長するのが難しくなります。プログラムの編成とそれを通る情報の流れを学ぶのが早ければ早いほど、プログラミングとは何か本当にのことをすぐに知ることができ、有用なプログラムをすぐに書くことができるようになります。
私の経験では、これを行う最もクリーンな方法は、必要なすべての変数を含むデータ構造を作成し、この構造への参照をすべての関数に渡すことです。
グローバル変数を使用する正当な理由が1つあります。これは、シグナルハンドラー内からこのデータ構造にアクセスして、データを再ロードするか、アプリケーションをクリーンアップして終了する必要がある場合です。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
struct my_data { int a, b, c; };
static struct my_data *_global_cfg = NULL;
void sigint(int signo);
struct my_data * my_init_data(void);
int my_processing(struct my_data *cfg);
int main(void) {
struct my_data *cfg = my_init_data();
signal(SIGINT, sigint);
return my_processing(cfg);
}
void sigint(int signo) {
printf("SIGINT: a=%d\n", _global_cfg ? _global_cfg->a : 0);
exit(EXIT_SUCCESS);
}
struct my_data * my_init_data(void) {
struct my_data *ret = calloc(1, sizeof(*ret));
_global_cfg = ret;
return ret;
}
int my_processing(struct my_data *cfg) {
if (cfg == NULL) return -1;
while (1) {
printf("%d - Waiting for SIGINT\n", cfg->a++);
sleep(1);
}
return 0;
}
私はグローバル変数の良いパターンを覚えていませんが、これは間違いなくそうではありません。パラメータが多すぎる場合は、おそらく関数をリファクタリングする必要があることを示しています。
特定の変数名は何度も何度も使用される傾向があります。たとえば、temp
、i
、theVar
、counter
などです。変数名を考え出すために創造的に多くの時間を費やすのではなく、それらを覚えるのにより多くの時間を費やすために、それらに小さなスコープを与えるのが良いでしょう。
すべての変数をグローバルにすると、temp2
、theOtherVar
、timer
(counter
と混同しないでください)などの名前になり、ループコントロールが到達するアルファベットなどすべての文字.
可能であれば範囲を制限します。 1つの関数のみが変数を使用する場合は、その関数で宣言します。プログラム全体で変数を使用する場合にのみ、変数をグローバルにする必要があります。それでも、それが常に最良のアイデアであるとは限りません。
関数を持つことの要点は、ロジックの一部をカプセル化できることです。たとえば、2つの座標間の距離を検出する関数がある場合、座標をその座標に渡し、距離を取得でき、その間で何が起こったかについて心配する必要はありません。ただし、関数がグローバル変数を使用していて、プログラムの別の場所で誤って変更した場合、関数が異常な動作をする理由を解明するために何時間も費やすことがあります。
私の提案は
いいえ、それはグローバルネームスペースを汚染し(最終的には名前がなくなる)、グローバルを変更できるため、良い方法ではありませんanywhere
多くの場合、渡されるパラメータのセットがある場合は、渡すことができる構造体でそれらを組み合わせることができます(ポインタが大きくなりすぎてコピーできない場合はポインタを渡します)。