C言語関係掲示板

過去ログ

No.516.printf関数を作る

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

printf関数
投稿者---さとし(2002/12/24 13:06:29)


Cでprintf関数を自分で作るにはどうすればいいんでしょうか?
ヘッダファイルを見てもまったくわかりません。。。
お教えください・・・。

No.4043

Re:printf関数
投稿者---aki(2002/12/24 14:33:47)


printfなどの可変個引数を受け取る関数を作るには staarg.h で定義さ
れているマクロを利用します。

これは簡易版ですが、printfは意外に奥が深いので、きちんとしたもの
を作ろうとしたら結構大変です。
#include <stdio.h>
#include <stdarg.h>

int my_printf(const char *fmt, ...)
{
    va_list ap;
    const char *p;
    int n = 0;

    va_start(ap, fmt);
    for (p = fmt; *p; p++) {
        if (*p != '%') {
            putchar(*p), n++;
            continue;
        }
        switch (*++p) {
        case '%':
            putchar('%'), n++;
            break;
        case 'c':
        {   int c = va_arg(ap, int);
            putchar(c), n++;
            break;
        }
        case 's':
        {   char *s = va_arg(ap, char*);
            while (*s) putchar(*s++), n++;
            break;
        }
        case 'd':
        {   int i = 0, v = va_arg(ap, int);
            char buf[10];
            if (v < 0) {
                putchar('-'), n++;
                v = -v;
            }
            do
                buf[i++] = v % 10;
            while (v /= 10);
            while (--i >= 0) putchar(buf[i] + '0'), n++;
            break;
        }
        }
    }
    va_end(ap);
    return n;
}


No.4044

Re:printf関数
投稿者---aki(2002/12/24 14:38:00)


>... staarg.h で定義されている ...
typoです。これは stdarg.h でした。

No.4045

Re:printf関数
投稿者---かずま(2002/12/24 16:48:56)


重箱の隅をつつくようで恐縮ですが、
    do
        buf[i++] = v % 10;
    while (v /= 10);
を
    do
        buf[i++] = (unsigned)v % 10;
    while ((unsigned)v /= 10);

にしないと、my_printf("%d", -2147483648); が正しい表示になりません。
もちろん、これは printf の実装の本質とは無関係な指摘です。

No.4049

Re:printf関数
投稿者---aki(2002/12/25 02:19:11)


>   do
>       buf[i++] = v % 10;
>   while (v /= 10);
>を
>   do
>       buf[i++] = (unsigned)v % 10;
>   while ((unsigned)v /= 10);
>
>にしないと、my_printf("%d", -2147483648); が正しい表示になりません。

ちょっと悩みましたが、なるほどです。2の補数表現の処理系では -INT_MIN
は INT_MIN に等しい(本当は未定義)けど、それをただ unsigned に変換す
るだけで符号が反転し、INT_MIN の反数になると。

(unsigned)v /= 10 のところは v = (unsigned)v / 10 ですね。

No.4065

Re:printf関数
投稿者---かずま(2002/12/26 00:44:37)


> (unsigned)v /= 10 のところは v = (unsigned)v / 10 ですね。

すみません。これは、私のミスです。
C では、キャストの結果は左辺値になりませんから。
これに対して、C++ では、左辺値をキャストしてもやはり左辺値です。

ところが、各処理系の対応を見ると、
 gcc: C++ と同じ。-pedantic オプションをつけると警告が出る。
 BC++: C++ と同じ。
 VC++: C++ と同じ。
 LSI C: C の規格どおり。
となってしまいました。もちろん、ファイルの拡張子は .c で試しています。

No.4082

Re:printf関数
投稿者---aki(2002/12/27 11:20:13)


>これに対して、C++ では、左辺値をキャストしてもやはり左辺値です。

これについて疑問に思い、調べてみましたが、C++でも(参照型へのキャ
ストは別にすれば)キャストの結果は右辺値になるようです。

---------- http://www.kuzbass.ru/docs/isocpp/ ----------
5.4 - Explicit type conversion (cast notation) [expr.cast]

-1-
The result of the expression (T) cast-expression is of type T.
The result is an lvalue if T is a reference type, otherwise the
result is an rvalue. [Note: if T is a non-class type that is
cv-qualified, the cv-qualifiers are ignored when determining the
type of the resulting rvalue; see basic.lval. ]
----------------------------------------------------

dynamic_cast、const_cast、reinterpret_cast、static_cast について
所でも、参照型以外へのキャストは rvalue だというふうに書かれてあ
ります。

No.4106

Re:printf関数
投稿者---かずま(2002/12/29 10:00:27)


>> これに対して、C++ では、左辺値をキャストしてもやはり左辺値です。

> これについて疑問に思い、調べてみましたが、C++でも(参照型へのキャ
> ストは別にすれば)キャストの結果は右辺値になるようです。

すみません。またしても、私のミスです。
ご指摘ありがとうございました。

なぜ、こんな間違った思い込みをしていたのか考えてみると、

1. 以前、ARM(The Annotated Reference Manual) で、参照型へのキャストが
 左辺値になることを知ったとき、キャストで結果が左辺値になることがある
 というのが印象的だった。
2. 条件式の結果が左辺値になることがあるというのも印象的だった。
 (x ? a : b = 1; と書けること)
3. g++ では、左辺値のキャストの結果がやはりキャストであって、これは
 -pedantic オプションを指定しても、警告が出ないこと。

などが挙げられるでしょう。また、VC++ や BC++ で、左辺値のキャストの
結果が左辺値になることも、この勘違いに拍車をかけたようです。
規格に完全に準拠した処理系って、なかなかないもんですね。

No.4046

Re:printf関数
投稿者---さとし(2002/12/24 17:23:29)


ありがとうございました。
今から内容をしっかり理解していこうと思います。