ホーム | ブログ | C++辞典 | サイトマップ | FAQ | 掲示板 | リンク集
メイン・メニュー
プログラミング
その他

FAQ(よくある質問と回答)

メインページ »» C/C++

目次


テキストモードとバイナリモードの違いは何ですか?
 fopen などでファイルをオープンするときには、テキストモードかバイナリモードかを指定します。これは文字通り、テキストを扱うか、バイナリデータを扱うかを選択するためのものです。具体的な動作の違いは処理系定義になりますが、実際には以下のような違いがあるようです。

1. テキストモードでは、環境依存の物理的な改行コードと '\n' を相互変換する。具体的には、Unix 環境では 0x0a が、Windows 環境では 0x0d, 0x0a が、Macintosh 環境では 0x0d が物理的な改行コードになります。すなわち、ファイルを読み込む際には、それらの物理的な改行コードから '\n' に変換し、書き込むときは逆に '\n' から物理的な改行コードに変換します。

2. テキストモードでは、EOF を意味するコードが現れると、ファイルの終端と認識する場合があります。0x1a が EOF として扱われる場合がありますが、この方式は、主に過去の環境との互換性のためにあるようです。

3. テキストモードでは、現在のロケールに応じて、文字コードが変換される場合があります。ナロー文字で、文字コードが変換されるケースは見かけませんが、ワイド文字の場合は、多バイト文字とワイド文字との相互変換が常に発生するため、この影響が顕著に現れます。

4. fseek など、ファイル位置を移動する操作では、規格上保障される動作が異なります。テキストモードでは、fseek に指定できるファイル位置は、オフセットが 0L か、ftell が以前に返した値のみ指定可能です。それに対して、バイナリモードでは、基点に SEEK_SET や SEEK_CUR にした場合はランダムアクセスが可能ですが、SEEK_END を用いた場合には期待通りの動作になるという保証がありません。

 Linux などの Unix 系 OS の環境では、テキストモードとバイナリモードの区別がないという表現がよく見受けられますが、それはあくまでもナロー文字を扱っている場合のことであり、ワイド文字を扱う場合には当てはまりません。また、たとえ Unix 環境でナロー文字しか扱わない場合であっても、移植性の高いコードを書くには、テキストモードとバイナリモードを等価なものと考えてはいけません。

ファイルのサイズはどのようにして調べることができますか?
 標準の範囲では、ファイルのサイズを調べるための直接的な方法はありません。したがって、一旦ファイルをバイナリモードでオープンし、1 文字ずつファイルの最後まで読み込んでみるしかありません。

#include <stdio.h>

long fsize(const char *filename)
{
    FILE *stream = fopen(filename, "rb");
    long size;

    for (size = 0; getc(stream) != EOF; size++)
        ;
    return size;
}

 ファイルをバイナリモードでオープンし、fseek(stream, 0L, SEEK_END); を呼び出してから ftell でサイズを取得する方法が紹介されている場合がよくありますが、バイナリモードで SEEK_END を用いた場合には、期待した動作になるかどうかが保証されません。こうした方法は絶対に避けるべきです。

 標準の範囲からは外れますが、stat または fstat を使用すれば、比較的高い移植性を持たせながらファイルのサイズを調べることができます。stat または fstat については、こちらを参照してください。

10進数で入力した整数値を2進数に変換するには?
C の場合


#include <stdio.h>

int main(void)
{
    char s[32+1];
    unsigned long x;
    int i;

    scanf("%lu", &x);

    for (i = 0; i < 32; i++)
        s[i] = '0' + ((x >> (31-i)) & 1);
    s[i] = '\0';

    puts(s);

    return 0;
}


C++ の場合


#include <iostream>
#include <bitset>

int main()
{
    unsigned long x;

    std::cin >> x;
    std::cout << std::bitset<32>(x) << std::endl;
    return 0;
}


 いずれの場合も、32 ビットまでしか対応していません。unsigned long が 32 ビットより大きい場合は、目一杯まで扱えるように変更してもよいでしょう。

__LINE__マクロの整数値を文字列化するには?
 __LINE__マクロは 10 進整数定数に定義されますが、これを文字列かする方法を解説します。つまり、__LINE__ が 123 であれば、"123" を作る方法についてです。


#define STRINGIFY(s)   STRINGIFY_(s)
#define STRINGIFY_(s)  #s


上のようにマクロを定義した上で、次のように使用することができます。


printf("%s\n", STRINGIFY(__LINE__));


sizeof 演算子の評価結果の型は?
 sizeof 演算子の評価結果は size_t 型であると定義されています。

 size_t 型は処理系定義の符号なし整数型です。ポインタの値を格納できる型に定義されている場合が多いのですが、ポインタに near や far などといった種類がある場合、必ずしも最大サイズのポインタの値を格納できるとは限りません。

閏年の判定方法は?
閏年の判定方法は、簡単なようで、実は奥が深い問題です。

ここ何十年かだけを対象にするのであれば、POSIXと同様に、単に4で割り切れる年を閏年だと考えても問題ありません。

int is_leap_year(int year)
{
    return year % 4 == 0;
}

グレゴリオ暦のルールでは、100で割り切れ、かつ400で割り切れない年は閏年としないことになっていますので、

int is_leap_year(int year)
{
    return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
}

とすれば、グレゴリオ暦またはUTCの閏年は判定できます。

問題はここからです。

グレゴリオ暦のルールでは、数世紀規模の計算を行っているわけです。何百年も前の閏年を判定するのであれば、単にグレゴリオ暦のルールを用いるだけでは現実にそぐわない結果となります。

グレゴリオ暦が採用されたのは、(本能寺の変が起きたのと同じ)1582年のカソリック圏からです。日本での採用は明治に入ってからです。プロテスタント圏やアジア諸国ではまた事情が異なります。(ウィキペディアの記事参照)

こう考えると、数世紀前の閏年を判定するには、ロケールを考慮に入れるなどしなければならないことがわかります。しかし、C言語のロケールは、歴史的なことまでは考慮されていないので、標準のロケールとは別の仕組みが必要になるかもしれません。

もっとも、ここまで考慮する必要が実際にあるかというと、普通はないわけです。過去のことはこもかく、未来に対する対応だという考え方もありますが、それこそ何百年先のことなど分かるはずもありません。イスラム圏が席巻すれば、イスラム暦が標準になることもないとは言い切れないのです(極端な話、宇宙世紀何たらとかの暦法を使わないとも限りませんし)。

ここ数十年のことを考えればよいのであれば、単に4で割り切れるかどうか(ユリウス暦と同じ方法)で十分ではないでしょうか。

CとC++のどちらから学習した方がよいでしょうか?
当然のことですが、実際に使いたい言語から学習してください。最終的には両方使いたいのであれば、よく使うであろう言語から学習するとよいでしょう。

よく間違って伝えられるのは、C++はCのスーパーセット言語で、Cで書いたプログラムは、C++でもそのまま使えるというものです。
実際にはそんなことはありませんし、何でもよいので動けばそれでよいというのではなく、きちんと信頼性の高いプログラムを書くためには、それぞれの言語の特性を十分に把握しておく必要があります。

そのため、本当に使いたい言語を重点的に学習するようにしてください。


投稿された内容の著作権はコメントの投稿者に帰属します。
 ホーム | プロフィール | メール | ログイン | 管理
Copyright © 2005-2009 by TAKAGI Nobuhisa