2010-11-11 2 views
0

У меня есть следующая программа, которая рушится. Кто-нибудь знает, почему он рушится?Использование sizeof в C

/* writes a, b, c into dst 
** dst must have enough space for the result 
** assumes all 3 numbers are positive */ 
void concat3(char *dst, int a, int b, int c) { 
    sprintf(dst, "%08x%08x%08x", a, b, c); 
} 

/* usage */ 
int main(void) { 
    printf("The size of int is %d \n", sizeof(int)); 
    char n3[3 * sizeof(int) + 1]; 
    concat3(n3, 0xDEADFACE, 0xF00BA4, 42); 
    printf("result is 0x%s\n", n3); 
    return 0; 
} 
+7

Каждый раз, когда вы используете 'sprintf', я убить котенка. –

+0

Это C или C++?Это не тот же язык. –

+0

Этот вопрос является продолжением вопроса [Создание уникального номера] (http://stackoverflow.com/q/4143473/25324) на том же плакате. – pmg

ответ

13

Вы путаете размер двоичных данных (что sizeof) дает вам, с размером текстового представления в шестнадцатеричном, который является то, что вы пытаетесь сохранить.

В большинстве современных систем sizeof(int) оценивается в 4. Ваш буфер n3 будет способен хранить 13 символов (3 * 4 + 1 == 13).

Затем вы отформатируете три целых числа в шестисимвольный формат с шестью символами, для чего потребуется хранить 3 * 8 + 1 == 25 символов. В результате переполнение буфера вызывает сбой.

Должно быть очевидно, что размер типа данных int не имеет значения, когда вы форматируете его как текст (и сами указываете ширину поля!).

+0

не могли бы вы предложить? – Venkata

+1

12 + 1 bytes :-) – Konrad

+0

Чтобы сохранить строку «0xDEADFACE», вам понадобится 11 байт (10 для содержимого и 1 для нулевого символа). Это намного больше, чем байты, необходимые для хранения целого числа, которое для большинства систем составляет всего 4 байта. – Neil

0

Это сбой, потому что sizeof(int) (скорее всего, на вашей системе) 4, что означает, что n3 имеет длину 13 байт. Затем вы пытаетесь записать 8 + 8 + 8 = 24 символа.

1

Я действительно не понимаю, что sizeof что-то делать в вашем коде. В concat3 вы пытаетесь напечатать текстовое представление каждого предоставленного целого в качестве 8-символьной шестнадцатеричной строки: размер необходимого буфера должен быть равен 8 * 3 + 1 = 25, а sizeof(int) не имеет к этому никакого отношения.

Вы, кажется, смешиваете размер, занимаемый в памяти с помощью int, и длину его текстового представления (которое в вашем случае легко определяется, поскольку оно фиксируется вашей строкой формата sprintf).

На боковой ноте: sprintf - действительно опасная функция, которую вы должны считать устаревшей.

+0

Кто осудил 'sprintf'? Где? Когда? Я хочу плакать ... – pmg

+0

Успокойся, никто не сделал :) Я имел в виду, что 'sprintf', вероятно, не должен появляться во вновь написанном коде и что' snprintf' следует рассматривать. Разве вы не согласились бы на этот вопрос? – icecrime

+1

В принципе, да. Но на самом деле нет, не совсем. 'snprintf' не упрощает управление строкой, поскольку вам все равно нужно отслеживать длины строк и размер выделения. Использование 'snprintf' не предотвращает ошибок типа' char s [2];/* ... в подпрограмме далеко внизу ... */snprintf (s, 7, "foobar"); ' – pmg

0

Используйте snprintf вместо sprintf. Подумайте о котятах!

Но серьезно, вы не должны создавать интерфейсы с указателями буфера, но не иметь информации о длине. concat должен иметь параметр максимальной длины. Затем используйте snprintf внутри. Длина, предоставляемая concat, равна sizeof (n3).

Это все еще не сработает, но оно не сработает. Другие ответы объясняют, как правильно использовать функциональность.

(О, и не использовать получает() тоже. Просто потому, что в стандартной библиотеке не означает, что это хороший код.)

+0

Вы запутываете' puts' с 'gets'? В 'puts' нет ничего плохого. Это наиболее эффективный способ (с точки зрения сгенерированного размера кода и, вероятно, производительности) для печати постоянных строк текста, заканчивающихся в новой строке. Современный gcc даже заменяет 'printf' постоянным аргументом, не содержащим спецификаторов формата и заканчивающимся в новой строке вызовом' puts'. –

2

Попробуйте 3*2*sizeof(int)+1, где 2*sizeof(int) это число байтов требуется для печати каждого байтового значения int, в шестнадцатеричном формате. Конечно, поскольку вы используете этот формат %08X и ожидаете результатов с фиксированной шириной, вы действительно должны использовать uint32_t. Кстати, ваша программа также неправильно передает 0xDEADBEEF как int, что, вероятно, не подходит, и, таким образом, входит в область реализации, ориентированного на реализацию, преобразованного в подпись.

Вот версия с этими поправками:

#include <inttypes.h> 
#include <stdio.h> 

/* writes a, b, c into dst 
** dst must have enough space for the result 
** assumes all 3 numbers are positive */ 
void concat3(char *dst, uint32_t a, uint32_t b, uint32_t c) { 
    sprintf(dst, "%08"PRIX32"%08"PRIX32"%08"PRIX32, a, b, c); 
} 

/* usage */ 
int main(void) { 
    printf("The size of int is %d \n", sizeof(int)); 
    char n3[25]; 
    concat3(n3, 0xDEADFACE, 0xF00BA4, 42); 
    printf("result is 0x%s\n", n3); 
    return 0; 
} 
+1

Для строки формата 'sprintf', которую вы хотите' '% 08" PRIx32) ', а не' '% 08" PRIu32 "x" '. – pmg

+0

Дох, исправлено ..... –

Смежные вопросы