あなたは「罪深く」コーディングしています(遅かれ早かれあなたを傷つけるであろう多くの間違いを犯します-ほとんどの場合もっと早く)。まず、整数が正しいエンディアンであると想定しています。一部のマシンでは、間違っています-Intelマシン、PowerPCまたはSPARCマシンのいずれかです。
一般に、間違った結果が得られたと言うだけでなく、実際に得られた結果を表示する必要があります。また、期待される結果を表示する必要があります。それは人々があなたの期待をデバッグするのに役立ちます。
これが私の修正バージョンのコードです-入力を要求する代わりに、指定した値を想定しているだけです。
#include <stdio.h>
int main(void)
{
unsigned int c = 2249459722;
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
return(0);
}
Mac(Intel、リトルエンディアン)でコンパイルすると、出力は次のようになります。
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
私のSun(SPARC、ビッグエンディアン)でコンパイルすると、出力は次のようになります。
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 134.20.8.10
(SPARCでGCC 4.4.2を使用すると、警告が表示されます。
xx.c:4: warning: this decimal constant is unsigned only in ISO C90
MacでGCC4.2.1を使用する-多くの警告が有効になっている(gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Werror
)-その警告は表示されません。これは興味深いことです。)U
整数定数に接尾辞を追加することで、警告を削除できます。
問題を調べる別の方法は、次のコードと上記の非常に煩雑なコンパイラ設定で示されています。
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722;
printf("Direct operations:\n");
print_value(c);
printf("Indirect operations:\n");
if (sscanf("2249559722", "%d", &c) != 0)
printf("Conversion failed for %s\n", str);
else
print_value(c);
return(0);
}
これは(-Werror
設定のために)コンパイルに失敗し、次のメッセージが表示されます。
cc1: warnings being treated as errors
xx.c: In function ‘main’:
xx.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
設定を削除する-Werror
とコンパイルされますが、次に発生する問題が表示されます。失敗する可能性のある関数からのエラー表示をチェックしないという問題です。
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Conversion failed for 2249459722
基本的に、sscanf()
関数は文字列を符号付き整数に変換できなかったことを報告します(値が大きすぎて収まらないため、GCC 4.4.2の警告を参照)が、コードはからのエラーリターンをチェックしていなかったためsscanf()
、当時残っていた値を使用しc
ていました。
したがって、コードには複数の問題があります。
- 特定のアーキテクチャを想定しています(ビッグエンディアンも存在することを認識するのではなく、リトルエンディアン)。
- 多くの警告が有効になっているコンパイラを使用すると、正しくコンパイルされません-正当な理由があります。
- 失敗する可能性のある関数が実際に成功したかどうかはチェックされません。
Alokのコメント
はい、テストsscanf()
は間違っています。これが、コードレビューがある理由であり、テストしているコードを投稿するのに役立つ理由でもあります。
私は今少し戸惑っています-すぐには説明できない一貫した行動をとっています。明らかな改訂(MacOS X 10.6.2、GCC 4.2.1、32ビットおよび64ビットのコンパイルでのテスト)で、私はあまり正気ではない答えを1つ得ます。よりモジュール化して書き直すと、正気の答えが得られます。
+ cat yy.c
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722;
printf("Direct operations:\n");
print_value(c);
printf("Indirect operations:\n");
if (sscanf("2249559722", "%d", &c) != 1)
printf("Conversion failed for %s\n", str);
else
print_value(c);
return(0);
}
+ gcc -o yy.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
+ ./yy.32
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Integer value: 2249559722
Integer value: 0x86158EAA
Dotted decimal: 170.142.21.134
値170.142.21.134についての適切な説明がありません。しかし、現時点では、私のマシンでは一貫しています。
+ gcc -o yy.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
+ ./yy.64
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Integer value: 2249559722
Integer value: 0x86158EAA
Dotted decimal: 170.142.21.134
同じ値-32ビットではなく64ビットでも。おそらく問題は、私が未定義の振る舞いを説明しようとしていることです。これは、定義上、多かれ少なかれ説明できない(説明できない)ものです。
+ cat xx.c
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
static void scan_value(const char *str, const char *fmt, const char *tag)
{
unsigned int c;
printf("Indirect operations (%s):\n", tag);
fmt = "%d";
if (sscanf(str, fmt, &c) != 1)
printf("Conversion failed for %s (format %s \"%s\")\n", str, tag, fmt);
else
print_value(c);
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722U;
printf("Direct operations:\n");
print_value(c);
scan_value(str, "%d", "signed");
scan_value(str, "%u", "unsigned");
return(0);
}
このように関数の引数を使用すると、GCCは偽の形式を見つけることができなくなります。
+ gcc -o xx.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c
+ ./xx.32
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (signed):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (unsigned):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
結果はここで一貫しています。
+ gcc -o xx.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c
+ ./xx.64
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (signed):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (unsigned):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
そして、これらは32ビットの場合と同じです。私は公式に困惑しています。主な観察結果は正確なままです。注意して、コンパイラの警告に注意して(そしてコンパイラの警告を引き出して)、「すべての世界がIntelチップ上で実行されている」と想定しないでください(以前は「すべての世界がVAX」、昔々!)。