掲示板
Powered by OTD
この記事は、K.Maebashi's BBSの過去ログです。
書き込む場合は、新しい掲示板へお願いします。
[日付順表示] | [日付順インデックス] | [スレッド順インデックス]


新規投稿 | 開設者ホームページへ戻る | ヘルプ


[510] Re:また質問です。
返信


投稿者: ishikawa
2003/12/07 12:42

Mail:   Link:
 どうもご丁寧にありがとうございます。


[509] Re:また質問です。
返信


投稿者: 本多
2003/12/07 09:35

Mail:manybook@msc.biglobe.ne.jp   Link:
 > > > 0000 0000 1011 0001 = 0x00B1 = 177 →
> > > 0000 0000 1011 0001 = 0x00B1 = -79
> > 177 を -79に変更しませんよ。
> なるほど、つまり「0000 0000 1011 0001 0x00B1」は
> signed型にしても177ということですね。

その通りです。

> 始めのビットに0がついてたら「+」として解釈するんですね・・、
> 俺はてっきり
>「0000 0000 →1011 0001」
> ここに1がついてるから「-」と解釈されるんだと思っておりました。

通常、符号bitは最上位bitを利用します。
数値の途中に符号bitがあったら、computerの設計も難しくなりますし、
それ以上に使用者にとって、使いにくいですよね。
0x00B1と0x0111の大小関係が単純比較できなくなりますから。

> えっと、他には俺のソースに間違いはなかったでしょうかね?
> (細かいとことかでもいいので)

細かいところでよろしいのであれば、

> printf("%d\n",w); /* 実行結果:-1 */
> printf("%u\n",w); /* 実行結果:4294967295 */

wはlong型なのですから、
printf("%ld\n",w);
printf("%lu\n",w);
にするべきです。

"%d"や"%u"のままでは環境がsizeof(int)=sizeof(long)である場合だけしか
うまく動作しません。

int:16bit, long:32bitだったら、printf("%d\n",w)としたら、
printf()関数は引数wの16bit分だけを切り出して表示しようとするので
うまく表示できない場合があります。

結論は正しいと思います。


[508] Re:また質問です。
返信


投稿者: ishikawa
2003/12/06 17:11

Mail:   Link:
 > > 訂正後:1011 0001 → 0000 0000 1011 0001になる。ですね。
> > 16進数で表すと0x001B、10進数で177になる。
> > %dによってsigned型に変換する。
> > 0000 0000 1011 0001 = 0x00B1 = 177 →
> > 0000 0000 1011 0001 = 0x00B1 = -79
> > これでも-79が出力のはず、おかしいです。なぜでしょう?
>
> 177 を -79に変更しませんよ。
> 桁が溢れてないのに、それが起こってしまったら数値演算できないでしょう?
>
> それに-79の2/16進数表記間違ってます。
> 1111 1111 1011 0001 = 0xFFB1 = -79

なるほど、つまり「0000 0000 1011 0001 0x00B1」はsigned型にしても177ということで
すね。
始めのビットに0がついてたら「+」として解釈するんですね・・、俺はてっきり「0000
0000 →1011 0001」ここに1がついてるから「-」と解釈されるんだと思っておりまし
た。よく考えたら前者の考え方の方が普通ですよね、 またもや勘違いです・・。
えっと、他には俺のソースに間違いはなかったでしょうかね?(細かいとことかでもいい
ので)


[507] Re:また質問です。
返信


投稿者: 本多
2003/12/06 12:06

Mail:manybook@msc.biglobe.ne.jp   Link:
 > 訂正後:1011 0001 → 0000 0000 1011 0001になる。ですね。
> 16進数で表すと0x001B、10進数で177になる。
> %dによってsigned型に変換する。
> 0000 0000 1011 0001 = 0x00B1 = 177 →
> 0000 0000 1011 0001 = 0x00B1 = -79
> これでも-79が出力のはず、おかしいです。なぜでしょう?

177 を -79に変更しませんよ。
桁が溢れてないのに、それが起こってしまったら数値演算できないでしょう?

それに-79の2/16進数表記間違ってます。
1111 1111 1011 0001 = 0xFFB1 = -79



[506] Re:また質問です。
返信


投稿者: ishikawa
2003/12/06 00:03

Mail:   Link:
 やはりちょっとなんか分かりませんね、、一応まとめてみたんですけどどこが間違って
いるんでしょうか?ちょっと雑ですがご指摘ください。意味の分からないところあった
も言ってください。
#include <stdio.h>
int main(void)
{
unsigned char a = 0xb1; /*この時点でaは177
2進数で表したら1011 0001
実際には2進数として変数に格納している。*/

unsigned long w = 0xFFFFFFFF; /*この時点でwは4294967295
2進数で表したら
1111 1111 1111 1111 1111 1111 1111 1111 */

printf("%d\n",a); /* 実行結果:177 */ /*汎整数拡張によりaの値がint型に変換
1011 0001 → 1111 1111 1011 0001になる
(あっ、たぶんここおかしいですね・・、、
ここ間違いですね?つまりsigned型の場合は
1011 0001の先頭が1だから残りのビットに1を
入れて拡張してしまう。しかしunsigned型の場合
                  は、先頭が1でも負数とみなさないので
残りのビットに0を入れて拡張する。)
訂正後:1011 0001 → 0000 0000 1011 0001に
                  なる。ですね。
    16進数で表すと0x001B、10進数で177になる。
    %dによってsigned型に変換する。
0000 0000 1011 0001 = 0x00B1 = 177 →
0000 0000 1011 0001 = 0x00B1 = -79
これでも-79が出力のはず、おかしい                       です。なぜでしょう?
(訂正前:16進数で表すと0xFFB、10進数で65457
                  になる。
                  %dによってsigned型に変換する。
    1111 1111 1011 0001 = 0xFFB1 = 65457 →
1111 1111 1011 0001 = 0xFFB1 = -79
    よって-79が出力のはず・・?)*/


printf("%u\n",a); /* 実行結果:177 */ /* 汎整数拡張によりaの値がint型に変換
1011 0001 → 0000 0000 1011 0001になる
16進数で表すと0x00B1、10進数で177になる。
%uによってunsigned型に変換する。
0000 0000 1011 0001 = 0x00B1 = 177 →
もとからunsignd型なので変換の必要性なし。
よって177が出力。正解。 */

printf("%d\n",w); /* 実行結果:-1 */  /*long型なので汎整数拡張はおきない。
       %dによってsigned型に変換する。
1111 1111 1111 1111 1111 1111 1111 1111 = 4294967295 → 
1111 1111 1111 1111 1111 1111 1111 1111 = -1になる。
         で、-1を出力。正解。 */ 

printf("%u\n",w); /* 実行結果:4294967295 */ /*long型なので汎整数拡張はおきない
     %uによってunsigned型に変換する。
 しかし元からunsigned型な訳だから
                        変換の必要がない。
というわけで4294967295のまま出力。                       正解。 */ 

return(0);
}


[505] Re:また質問です。
返信


投稿者: 本多
2003/12/05 16:33

Mail:manybook@msc.biglobe.ne.jp   Link:
 回答ありがとうございました。
つまり、64bit整数をstackに積み重ねるときの上位32bitと下位32bitの順序も
big endianとlittle endianで異なるんですね。

big endian: <上位32bit> <下位32bit>
little endian: <下位32bit> <上位32bit>

> 完全 64bit 処理系であって long long と int が同じ大きさ (64bit) の場合は、
> 上記のようなことは起こらずうまく動きます。
long longとintが同じ大きさになるような処理系って、
出てくることはないと思うんですけど、そういうのって予定されてましたっけ?
intとlong longがそろって64bitじゃ、
32bitを扱う整数型がなくなっちゃいますよね?困らないかな?
それにlongの居場所もないですし。

# longとlong longが共に64 bitになる処理系はありますが。
# longを64bitにするなら、long longは128 bitにして欲しいなぁ


[504] Re:また質問です。
返信


投稿者: 774RR
2003/12/05 15:45

Mail:   Link:
 あ、うそ発見。

> スタックには 61 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 が積まれています。
は 61 00 00 00 00 00 00 00 41 00 00 00 の誤り(結論は変わらず)

> スタックには 00 00 00 00 00 00 00 61 00 00 00 00 00 00 00 41 が積まれています。
は 00 00 00 00 00 00 00 61 00 00 00 41 の誤り(結論は変わらず)



[503] Re:また質問です。
返信


投稿者: 774RR
2003/12/05 14:53

Mail:   Link:
 アーキテクチャにバリバリ依存になるので話を限定します。
long long ll=0x61; (64bit) であり int=long(=32bit) とします。
ウチの hpux11.00 (pa-risc 2.0w) ではこの状況です。

んで、問題をわかりやすくするため
printf("<%c><%c>", ll, 'A'); とします。

まず little-endian の場合
スタックには 61 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 が積まれています。
最初の %c は vaarg を使って int を取り出そうとします。
61000000 を取り出し 'a' が表示されます。
次の %c も同じく int を取り出そうとして
00000000 を取り出し (おそらく) 空白文字が表示されます。
'A' は出力されません。

次に big-endian の場合
スタックには 00 00 00 00 00 00 00 61 00 00 00 00 00 00 00 41 が積まれています。
最初の %c は vaarg を使って int を取り出そうとします。
00000000 を取り出し (おそらく) 空白文字が表示されます。
次の %c も同じく int を取り出そうとします。
00000061 を取り出し 'a' が表示されます。
'A' は出力されません。

完全 64bit 処理系であって long long と int が同じ大きさ (64bit) の場合は、
上記のようなことは起こらずうまく動きます。

本多さんも難しく考えすぎかと。


[502] Re:また質問です。
返信


投稿者: 本多
2003/12/05 14:19

Mail:manybook@msc.biglobe.ne.jp   Link:
 ごめんなさい。私はここらへんの理解が怪しいというか、
わかってないので、教えていただけますか?

> > long long ll= 0x6100000000;
> > printf("ll=%c\n",ll);
> big-endian ならそうですね。 IA64 だと多分×。

1点目:
えっと、32bitを超える部分の処理ってendianに関係あるんでしたっけ?
8bitの配列を、32bitの整数型で見たときの位置関係のことだと思っていたのですが。
32bitを越える整数型は単純に32bit整数を2つ重ねた感じになると思っていたのですが
重ねられる順序が変わっちゃうんですか?

変わるとしたら、
long long ll=0x61;
printf("ll=%c\n",ll);
で、正しく表示されるのでしょうか。
それとも、全く別でしょうか?

2点目:
IA64に限定していらっしゃるようですが、
IA64ってintelの新しい64bit Architecture CPUですよね?

認識が間違っていたら指摘していただきたいのですが、
address空間が64bitまで拡張されるだけで、
64bitの整数型の計算はPentiumでも可能だと思っていたのですが、
違いますか?(もしかして、できないのかしら?)

できると仮定したとき、PentiumとIA64でprintf()の動きが異なるんですか?


[501] Re:また質問です。
返信


投稿者: 774RR
2003/12/05 12:59

Mail:   Link:
 >  あぁ、そうか、stackに積み上げられた段階で型情報がなくなるので
> printf()はlong longで与えた数値(私の環境で64bit)のうちの
> int分(32bit分)だけ切り取って、表示しようとしてるんですね。
ここは御意。

> long long ll= 0x6100000000;
> printf("ll=%c\n",ll);
big-endian ならそうですね。 IA64 だと多分×。

%c は (暗黙の汎整数拡張後の) u/int 型を要求するので、それに
long や long long を渡すのはそもそも誤りです。
# sizeof(int)==sizeof(long) でも誤り。動くけど。
# 今規格書が手元に無いので未規定か未定義かは不明。

> えぇ。思いっきり馬鹿にして笑ってやってください...
そこまで自虐にならんでも...


[500] Re:また質問です。
返信


投稿者: 本多
2003/12/05 12:27

Mail:manybook@msc.biglobe.ne.jp   Link:
 > >  printf("%c %c %c %c %c\n", c, s, l, i, ll);
> > を実行すれば、どれも「いわゆる半角のア」を表示するはずです。
> ダウト。というか、うそ。
あらら? 確認せずにちょちょいと、書いたら嘘書いちゃってましたか。
いやいやお恥ずかしい *^_^*

えっと、例えば、
long long ll= 0x61;
long l = 0x61;
int i = 0x61;
short s = 0x61;
char c = 0x61;

printf("ll=%c\n",ll);
printf("l =%c\n",l );
printf("i =%c\n",i );
printf("s =%c\n",s );
printf("c =%c\n",c );

を実行してみると、あら、本当だ。
long〜charはちゃんと'a'が表示されますが、
long longは表示されないですね。

あぁ、そうか、stackに積み上げられた段階で型情報がなくなるので
printf()はlong longで与えた数値(私の環境で64bit)のうちの
int分(32bit分)だけ切り取って、表示しようとしてるんですね。

いや、失礼しました。
longも今どきのcomputerはだいたいintと同じ大きさなのですが、
過去や将来においてintとsizeがずれた場合は同様ですね。

例えば、以下の様にやると、全部'a'が表示できますね。

long long ll= 0x6100000000;
printf("ll=%c\n",ll);
いや〜、恥ずかしい *^_^*

> >「おいおい、それは不定な動作の部分が、たまたまうまく行く例じゃんか」
> > とか思ってニヤニヤしてくれればいいわけで(^^)
> この例のことですかw
えぇ。思いっきり馬鹿にして笑ってやってください...
恥ずかしー(*^_^*)


[499] Re:また質問です。
返信


投稿者: 774RR
2003/12/05 10:40

Mail:   Link:
 ishikawa さんは難しく考えすぎなのでしょう。
問題を分離して考える必要があります。

簡単のため2の補数、 int=16bit としておきます。

1.unsigned/signed はなぜあるか。
(メモリ上の)値をどう取り扱うかを決めるためにあります。
printf() の話とは全く関係ありません。

0xffff を unsigned では 65535 に
0xffff を signed では -1 に
みなします。そのため 0xffff*0xffff を演算すると
unsigned : 65535*65535 = 4294836225 (OVERFLOW!)
signed : (-1)*(-1) = 1 (valid)
となります。結果は全く異なります。
# signed と unsigned を大小比較するのは話が別です。

2.暗黙の汎整数変換
printf() など可変個引数をもつ関数に値を渡すときは
[signed/unsigned] char/short 型は「暗黙の汎整数変換」によって
signed int 型に変換されます。
# 他の型と演算されるときは 494 の規則によって変換されます。

unsigned char は [0--255] の値を保持できる型なので、
これが汎整数変換されるときにはやはり [0--255] の値になるように
変換が行われます。
unsigned char の 0xb1 は 177 なので int の値として 177 になるよう
0x00b1 に変換がされます。

signed char は [-128--127] の値を保持できる型なので、
汎整数変換されるときには [-128--127] の値にする変換がされます。
signed char の 0xb1 は -79 なので int の値として -79 になるよう
0xffb1 に変換がされます。

3. printf() の %c の動作
%c で1文字表示する場合でも 2. により必ず int 型を受け取ることになります。
[signed/unsigned] char c=0xb1;
printf("%c", c);
これは暗黙のうちに printf("%c", (int)c); になるのです。
規則 2 によって2番目の引数は 0x00b1 または 0xffb1 となります。
しかし %c は与えられた引数の下位8ビットだけを取り扱うため
0x00b1 でも 0xffb1 でも表示は b1 の部分だけ見て行われます。

4. printf() の %d %u の動作
printf("%d", c); /* 上記の c */
の場合でも printf("%d", (int)c); となるためこれは正しいコードです。
%d は signed int を %u は unsigned int を表示するものです。
0x00b1 が渡されたら signed/unsigned ともに値は 177
0xffb1 が渡されたら signed なら -79 unsigned なら 65457
となります。

C++ の stream は「型」を知っているのでその辺が簡単になります。


[498] Re:また質問です。
返信


投稿者: 774RR
2003/12/05 10:14

Mail:   Link:
 >  printf("%c %c %c %c %c\n", c, s, l, i, ll);
> を実行すれば、どれも「いわゆる半角のア」を表示するはずです。
ダウト。というか、うそ。

>「おいおい、それは不定な動作の部分が、たまたまうまく行く例じゃんか」
> とか思ってニヤニヤしてくれればいいわけで(^^)
この例のことですかw


[497] Re:また質問です。
返信


投稿者: 本多
2003/12/05 09:59

Mail:manybook@msc.biglobe.ne.jp   Link:
 > > 二人は理解しあえているようですが、ここでいう%sにするってどういう意味ですか?
> > # もしかして、例題を変えるんじゃなくて、
> > # もっと単純にprintf("%s\n","入");を考えていらっしゃいます?
> 僕は単純にprintf("%s\n","入");でやりました。。

なーんだ、やっぱり、二人は理解して、いらっしゃったんですね(^^;)
...私だけなんですね。「%sに変える」という説明に引っかかっていたのは。

> 本多さんのやり方でもちゃんと'入’と表示されました。
> (僕もこのやり方は仕組みがよく分かりません)

「僕も」と言われると、えっと、それはちょっと違うとか言いたくなるんですけど。
それはさておき。

私の提示した例は、C言語ではルール違反をして表示しているので忘れてください。
えっと、わかる人だけ
「おいおい、それは不定な動作の部分が、たまたまうまく行く例じゃんか」
とか思ってニヤニヤしてくれればいいわけで(^^)


[496] Re:また質問です。
返信


投稿者: 本多
2003/12/05 09:51

Mail:manybook@msc.biglobe.ne.jp   Link:
 > char型っていうのは文字コードを入れた時点で、
>「この文字だ」って言うのは決めてるんですかね。。?
char型って言う名称のせいで文字との関係を考えてしまうかもしれませんが、
char型って言うのは
「1 byte分しか容量のない整数を格納するための型」と思ってください。
int型と同じだけどsizeがshortより更に小さい奴と思っていいです。

で、数値→文字に変換するのは文字コードとprintf()の仕事で
char型とは関係ないんです。

0xb1という数値が「いわゆる半角文字のア」として表示するのは
そういう文字コードを使っているsystem上では
printf()がそういう数値を「いわゆる半角のア」として出力するからで
char型でもshort型でもint型でもlong型でも(long long型でも)
関係ないんです。

char c = 0xb1;
short s = 0xb1;
int i = 0xb1;
long l = 0xb1;
long long ll = 0xb1;

printf("%c %c %c %c %c\n", c, s, l, i, ll);

を実行すれば、どれも「いわゆる半角のア」を表示するはずです。


[495] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/05 02:25

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > unsigned char c = 0xFF;
> int i = -10;
>
> printf("%d\n", c + i);
>
> この場合、cはsigned intの255として扱われ、この表示結果は245になります。

ああいけない、この例は不適切ですね。iがunsignedに変換されて0xFFFFFFF6に
なっていたとしても筋は通ってしまうので。

printf("%d\n", c < i);

これならいいのか。


[494] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/05 01:48

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > と言うことはsigned型とunsigned型の計算だったら両方unsigned型としての計算になるっ
> てことでいいんですよね?

これは込み入っているので朝には返答できませんでした。
整数同士の演算について、規格書から該当部分を引用します(6.2.1.5)。

・一方のオペランドが型unsigned long intをもつ場合、他方のオペランドを
 unsigned long intに型変換する。
・そうでない場合、一方のオペランドが型long intをもち、他方のオペランドが
 型unsigned intをもち、更に、long intがunsigned intのすべての値を
 表現できるなら、型unsigned intのオペランドをlong intに型変換する。
 long intがunsigned intのすべての値を表現できないなら、両オペランドを
 unsigned long intに型変換する。
・そうでない場合、一方のオペランドが型long intをもつならば、他方の
 オペランドをlong intに型変換する。
・そうでない場合、一方のオペランドが型unsigned intをもつならば、
 他方のオペランドをunsigned intに型変換する。
・そうでない場合、両オペランドは型intをもつ。

ふたつめの規則があるので、「signed型とunsigned型の計算だったら両方
unsigned型としての計算になる」とも言えないわけです。
intよりlongの方が大きな処理系なら、longとunsigned intの間の計算は
signedの方のlongで行われます。

また、汎整数拡張でcharやshortがintに変換されるときも、
「unsignedのcharやshortのとりうる値がすべて、signedのintで表現できるなら、
 signed intに拡張する」という規則で変換されますから、

unsigned char c = 0xFF;
int i = -10;

printf("%d\n", c + i);

この場合、cはsigned intの255として扱われ、この表示結果は245になります。

ぶっちゃけて言えば、「できるだけ値を保存する方向で計算する」ということですが…

このあたりの規則は何かとややこしいので、
「特に理由がなければunsignedなんか使わない」
と決めてしまった方が苦労がないように思います (^^;



[493] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/05 00:51

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > しかしまたなんか新たな謎がでてきました・・・。

うーん、どこが疑問なのかを書いていただかないと…

> unsigned char a = 0xb1;

これはunsigned char a = 177;と同義ですし、unsigned charは255までは
表現できるわけで、普通にaを177で初期化しただけですよね?

> printf("%d\n",a); /* 実行結果:177 */
> printf("%u\n",a); /* 実行結果:177 */

で、177は、intに拡張されてもやっぱり177なので、普通に177が表示されています。
変な動作はないように思うのですが。

signedのcharなら、ビットパターン0xb1は-79です。これがintに拡張されたら
-79ですが、unsignedのcharですから177が表示されます。


[492] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/05 00:33

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > # もっと単純にprintf("%s\n","入");を考えていらっしゃいます?

私はこれを考えていました。元質問に

> あと、漢字って出力できないですよね?

とあったので、printf("%s\n","入");とすれば出力できるじゃないか、
という意味で。


[491] Re:また質問です。
返信


投稿者: ishikawa
2003/12/05 00:03

Mail:   Link:
  > char a=0xb1;                         /* a=-79と同じ                    */
> printf("a=0x%x\n",a); /* 0xfffffffb1が表示されます (A) */
> printf("a=0x%x\n",(unsigned char)a); /* 0xb1が表示されます (B) */
>
> char型は、関数の引数に渡された段階で型が繰り上がりint型になります。
> char型で 0xb1=-79ですから、これをint型にすると-79=0xffffffb1になります。
> これを16進数で表示するのですから、(A)では0xffffffb1が表示されます。
>
> 次にchar型をunsigned char型に変換すると0xb1=-79は0xb1=177に変換されます。
> unsigned char型で0xb1=177がint型に繰り上がっても0xb1=177です。
> ですから、(B)では0xb1が表示されます。

ありがとうございます、ちゃんと表示されました。
しかしまたなんか新たな謎がでてきました・・・。
#include <stdio.h>
int main(void)
{
unsigned char a = 0xb1;
unsigned long w = 0xFFFFFFFFF;

printf("%d\n",a); /* 実行結果:177 */
printf("%u\n",a); /* 実行結果:177 */

printf("%d\n",w); /* 実行結果:-1 */
printf("%u\n",w); /* 実行結果:4294967295 */

return(0);
}
long型の方は納得できるんです、でもchar型の方がいまいち・・・
char型っていうのは文字コードを入れた時点で、「この文字だ」って言うのは決めてるんですかね。。?


[490] Re:また質問です。
返信


投稿者: ishikawa
2003/12/04 23:49

Mail:   Link:
 
> 二人は理解しあえているようですが、ここでいう%sにするってどういう意味ですか?

> # もしかして、例題を変えるんじゃなくて、
> # もっと単純にprintf("%s\n","入");を考えていらっしゃいます?

僕は単純にprintf("%s\n","入");でやりました。。
本多さんのやり方でもちゃんと'入’と表示されました。(僕もこのやり方は仕組みがよく分かりません)


[489] Re:また質問です。
返信


投稿者: ishikawa
2003/12/04 23:44

Mail:   Link:
 > いいえ。
> Cでは、式の中では、「intより小さい整数型はintまで拡張される」という
> 汎整数拡張と呼ばれる変換が起こります。この拡張はintまでで、longは別扱いです。
> だからprintf()でlong型を表示しようと思ったら%ldを使います。
>
> %cの場合「文字」を渡しますが、%sの場合「文字列」を渡します。
> printf()は1バイト文字を想定しているので、%cなら上位がちょん切られますが、
> %sなら、2バイト文字は単に「2文字」と解釈されるわけです。

なるほど、なんか俺ちょっと勘違いしてました。
printf()の中で書式指定(引数で書いた)によって換えられるということですね。。。
ありがとうございます。


[488] Re:また質問です。
返信


投稿者: 本多
2003/12/04 12:37

Mail:manybook@msc.biglobe.ne.jp   Link:
 > > %sでよければ簡単と書いてありますが、上に書いてある通りPrintf()側で上位ビットをち
> > ょん切ってあるんだったら%sで表現しようとしても2バイトしか読み取れないからまちが
> > った文字が表示されると思うんですが、どうでしょうか?(でも実際には表示されまし
> > た)
> %cの場合「文字」を渡しますが、%sの場合「文字列」を渡します。
> printf()は1バイト文字を想定しているので、%cなら上位がちょん切られますが、
> %sなら、2バイト文字は単に「2文字」と解釈されるわけです。

二人は理解しあえているようですが、ここでいう%sにするってどういう意味ですか?

#include <stdio.h>
int main(void)
{
int a,b;

a = '入';
printf("%X\n",a);
b = 0xFC93;
printf("%c\n",b); /* (1) */

return(0);
}
(1)をprintf("%s\n",b)にしたら、
0xfc93というaddressに文字列は(おそらく)格納されていませんから、
もちろんダメですよね。

正しく表示されたということは、printf("%s\n",&b)とかやって、
bという変数の次の領域に0が入っていたってことなのでしょうか。

ここって常に0なんでしたっけ?
stack領域ですよね(a,bの次の領域って どっち方向だったかな?)。

printf()が呼ばれた段階で、printf()の戻り位置情報かmain()の戻り位置情報が
入ってるから非0の可能性がすごく高いような気がしているのですが
普通"入"だけが表示されるものなのでしょうか。
(私のsystemでは「入」だけ表示されましたが)

# もしかして、例題を変えるんじゃなくて、
# もっと単純にprintf("%s\n","入");を考えていらっしゃいます?


[487] Re:また質問です。
返信


投稿者: 本多
2003/12/04 11:51

Mail:manybook@msc.biglobe.ne.jp   Link:
 > >キャストしてからunsigned int型の変数に格納しなおすなどすれば、
> >0xb1と表示されるはずです。

> これはいまいちやりかたが分からないんです・・・。
> 書式指定で「unsigned型で16進数表示」って言うのがないから
> 無理なんじゃないでしょうか?

ごめんなさい。書き方が正確ではなかったですね。
しかも丁寧に説明してなかったのでわかりにくかったです。
すいません。

以下の様にやります。

char a=0xb1; /* a=-79と同じ */
printf("a=0x%x\n",a); /* 0xfffffffb1が表示されます (A) */
printf("a=0x%x\n",(unsigned char)a); /* 0xb1が表示されます (B) */

char型は、関数の引数に渡された段階で型が繰り上がりint型になります。
char型で 0xb1=-79ですから、これをint型にすると-79=0xffffffb1になります。
これを16進数で表示するのですから、(A)では0xffffffb1が表示されます。

次にchar型をunsigned char型に変換すると0xb1=-79は0xb1=177に変換されます。
unsigned char型で0xb1=177がint型に繰り上がっても0xb1=177です。
ですから、(B)では0xb1が表示されます。



[486] Re:strtol
返信


投稿者: tos
2003/12/04 10:05

Mail:   Link:
 774RRさん、本多さんありがとうございます。

char *では確かに774RRさん、本多さんの言われるように正常に動作しませんね。
(だめだめですね。もう一度勉強しなおします)



[485] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/04 08:24

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > えーと些細なことかもしれませんが、printf()の引数にlong型を受け取った場合、上の文
> 章通りでint型が2バイトだったらlongが表現できないってことになりますよね?
> 上の文章はlong型に変換ってことでいいんでしょうかね?

いいえ。
Cでは、式の中では、「intより小さい整数型はintまで拡張される」という
汎整数拡張と呼ばれる変換が起こります。この拡張はintまでで、longは別扱いです。
だからprintf()でlong型を表示しようと思ったら%ldを使います。

> %sでよければ簡単と書いてありますが、上に書いてある通りPrintf()側で上位ビットをち
> ょん切ってあるんだったら%sで表現しようとしても2バイトしか読み取れないからまちが
> った文字が表示されると思うんですが、どうでしょうか?(でも実際には表示されまし
> た)

%cの場合「文字」を渡しますが、%sの場合「文字列」を渡します。
printf()は1バイト文字を想定しているので、%cなら上位がちょん切られますが、
%sなら、2バイト文字は単に「2文字」と解釈されるわけです。


[484] Re:また質問です。
返信


投稿者: ishikawa
2003/12/04 02:11

Mail:   Link:
 (ぱ)さん、本多さんレスありがとうございます。

> > あと・・、「ア」という文字コードはB1だと思いますが%xで表示したらなぜFFFFFFB1
とな
> > るんでしょうか?謎です。
>
> 本多さんからレスがありましたが、 [intに変換] されているからです。
> このとき、0xB1が負の数として認識されているので、上で説明したように
> 負の数のまま拡張するために頭にFFFFFFが補われています。

えーと些細なことかもしれませんが、printf()の引数にlong型を受け取った場合、上の文
章通りでint型が2バイトだったらlongが表現できないってことになりますよね?
上の文章はlong型に変換ってことでいいんでしょうかね?

> > 出力する時「FFFFFF」の部分は数字を変えてもB1さえ最後に入
> > っていれば「ア」と表示されます。(例えば「0x111111B1」でも表示される)
>
> これは、printf()の%cで表示しようとしていますね?
> printf()側で、いったんcharに代入するとかして、上位ビットを
> ちょん切っちゃってるわけでしょう。
>
> > あと、漢字って出力できないですよね?
>
> %sでよければ簡単だと思うんですが。
> >FC93
> >・
> >とでました、なぜなんでしょう?
>
> 上の理由により、0xFC93を%cで表示させるのは、0x93を表示させるのと同じことです。
> で、0x93は、Shift JISコードでは2バイト文字の1バイト目ですから、
> DOS窓側で困って「・」を表示しているのでしょう。

%sでよければ簡単と書いてありますが、上に書いてある通りPrintf()側で上位ビットをち
ょん切ってあるんだったら%sで表現しようとしても2バイトしか読み取れないからまちが
った文字が表示されると思うんですが、どうでしょうか?(でも実際には表示されまし
た)

本多さん
>キャストしてからunsigned int型の変数に格納しなおすなどすれば、
>0xb1と表示されるはずです。

これはいまいちやりかたが分からないんです・・・。
書式指定で「unsigned型で16進数表示」って言うのがないから無理なんじゃないでしょう
か?

それと、

#include <stdio.h>

int main(void)
{
long z;
/*unsigned*/ long zz; /* signed型とunsigned型だったら両方unsigned型と
                 して計算する?*/

zz = 1000000000;
z = 3000000000;

printf("%d\n",z+zz);

if(z+zz > 1000000000)
printf("aaaaaaaaaaaaaaa\n");

return(0);
}
変数zzは1000000000だから符合ありでもなしでもどっちでもいいはずです。
しかし実行結果が違います。
と言うことはsigned型とunsigned型の計算だったら両方unsigned型としての計算になるっ
てことでいいんですよね?





[483] Re:また質問です。
返信


投稿者: (ぱ)
2003/12/04 00:06

Mail:PXU00211@nifty.ne.jp   Link:http://member.nifty.ne.jp/maebashi/
 > それと、unsigned型とsigned型ってなんであるんでしょうかね?

思うにishikawaさんは、

 2の補数表現なら、正の値に負の値を加算すればちょうど減算になるわけだから、
 unsignedとsignedを特に区別しなくても問題ないのではないか。

ということをおっしゃってるのだと思います。

でも、0xFFFFFFFFと1を比較したら0xFFFFFFFFの方が大きいですが、
-1と1なら1の方が大きくなければいけませんし、
signedのchar型変数に-1(ビットパターンは0xFF)が入っていた場合、
それをsigned intに代入したらビットパターンは0xFFFFFFFFにならなければ
いけません。

> (すいません一応聞きますけど、数字は一回内部で2進数に直してから
> 書式指定にしたがって出力するってことでいいんですよね?)

メモリに保持されるときはどうせ2進数だ、という意味ではそうですね。

> そういや前の文章で0xFFFFFFFFはオーバーフローを起こして-1になるって書きましたけ
> ど、これってオーバーフローじゃないですよね、1111 1111 1111 1111 1111 1111 1111
> 1111は「2の補数表現」ですよね。%dだから2の補数としてよんでいるんですね。タブ
> ンそうですね?

そうです。ただ細かいことを言えば、Cの規格は、負の数の表現形式として
2の補数表現を使うことを強制しているわけではないですし、intが32ビットとも
限りませんけれども。

> あと・・、「ア」という文字コードはB1だと思いますが%xで表示したらなぜFFFFFFB1とな
> るんでしょうか?謎です。

本多さんからレスがありましたが、intに変換されているからです。
このとき、0xB1が負の数として認識されているので、上で説明したように
負の数のまま拡張するために頭にFFFFFFが補われています。

> 出力する時「FFFFFF」の部分は数字を変えてもB1さえ最後に入
> っていれば「ア」と表示されます。(例えば「0x111111B1」でも表示される)

これは、printf()の%cで表示しようとしていますね?
printf()側で、いったんcharに代入するとかして、上位ビットを
ちょん切っちゃってるわけでしょう。

> あと、漢字って出力できないですよね?

%sでよければ簡単だと思うんですが。

>FC93
>・
>とでました、なぜなんでしょう?

上の理由により、0xFC93を%cで表示させるのは、0x93を表示させるのと同じことです。
で、0x93は、Shift JISコードでは2バイト文字の1バイト目ですから、
DOS窓側で困って「・」を表示しているのでしょう。


[482] Re:strtol
返信


投稿者: 本多
2003/12/03 12:50

Mail:manybook@msc.biglobe.ne.jp   Link:
 > 素朴な疑問なのでが、strtol()が要求する第2引数は、
> 何故、char **なのでしょうか?
> 変換できなかった文字列を格納するなら、char *で
> いいような気がするのですが。

もし、第2引数がchar *だったら、pの指す所の値は変更できますが、
p自身は変更できないですよね?

strtol()は以下の様に使いますが、
char str = "12345abcef";
char *p = NULL;
long l;

l = strtol( str, &p, 0);

関数が終わった時点でpはaのaddressを指していて欲しいわけです。
pの指す先(この場合はNULLの位置)に、
"abcdef~を代入したいわけじゃないんです。

だから、char **なんですね。



[481] Re:strtol
返信


投稿者: 774RR
2003/12/03 12:48

Mail:   Link:
 int x(char *p) { p=malloc(1); }
では p に値を返せないのと同じ。


ひとつ前の過去ログ | 目次へ | ひとつ後の過去ログ