Вы кодируете 'греховно' (создавая ряд ошибки, которые рано или поздно навредят вам — чаще всего раньше). Во-первых, вы предполагаете, что целое число имеет правильный порядок следования байтов. На некоторых машинах вы ошибетесь — либо на машинах 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
(Используя GCC 4.4.2 на SPARC, я получаю предупреждение:
xx.c:4: warning: this decimal constant is unsigned only in ISO C90
Используя GCC 4.2.1 на Mac — с множеством включенных предупреждений (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
в то время.
Итак, в вашем коде есть несколько проблем:
- Он предполагает определенную архитектуру (с прямым порядком байтов, а не с признанием того, что существует и прямой порядок байтов).
- Он не компилируется чисто при использовании компилятора с большим количеством включенных предупреждений - по уважительной причине.
- Он не проверяет, что функции, которые могут дать сбой, на самом деле выполнены успешно.
Комментарий Алока
Да, тест на sscanf()
неверен. Вот почему у вас есть обзоры кода, а также почему это помогает публиковать код, который вы тестируете.
Теперь я немного озадачен - постоянное поведение, которое я не могу сразу объяснить. При очевидной доработке (тестирование на MacOS X 10.6.2, GCC 4.2.1, 32-битные и 64-битные компиляции) получаю один не очень вменяемый ответ. Когда переписываю более модульно, получаю вменяемый ответ.
+ 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
То же значение - даже в 64-битной версии вместо 32-битной. Может быть, проблема в том, что я пытаюсь объяснить поведение undefined, которое по определению более или менее необъяснимо (необъяснимо).
+ 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", когда-то давно!).
person
Jonathan Leffler
schedule
09.01.2010
2249459722
не обязательно вписываться вint
илиunsigned int
в этом отношении. Используйтеunsigned long
или, если он у вас есть,uint32_t
. В этом случае формат дляscanf()
становится"%lu"
/"%" SCNu32
. - person Alok Singhal   schedule 10.01.2010