do-while(0) ステートメントは正確には何のためにあるのでしょうか?

序文

グループで「do-while(0) ステートメントは何に使うのですか?」と質問している人を見かけました。do-while(0) プログラムの最終結果は、プログラムが 1 回だけ実行されることにはならないはずですが、書き込みを行う場合と書き込みを行わない場合の違いは何でしょうか?

複雑なマクロ定義における do-while(0) の利点

なぜ複雑なマクロが必要なのか

(1) 複雑なマクロの定義における do-while(0) の利点を説明する前に、まず複雑なマクロの利点を紹介します。
(2) マクロ定義が非常に複雑であることを知ると、それを関数にしたらどうだろうかと自然に考えます。このような追加機能を取得する関数をマクロ定義で直接定義するのはそれほど便利ではありません。
(3) 交流グループにこんな質問をしたら、すぐに偉い人が返事をくれた。
<1>8ビットのシングルチップマイコンを開発していたときは、あえて深く関数を呼び出すことはせず、マクロ関数を書いたものが多かったという。資源には限りがあるからです。
<2> 兄貴は、自分のシングルチップマイコンのハードウェアスタックが7層あるようで、2層が割り込み用に確保されていると紹介しました。C標準関数の中には2層のスタックを使用するものや、で書かれたものがあります。私自身はレイヤーが 3 ~ 4 つまでに制限されています。スタックは限られているので、残念ですが、関数の詳細な呼び出しが多すぎる場合、または再帰層の数が深すぎる場合、スタック領域が不足し、スタック オーバーフローが発生する可能性があります。スタック領域が新しいスタック フレームを収容するのに十分でない場合、プログラムはクラッシュするか異常終了します。
<3> 当時、彼らはこのようなコードを書いていたのですね

ここに画像の説明を挿入

利点

(1) ビジネス ロジックを作成する場合、より複雑なマクロを定義する必要がある場合があります。次のように

#define  fun printf("hello"); printf("world")
int main()
{
    
    
	fun;
	return 0;
}

(2) このマクロをもとに、判断文を追加すると問題が見えてきます。

/*******  c文件   *******/
#define  fun printf("hello"); printf("world")
int main()
{
    
    
	int a=0;
	if (a == 0)
		fun;
	else
		...
	return 0;
}

/*******  预处理之后   *******/
int main()
{
    
    
	int a=0;
	if (a == 0)
		printf("hello"); printf("world");
	else
		...
	return 0;
}

(3) 上記のコードを通じて、明らかにわかりました。if ステートメント内にある場合、このマクロを実行すると、2 番目のステートメントが if ステートメントに含まれないことがわかります。これにより、if 分岐の後に 2 つのステートメントがあるため、else 分岐には対応する if がなく、コンパイルが失敗します
(4) この時点で誰かがそれを言うかもしれないので、else ステートメントがない場合はどうなりますか? コンパイルできることは間違いありませんが、その場合はとにかく printf("world"); が実行されてしまいます。
(5) 上記の問題は間違いなくプログラムに例外をもたらすため、do-while(0) の目的が明らかになります。
<1>if文に起因する問題を解決しました。
<2>プログラムをより美しくするこれは、習慣的に各ステートメントの後ろに「;」を追加するためですが、do-while(0) を使用しないマクロでは、最後のステートメントに「;」を追加することができません。これは失読症につながりやすいです。

/*******  c文件   *******/
#define  fun do{
      
                        \
				printf("hello");  \
				printf("world");  \
			   }while(0)
int main()
{
    
    
	int a=0;
	if (a == 0)
		fun;
	else
		...
	return 0;
}

/*******  预处理之后   *******/
int main()
{
    
    
	int a=0;
	if (a == 0)
		do{
    
    printf("hello");printf("world");}while(0);
	else
		...
	return 0;
}

プログラムリリースにおける do-while(0) のメリットとデメリット

アドバンテージ

(1) Linux カーネルでは、一部のプログラムを終了するために使用される goto ステートメントがよく見られます。(ドライバのアンインストール、デバイス番号の破壊、GPIOの解除など)
(2)ただし、gotoはソフトウェア工学の仕組みに準拠しておらず、コードがわかりにくくなったり、バグが発生しやすいため、一部の人からは冗談めかして「C言語禁断の技」と呼ばれています。

/*******  使用goto  *******/
void test_func(void)
{
    
    
    //申请资源 。。。
 
    if (!condition1) {
    
    
        goto exit_entry;
    }
 
    //执行一些逻辑
 
    if (!condition2) {
    
    
        goto exit_entry;
    }
 
    //执行一些逻辑
 
    if (!condition3) {
    
    
        goto exit_entry;
    }
 
    //执行一些逻辑
 
exit_entry:
    //释放资源 。。。
 
}
/*******  使用do-while(0)  *******/
void test_func(void)
{
    
    
    //申请资源 。。。
 
    do {
    
    
        if (!condition1) {
    
    
            break;
        }
 
        //执行一些逻辑
 
        if (!condition2) {
    
    
            break;
        }
 
        //执行一些逻辑
 
        if (!condition3) {
    
    
            break;
        }
 
        //执行一些逻辑
    } while(0);
 
exit_entry:
    //释放资源 。。。
 
}

欠点がある

(1) do-while(0) は goto よりも優れているのに、なぜ大手は依然として goto を使用することを好むのでしょうか?
(2) Linux ドライバーの開発プロセスでは、1 つが適用できなかった場合、多くのものを適用する必要があるためです。以前のアプリケーションをすべて解放してから、エラーを返す必要があります。
(3) まだ理解できない人もいるかもしれません。例を挙げますと、ドライバーは最初にタスク A、次にタスク B、最後にタスク C を完了する必要があります。その後、タスクが終了し、最初に c タスクが実行され、次に b タスクが実行され、最後に a タスクが実行されます。大文字のタスクは作成を示し、小文字のタスクはリリースを示します。これらはまったく逆の順序で実行されます。

// 创建
void create()
{
    
    
	A;
	B;
	C;
}
//销毁
void destroy()
{
    
    
	a;
	b;
	c;
}

(4) 以上は、すべてがスムーズに実行されるようにプログラムを書くことです。ただし、A タスク、B タスク、または C タスクのいずれかの実行が失敗した場合。それではどうすればいいでしょうか?
(5) タスク A が失敗した場合、タスク a のみを実行すればよいことがわかっているためです。タスク B の実行に失敗した場合は、b と a を実行する必要があります。do-while(0) ステートメントを使用すると、明らかに非常に面倒です。そしてgotoを使うとさらに便利になります。

// 创建
void create()
{
    
    
	A;
	if(A创建失败)
		goto A_err;
	B;
	if(B创建失败)
		goto B_err;
	C;
	if(A创建失败)
		goto C_err;
C_err:
	c;
B_err:
	b;
A_err:
	a;
}

結論は

したがって、関数の最終出口が固定されており、実行する if 判定文が複数ある場合には do-while(0) を使用した方が良いと言えます。また、関数 exit が固定されておらず、順番に実行される場合は、goto の方が良いでしょう。

おすすめ

転載: blog.csdn.net/qq_63922192/article/details/131360041