- PR -

C の localtime() の引数は混乱を招く。

投稿者投稿内容
ぽんす
ぬし
会議室デビュー日: 2003/05/21
投稿数: 1023
投稿日時: 2004-09-10 12:21
引用:

ナキヲさんの書き込み (2004-09-10 10:06) より:
size_tとtime_tは規定が違うのでしょうか。



> Ancient Unix でカレンダー時刻を格納していたのが int time[2]
> であったことの名残ではないかと思います。

と書きましたが...
これには同意しない、ということですか?
# int time[2] について確認したいのであれば、容易に入手できる
# 6th Edition のカーネルソースでどうぞ。

どこでみたのか忘れましたが、雑誌の記事とかでもみかけたような
気がします。
ネタの方向から考えて、Unix User の Truth of the Legend
あたりかな?
ぽんす
ぬし
会議室デビュー日: 2003/05/21
投稿数: 1023
投稿日時: 2004-09-10 12:27
引用:

コブラさんの書き込み (2004-09-10 11:37) より:
なんで、Solaris8 では 無問題でバグが潜在化して、なぜ RedHat やとちゃんと
セグメンテーション・フォルト出して顕在化してくれるんか、という事でね。
セグメンテーション・フォルトは出してくれんとマズい訳で。


文法を理解せず、APIについて調べることもせず、コンパイラや
規格に文句をつけるばかりなら C も C++ も使うのをやめた
ほうがよいのでわ?
お得意のS式で自分だけの世界を構築したほうが幸せでしょう。
タコツボ
常連さん
会議室デビュー日: 2004/01/20
投稿数: 22
お住まい・勤務地: 京都・大阪
投稿日時: 2004-09-10 12:42
タコツボです。

引用:

UTさんの書き込み (2004-09-09 20:05) より:
ポインタ引数の領域を確保するべきか、するべきじゃないかを判断する指針に
const型か非const型かがあるとおもいます。
const型のポインタに確保していない領域のアドレスの渡してあげても、
渡された関数はこの領域に対して基本的にはなにもできません。
このようなアドレスを渡すことに意味は無いわけです。

関数でわざわざconst指定しているあたりに、
「既に確保した領域を渡してあげるんだなぁ・・・」
と使用者は察してあげる必要があります。


ん〜、これはちょっと違うのでは?
const修飾子は、ただ単に呼ばれた側の関数(今回の例で言えばlocaltime)の
中で値を変更しない、参照のみである、ということを意味しているだけで、
constの有無に関わらず領域は確保されていないとまずいのではないでしょうか。
localtimeの中から確保されていない領域のアドレスを参照に行くということは
即ち意味の無い値を見に行くことになるわけで、そのアドレスがアクセスできない
領域(セグメント)を指していた場合には、参照した時点で落ちるものと思われます。

引用:

コブラさんの書き込み (2004-09-10 11:37) より:
私が言いたいのは、上の、

ret= func((unsigned long *)&bb->sa);
ret= func((unsigned long *)&bb->sb);

この部分でね。
なんで、Solaris8 では 無問題でバグが潜在化して、なぜ RedHat やとちゃんと
セグメンテーション・フォルト出して顕在化してくれるんか、という事でね。
セグメンテーション・フォルトは出してくれんとマズい訳で。
問題を勝手に妄想で履き違えとる人が過去二人ぐらいおられましたが、私にとって問題は逆に
これでなぜ Solaris8 はセグメンテーション・フォルト出さんのか。これ不確定要素
になるでしょ?


s型のポインタ変数bbは、main関数の中のauto変数であり、初期値は不定です。
不定というのは、このプログラムが起動された時点でのメモリの中身が
初期化されずにそのまま残っているということです。
ですから、OSだけでなくマシンや直前に動かしていたアプリケーションなどによって結果が異なったりすることは十分に考えられます。
確認する方法として、bbを使用する前に
printf("%08X\n", bb);
のようにしてbbの値を見てみると、bbがいったいどこを指しているのかがわかります。
この値次第で(運良く)動くこともあれば、セグメンテーション・フォルトになることもあるということだと思います。

コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-10 13:17
OSによって文法が違ごてしまう事を意味する事になるとも悟らず、同じAPIを使って
OSによって結果が異なる事を黙認し、それを己の狭い世界で「常識」と思い込み、
デファクトスタンダードなる幻想にとり憑かれ、改善・進歩を諦めた現状維持
Conservative Jap、標準関数のインターフェースにおいてどちからと言えば低級言語
に属するとは言え、「最低限のユーザーフレンドリー」という重大なファクターを無視し、
曖昧性を助長させる馬鹿は放置し、

件の問題は、最初の例では

bb = (struct S *)malloc(sizeof(struct S));

こいつと

free(bb);

こいつを追加し、更に `b' に関しても同じ要領で「実体」の存在を前提に

unsigned long c; を別に宣言してやり、

b = &c;

というセンテンスを追加して実体のアドレスを確定させてやらねばならない。
つまり、Cでは変数を宣言した後、アドレスと値の両方を具体的に確定させてやら
なければ初期化された事にならない。


後に出した gettimeofday(); と localtime(); を使った短い例では

ts = (struct timeval *)malloc(sizeof(struct timeval));

こいつと

free(ts);

こいつで -Wuninitialized -Werror のチェックを乗り越えられる。
まぁ、これだけでも、大概面倒臭い訳やが(笑)、

最初の例で、

35行目付近にある

*b = 200L;

こいつの場合、値は初期化されてるものの、宣言されたまんまの実体のアドレス亡き
ポインタに値をぶち込んでるので、意味が無い。

b = &c;

が無いと、Warning は出るものの、セグメンテーション・フォルトが起きたり、
起きなかったりする。。。どちらかと言えば、セグメンテーション・フォルトが
発生する確率の方が高い。ところが、Solaris では 100% 間違いなくセグメンテーション・
フォルトは発生しない。

これが問題な訳である。
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-09-10 13:18
コード:

#include <time.h>
#include <sys/time.h>

main()
{
struct timeval *ts;
struct tm *tm;

gettimeofday(ts, NULL);
tm = localtime((const time_t *)&ts->tv_sec);

tm->tm_year += 1900;

printf("%d\n", tm->tm_year);
}


このコード、どこが最初に Segmentation fault を起こすのか確認しようと動かしてみたところ、正常に動作してしまいました
ちなみに RedHat 7.3, gcc ver. 2.96-110 です。

オプティマイズしない状態でしたが、たまたま ts がまともにアクセスできる領域のアドレスで初期化されていたのが原因でした。
オプティマイズすると localtime() を呼ぶ前に落ちました。
この時は、struct timeval * としては ts は正しい値(ポインタアラインメントには適合)していましたが、ts->tv_sec が long アラインメント不適合になっていました。

引用:

いや、私の場合・・・もう見ておられるかも知れませんが、 gettimeofday(); の第一引数
で値が設定される事は確実でした。


ここはウソですよ。gettimeofday() は引数として渡されるポインタ変数の値は設定しません。(特にこの関数に限った話ではありませんが)
疑わしいと思われたら、宣言直後、および gettimeofday() の前後で第一引数の値を表示させてみてください。

今の場合はポインタ変数が明示的に設定されていなかったので、環境に依存する不定な値で初期化されており、それが Solaris の場合にはたまたま更新/参照ができてメンバのアラインメントも正しい値になり、偶然動いてしまった。
それだけのことです。
RedHat であっても動くケースはありましたし

# タコツボさんの後半と大きく被ってしまいました(_ _)

[ メッセージ編集済み 編集者: Gio 編集日時 2004-09-10 13:22 ]
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-09-10 13:27
引用:

ぽんすさんの書き込み (2004-09-10 12:21) より:

> Ancient Unix でカレンダー時刻を格納していたのが int time[2]
> であったことの名残ではないかと思います。
と書きましたが...
これには同意しない、ということですか?



いえ、同意しています。
不勉強で、C標準ライブラリの大元の仕様をみたことが無いので、
size_t,time_t等が本来どのように規定されているのか知らないのですが、

time_t→実装方法は規定しません
size_t→int等算術演算を行える型とします

とかその類のことが書いてあると勝手に想像しています。

もし
size_t→実装方法は規定しません
なんて規定になっているのだったら、
malloc(size_t)は
malloc(size_t*)
にしないといけないなと思ったまでです。

c標準ライブラリの規定ってどこで見られるかご存知ですか?



[ メッセージ編集済み 編集者: ナキヲ 編集日時 2004-09-10 13:28 ]
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-10 13:36
UT氏:
>const型のポインタに確保していない領域のアドレスの渡してあげても、
>渡された関数はこの領域に対して基本的にはなにもできません。

たこつぼ氏:
>const修飾子は、ただ単に呼ばれた側の関数(今回の例で言えばlocaltime)の
>中で値を変更しない、参照のみである、ということを意味しているだけで、

UT氏:
>このようなアドレスを渡すことに意味は無いわけです。

たこつぼ氏:
>ocaltimeの中から確保されていない領域のアドレスを参照に行くということは
>即ち意味の無い値を見に行くことになるわけで、

俺には、二人が全く同じ事を言ってるように見えるんやが・・・



Gio氏:
>ここはウソですよ。gettimeofday() は引数として渡されるポインタ変数の値は設定
>しません。(特にこの関数に限った話ではありませんが)

もし、引数がポインタ変数「だけ」という条件に絞られたなら、、な。
「第一引数」としか限定してない。アドレス参照してそのメンバーに値が設定
されんとしたら、 gettimeofday(); としての機能が存在せんという事になるが。。。

>このコード、どこが最初に Segmentation fault を起こすのか確認しようと動かして
>みたところ、正常に動作してしまいました

これは、俺にとっては大問題です。
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-09-10 13:43
引用:

文法を理解せず、APIについて調べることもせず、コンパイラや
規格に文句をつけるばかりなら C も C++ も使うのをやめた
ほうがよいのでわ?
お得意のS式で自分だけの世界を構築したほうが幸せでしょう。



ついでながら、これにも同意

そもそも、不定なポインタに対してアクセスしたらどうなるか、
は処理系の問題であってC標準ライブラリの規定範囲外では
ないかと思います。

また、ライブラリの歴史的経緯や、ライブラリ固有の設計方針が
あると思いますので、仕様を読んで(読んでわかる仕様があるかどうかは
別の問題ですが)理解する必要があると思います。

設計がおかしい!と思っても、既に広まってしまっているものは
そうたやすくかえられないという事情もあると思いますし、
それがいやなら、自分で作ればいいだけのことです。
例えば、わたしはstrtokやgetsは使いません。

余談ですが、
win32 APIで、テキストボックスから文字列を取得する以下の
関数があります。

コード:
ダイアログボックス内の指定されたコントロールに関連付けられているタイトルまたはテキストを取得します。

UINT GetDlgItemText(
  HWND hDlg,       // ダイアログボックスのハンドル
  int nIDDlgItem,  // コントロールの識別子
  LPTSTR lpString, // テキストを受け取るバッファへのポインタ
  int nMaxCount    // 文字列の最大サイズ
);



先日、会社の新人さんに、この関数で、渡したバッファサイズ
を超える文字列があったらどうなるのか?
と質問されました。
この関数は、バッファを越える文字列がボックスにあった場合、
バッファぎりぎりまで文字を埋めて返してきます。
(最後の1バイトをNULLで止めてくれない)
で、新人さんは、"なんで?"と聞くわけです。
いやなら使わなければいいんですが、
そういうわけにもいかないので、仕様だから、そういうもんだと
思って気をつけよう、といっておきましたが。。

スキルアップ/キャリアアップ(JOB@IT)