質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

Q&A

解決済

2回答

724閲覧

C言語 strlenに実装されるマクロでdo{...}while(0)が使われている理由を知りたい

Kchan_01

総合スコア110

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

0グッド

0クリップ

投稿2020/10/25 05:06

C言語を学んでいるものです。

freebsdのstrlenの実装を確認しています。
freebsd/strlen.c at master · lattera/freebsd

ここで実装されているtestbyte(x)というマクロについて、疑問があります。
なぜdo{...}while(0)で囲まれているのでしょうか。
試しにdo whileを取って、実行して見ましたが、問題なく動きました。

while(0)だと必ず終了するのでdo...whileを使う意味が無いように思います。
どういった理由で使用されているのでしょうか。

#include <sys/cdefs.h> #include <stdlib.h> static const unsigned long mask01 = 0x0101010101010101; static const unsigned long mask80 = 0x8080808080808080; #define LONGPTR_MASK (sizeof(long) - 1) #define testbyte(x) \ do { \ if (p[x] == '\0') \ return (p - str + x); \ } while (0) size_t ft_strlen(const char *str) { const char *p; const unsigned long *lp; long va, vb; lp = (const unsigned long *)((uintptr_t)str & ~LONGPTR_MASK); va = (*lp - mask01); vb = ((~*lp) & mask80); lp++; if (va & vb) for (p = str; p < (const char *)lp; p++) if (*p == '\0') return (p - str); for (; ; lp++) { va = (*lp - mask01); vb = ((~*lp) & mask80); if (va & vb) { p = (const char *)(lp); testbyte(0); testbyte(1); testbyte(2); testbyte(3); testbyte(4); testbyte(5); testbyte(6); testbyte(7); } } return (0); }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

do while で囲まなかったとき、たとえば

c

1if (ほにゃらら) testbyte(x); 2else なんちゃらかんちゃら;

といった文があったら、これは

c

1if (ほにゃらら) 2 if (p[x] == '\0') 3 return (p - str + x); 4 else なんちゃらかんちゃら;

と展開されます。

else 節が外側の if と対応付けることを意図しているにもかからわず testbyte の中の if と対応付いてしまいます。

こういうことを防止するためにマクロ定義で do while を用いるのはよくあるテクニックです。


Free BSD のカーネルのコーディングスタイルの文章の中にマクロを do while で囲むことについて言及があるのを発見しました。 if 文の中で安全に使えると述べています。

STYLE(9) FreeBSD Kernel Developer's Manual

余談ですが現代では関数として記述できるならば関数として書くことが一般的には好まれます。 現代のコンパイラはこのような簡単な関数をインライン化することが期待できるのでマクロで書く優位性がないからです。 カーネルの場合はコンパイラの最適化機能を無効に (または弱く) する必要がある場合もありますので事情が異なるのかもしれませんが。

投稿2020/10/25 05:27

編集2020/10/25 08:27
SaitoAtsushi

総合スコア5446

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pepperleaf

2020/10/25 09:13

昔のマニアックなテクニックではないかと。(初期のC世界)
guest

0

単に、{ }で囲まずに、do { } while(0)で囲む理由ですが、例えば、

C

1#define foo(x) { a=x; b=x*2; }

などと定義すると、

C

1if(zz) foo(1); 2else foo(2);

が、

C

1if(zz) { a=1; b=1*2; }; 2else { a=2; b=2*2; };

となり、余分な;のためにelseがエラーとなります。
これを回避するためには、

C

1if(zz) foo(1) 2else foo(2)

と書けばいいのですが、そのためにはfooは関数じゃなくて(最後に;を書いてはいけない)マクロだということを知っていないといけませんし、見たときにもちょっと違和感があります。

まあ、「ifelseの後は単文でも必ず{ }を付ける」という方針で、

C

1if(zz) { foo(1); } 2else { foo(2); }

と書けばいずれにせよ問題ないのですが、「使う側では全く関数のごとく書けるマクロ」に近づけようとすると、do { } while(0)と、最後に;を付けられる形の定義をします。

とはいえ、実引数が評価されるタイミングなどが同じには出来ないので、「使う側では全く関数のごとく書けるマクロ」の実現は限度がありますが。

投稿2020/10/25 06:46

otn

総合スコア84557

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問