- PR -

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

投稿者投稿内容
Toshi
ベテラン
会議室デビュー日: 2003/09/12
投稿数: 62
投稿日時: 2005-01-14 14:07
引用:

コブラさんの書き込み (2005-01-14 12:59) より:

Solaris にせよ、 RedHat にせよ、チェックのレベルはともかく、結果として
core を吐くとか結果が間違っておれば一目瞭然なのですが、Solaris の場合
「正しく」動いてしまうのが問題だと言う事で。



コード:
#include <stdio.h>
#include <time.h>
 
main()
{
        struct tm *p_tm;
        time_t *p_t;
 
        printf("p_tm = %p\np_t = %p\n", p_tm, p_t);
        p_tm = localtime(p_t);
        printf("p_tm = %p\np_t = %p\n", p_tm, p_t);
        printf("%d : %d : %d\n", p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec);
}


と似たコードを書いてsolaris とredhat でgccでコンパイル、実行したら、solarisではcoreを吐いてredhatではなんとなく動いてしまいました。

C言語では、ローカル変数はスタックに領域が確保され、人為的に初期化しない限り、たまたまスタックに有った値になっていると勉強しました。

Solarisもその通りだとしたら、前の処理でスタックにのこった汚れのデータがはいったポインタ変数localtimeに渡しても、たまたま正常に動いて見えただけではないでょうか?

スタックの汚れは、前の処理しだいですので、直前の処理を変えてみると結果が変るかも知れません。
試しに struct tm *p_tm; の前に int i; を宣言したらsolarisでもなんとなく動いてしまいました。
ほろりん
ベテラン
会議室デビュー日: 2004/11/24
投稿数: 98
お住まい・勤務地: あそこ
投稿日時: 2005-01-14 14:08
ただの思い出ばなしですじゃ。
プログラミング言語C(ANSI−C以前の本)には「標準関数はC言語の一部ではない。」と書かれていたように覚えてるのじゃ。それで、結構驚いたもんじゃ。
#この標準関数とはprintf()とかその手の関数じゃ。
#古い本なんで、まだうちにあるかのう。
だから、わしはCはマクロアセンブラぐらいにしか思ってないんじゃ。

値渡しかどうかという話じゃが、関数の引数は変数の値をスタックにコピーしてから関数に渡されるんじゃ。だから、関数内で引数の値を変えてもスタック中の値が変わるだけで、呼び出し側の元の変数の値の部分は変わらんのじゃ。だから、値渡しじゃと。
で、ポインタが引数ということは、たまたま、その値がポインタだったこいうことじゃ。

ポインタのポインタなんぞ、むずかしくない。渡した値がポインタで、たまたまそれが指す先がポインタということだけじゃ。ポインタが指す実態がintだったり、ぽゐんただったりするだけじゃ。

そうえば、引数にcharを二つ渡して、関数側でそれをintで受け取るなんて極悪なテクニックもあったのう。
プロトタイプもなかったしのう。


[ メッセージ編集済み 編集者: ほろりん 編集日時 2005-01-14 14:19 ]
プログラミング言語C(初版)をみつましたじゃ。「標準関数はC言語の一部ではない。」は間違いで、「入出力機能は言語Cの範囲にない」が正確な記述じゃった。すまんかったのう。

[ メッセージ編集済み 編集者: ほろりん 編集日時 2005-01-15 11:09 ]
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2005-01-14 14:25
それです。そうなんです。
その類の「すんなり」で私は何度泣かされた事か・・・>Toshi氏

「変数には局所性がある」の法則通り、「たまたま」可も無く不可も無い
領域を Solaris は割り当て易いんでしょうかね。。。

>試しに struct tm *p_tm; の前に int i; を宣言したらsolarisでも
>なんとなく動いてしまいました。

やっぱ、この手のバグが顕在化するんは確率なんでしょうかね?


ブハハハハ、風情があって「いとおかし」ですな>ほろりん氏
ん〜、情報処理試験問題にすら "Call By Reference" と出てくる
様で、しかも確かに同じアドレスを仮と実で「参照」してるもん
やから何の違和感も無く Call By Reference と使い慣れてました
が、Java とか C++ には「本物の」 Call By Reference がある
んでしょうかね?

確かに、ポインターの指し先を間接的に持つ別のアドレスという
だけで、その間接アドレスも値の一つと言われればそうでしょうが、
Java とか C++ との違いがいまいちよく見えません。

「本物の Call By Reference」なるものがどういうものなのか・・・

[ メッセージ編集済み 編集者: コブラ 編集日時 2005-01-14 14:37 ]
Toshi
ベテラン
会議室デビュー日: 2003/09/12
投稿数: 62
投稿日時: 2005-01-14 14:35
引用:

ほろりんさんの書き込み (2005-01-14 14:08) より:

そうえば、引数にcharを二つ渡して、関数側でそれをintで受け取るなんて極悪なテクニックもあったのう。


va_arg なんていう醜悪な関数?もありますね。

関数は所詮他人、値をどう解釈するかは関数次第

[ メッセージ編集済み 編集者: Toshi 編集日時 2005-01-14 14:39 ]
どぅ
会議室デビュー日: 2005/01/13
投稿数: 4
投稿日時: 2005-01-14 15:28
ちょっと脇道にそれますが、とりあえず、C++の参照渡しについて参考まで。

コード:
//C++における参照渡し
void func(int& a ){  //この仮引数の宣言で参照渡しを宣言(?)する
  a++;
}

void hoge(void){
 int x = 0;

 func(x);//←これで参照渡ししてる。ここだけでは、値渡しとの区別ができない。そこが味噌。

 printf("%d\n", x);

}



関数hogeの結果、表示されるのは、0ではなくて、1です。funcには、xの値0が渡るのではなく、xへの参照が渡ります。なので、func側でxが書き換えられます。これが参照渡しです。Call By Reference。ポインタもアドレスも(表面上は)出てきません。
#ややこしいですが、普通の変数のみならず、ポインタ変数も参照渡しできると思います(多分)。
#その場合、ポインタ変数自身を書き換えることができるのでしょう。。

これは、Cでは実現できません。Cでfunc(x)と書いても、xの値は書き換わりません。ただし、ポインタを使って似たようなことを再現することはできます。

コード:
//Cにおける、アドレスとポインタを用いた、参照渡しのようなもの
void func(int *p_a ){//書き換えたい変数の型の「ポインタ」を宣言する
  (*p_a)++;
}

void hoge(void){
  int x = 0;

  func(&x);//←xのアドレスを値渡ししている。xの参照を渡していると解釈することもできる。

  printf("%d\n", x);
}



これで、1が表示されます。逆に言うと、Cではアドレスを明示的に渡してあげなければ、関数側に変数の値を書き換えさせることはできません。(返り値以外では)

以上参考まで。。細かい解説は省略です。。本筋の議論はまた後ほど。。
okutin
ベテラン
会議室デビュー日: 2003/12/11
投稿数: 98
お住まい・勤務地: 広島
投稿日時: 2005-01-14 15:29
こんにちは。

引用:

コブラさんの書き込み (2005-01-14 14:25) より:

「本物の Call By Reference」なるものがどういうものなのか・・・




本物かどうかはわかりませんが、例えば言語レベルで参照渡しをサポートしているかどうかを基準にしてみるというのもあると思います。
VisualBasicやPASCALなどはサポートしていますよね。VBですとデフォルトが参照渡しで「by val」を付けると値渡しでしたっけ。
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2005-01-14 15:45
どぅさんの、

コード:
void hoge(void){
  int x = 0;

  func(&x);

  printf("%d\n", x);
}



これは、もし x が 構造体ならば、構造体の先頭アドレスを渡してやる訳で、
time(); とか localtime_s(); の引数でも同じではないのですか?

ちょっとそこら辺は曖昧なもんで。
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2005-01-14 18:11
コード:

// 引数を参照として宣言
void func(int& a ){
a++; // 値のように使えるが("->"がいらない)、実際はポインタの先を書き換える
}

void hoge(void){
int x = 0;

func(x);// '&'をつけない。値渡しのように見えるが、ポインタが渡る
//func(NULL);// コンパイルエラー
//func(777);// コンパイルエラー

printf("%d\n", x);

}



これだけではなくて、C++の"参照引数(&)"には、
ポインタとの決定的な違いがあります。
『引数型の"実体のある変数"しか渡せない』という制限です。

引数がポインタ型なら、
func(NULL);
とか
func(777);//適当
なんてことが出来てしまいますが、
参照型引数には、なんらかの実体のある変数
を渡さないとコンパイルエラーになります。

参照先が不正なアドレスで落ちる、ということが防げるわけです。

[ メッセージ編集済み 編集者: ナキヲ 編集日時 2005-01-14 18:15 ]

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