コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2008年1月31日 >>> 2008年1月31日
link もっと後

2008年1月31日

long longの罠

目次: C言語とlibc

突然printfの動きがおかしくなって、引数で与えた数値を表示したりしなかったりするようになりました。

で、調べてみるとこーんなプログラムになってたわけです。


#include <stdio.h>

int main(int argc, char *argv[])
{
  long long int a;
  int b, c;

  a = 0x1234567887654321LL;
  b = 200;
  c = 300;
  printf("a:%d, %s, b:%d, c:%d \n", a, "strings", b, c);

  return 0;
}

実行してみると

$ gcc a.c
$ ./a.out
Segmentation fault

見事に落ちました。

このプログラムのまずいところは変数aは8バイト(long long int型)あるのに、printfには %d書式(signed int型の指定)と指示しているため、printf側が4バイトしか見ない、ってところです。残った4バイトは次の %s指令のデータと見なされて、その結果変なアドレスを見に行ってプロセスが死にます。

なので、この場合は %dじゃなくて %lldと書いてlong long signed int型であることを指定すべきです。正しく動いたときの結果はこんな感じ。

$ ./a.out
a:1311768467139281697, strings, b:200, c:300

整数だからといってなんでもかんでも %dにしちゃだめですよ、って教訓ですな。

$ gcc --version
gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc -Wall a.c
a.c: In function 'main':
a.c:12: warning: format '%d' expects type 'int', but argument 2 has type 'long long int'

ちなみにgccなら -Wallオプションを指定すれば、printfの書式指定が間違っていたときに教えてくれます。

the Typedef Hell!!

もちろん好きこのんでこんな状態を作ったわけではありませんので、お間違いなく…。

この問題に出会うきっかけとなったプログラムは、言うなれば「typedef地獄」でしょうか。ぱっと見ても、整数なのか浮動小数点数なのか、はたまた構造体なのか…型が全くわかりません。何よりひどいのはtypedefが連鎖しまくっていることでしょうか。

例えばAライブラリのA_TypeがBライブラリのB_Typeのエイリアスだったとして、そのB_TypeがさらにCライブラリのC_Typeのエイリアスで、それがさらにDの…というように、ひねりのないtypedefが延々と続きます。

そのくせ最後まで辿ってみると無条件でtypedef int X_Type;(単なるint)とかいうオチが多いので、ウザいことこの上ない。

やがて調べるのが面倒くさくなって、どうせlongかintだろって思ってなめてたら、long long intのエイリアスがいくつか混ざっていて、警告オプション -Wallもご丁寧に抹消されており、上記の問題にはまったわけです。

C言語において、ダメなマクロの話は良く聞きますが、ダメなtypedefの使い方はそうそうないと思う。

編集者:すずき(2023/02/04 20:26)

コメント一覧

  • hdkさん(2008/02/02 01:02)
    型って難しいですね。昔は 16 ビットをこえる整数を扱うのに long int を使っていましたが、今は環境によっては 64 ビットになってしまいます。typedef を使うと、あとから変えるのは簡単になりますが、それはそれで読みづらい。printf みたいな変な関数が存在する C の仕様が古すぎるんでしょうか。(C++ の cout ならこの手のトラブルは起きないのかも...)
    # ちなみに x86 の 64 ビット環境なら上のプログラムはちゃんと動いてしまいますw
  • すずきさん(2008/02/02 03:17)
    >hdkさん
    C のうまくないところは
    ・環境により int の大きさが変わる
    ・printf のような可変引数を取る関数がある
    ってところでしょうか。
    >typedef
    3連鎖以内に抑えていただければ幸せだったなー、と…(泣
    >64ビット環境
    64ビットなら問題ないっす。32ビットを想定ってのを書き忘れました。
    # そしてまた 64 -> 128 ビットの交代時に C 言語(きっと生き残っている)は同じ問題を起こすわけか…。
open/close この記事にコメントする



link もっと前
2008年1月31日 >>> 2008年1月31日
link もっと後

管理用メニュー

link 記事を新規作成

<2008>
<<<01>>>
--12345
6789101112
13141516171819
20212223242526
2728293031--

最近のコメント5件

  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」
  • link 24年1月24日
    すずきさん (02/19 18:37)
    「簡単にできる方法はPowerShellの...」
  • link 24年1月24日
    KKKさん (02/19 02:30)
    「追伸です。\nネットで調べたらマイクロソ...」
  • link 24年1月24日
    KKKさん (02/19 02:25)
    「私もエラーで困ってます\n手動での回復パ...」

最近の記事3件

  • link 24年3月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/26 03:20