Почему memset () неправильно инициализирует int? - PullRequest
24 голосов
/ 18 августа 2011

Почему вывод следующей программы 84215045?

int grid[110];
int main()
{
    memset(grid, 5, 100 * sizeof(int));
    printf("%d", grid[0]);
    return 0;
}

Ответы [ 9 ]

44 голосов
/ 18 августа 2011

memset устанавливает каждый байт буфера назначения в указанное значение. В вашей системе int - это четыре байта, каждый из которых равен 5 после вызова memset. Таким образом, grid[0] имеет значение 0x05050505 (шестнадцатеричное), которое равно 84215045 в десятичном виде.

Некоторые платформы предоставляют альтернативные API для memset, которые записывают более широкие шаблоны в буфер назначения; например, в OS X или iOS вы можете использовать:

int pattern = 5;
memset_pattern4(grid, &pattern, sizeof grid);

чтобы получить поведение, которое вы, кажется, ожидаете. На какую платформу вы ориентируетесь?

В C ++ вы должны просто использовать std::fill_n:

std::fill_n(grid, 100, 5);
12 голосов
/ 18 августа 2011
memset(grid, 5, 100 * sizeof(int));

Вы устанавливаете 400 байтов, начиная с (char*)grid и заканчивая (char*)grid + (100 * sizeof(int)), значением 5 (здесь необходимо преобразование, потому что memset имеет дело с байтами , тогда как арифметика указателя предложения в objects.

84215045 в гексе - 0x05050505; поскольку int (на вашей платформе / компиляторе и т. д.) представлен четырьмя байтами, при его печати вы получите «четыре пятерки».

8 голосов
/ 18 августа 2011

memset - это установка байтов, а не значений.Один из многих способов установить значения массива в C ++ - std::fill_n:

std::fill_n(grid, 100, 5);
6 голосов
/ 18 августа 2011

Не использовать memset.

Вы устанавливаете каждый байт [] памяти на значение 5. Каждое int имеет длину 4 байта [5][5][5][5], которое компиляторправильно интерпретируется как 5 * 256 * 256 * 256 + 5 * 256 * 256 + 5 * 256 + 5 = 84215045. Вместо этого используйте цикл for, который также не требует sizeof ().В общем, sizeof () означает, что вы делаете что-то нелегко.

for(int i=0; i<110; ++i)
    grid[i] = 5;
3 голосов
/ 18 августа 2011

Ну, memset записывает байты с выбранным значением. Поэтому int будет выглядеть примерно так:

00000101 00000101 00000101 00000101

Что интерпретируется как 84215045.

2 голосов
/ 18 августа 2011

Вы на самом деле не сказали, что вы хотите, чтобы ваша программа делала.

Предполагая, что вы хотите установить для каждого из первых 100 элементов grid значение 5 (и игнорируя расхождение 100 против 110), просто сделайте следующее:

for (int i = 0; i < 100; i ++) {
    grid[i] = 5;
}

Я понимаю, что вы беспокоитесь о скорости, но ваша проблема, вероятно, неуместна. С одной стороны, memset(), вероятно, будет оптимизировано и, следовательно, быстрее, чем простой цикл. С другой стороны, оптимизация может состоять из записи более одного байта за раз, что и делает этот цикл. С другой стороны, memset() в любом случае является циклом; явное написание цикла вместо его похоронения в вызове функции не меняет этого. С другой стороны, даже если цикл медленный, это вряд ли имеет значение; сконцентрируйтесь на написании понятного кода и подумайте об его оптимизации , если фактические измерения указывают на существенную проблему с производительностью.

Вы потратили на порядок вопросов больше времени, чем ваш компьютер, потратив на настройку grid.

Наконец, прежде чем у меня кончатся руки (слишком поздно!), Не имеет значения, насколько быстрым является memset(), если он не выполняет то, что вы хотите. (Не устанавливать grid вообще еще быстрее!)

1 голос
/ 01 февраля 2018

Если вы наберете man memset на своей оболочке, она сообщит вам, что

void * memset(void *b, int c, size_t len)

Простое объяснение на английском языке: заполнениестрока байтов b длины len с каждым байтом значением c.

Для вашего случая

memset(grid, 5, 100 * sizeof(int));

Начиная с sizeof(int)==4, поэтому приведенные выше фрагменты кода выглядели как:

for (int i=0; i<100; i++)
    grid[i]=0x05050505;

ИЛИ

char *grid2 = (char*)grid;
for (int i=0; i<100*sizeof(int); i++)
    grid2[i]=0x05;

Это вывело бы 84215045

Но в большинстве кода C мы хотим инициализировать частьблок памяти с нулевым значением.

  • char тип -> \0 или NUL
  • int тип -> 0
  • * тип 1040 * -> 0.0f
  • * тип 1044 * -> 0.0
  • тип указателя -> nullptr

И gcc или clang и т. Д. Современные компиляторы могут позаботиться об этом автоматически за вас.

// variadic length array (VLA) introduced in C99
int len = 20;
char carr[len];
int iarr[len];
float farr[len];
double darr[len];
memset(carr, 0, sizeof(char)*len);
memset(iarr, 0, sizeof(int)*len);
memset(farr, 0, sizeof(float)*len);
memset(darr, 0, sizeof(double)*len);
for (int i=0; i<len; i++)
{
    printf("%2d: %c\n", i, carr[i]);
    printf("%2d: %i\n", i, iarr[i]);
    printf("%2d: %f\n", i, farr[i]);
    printf("%2d: %lf\n", i, darr[i]);
}

Но имейте в виду, C Комитет ISO не навязываеттакие определения, это зависит от компилятора.

0 голосов
/ 11 сентября 2012

Этот код был протестирован. Вот способ установить массив «Integer» в значение от 0 до 255.

MinColCost=new unsigned char[(Len+1) * sizeof(int)];

memset(MinColCost,0x5,(Len+1)*sizeof(int));

memset(MinColCost,0xff,(Len+1)*sizeof(int));
0 голосов
/ 18 августа 2011

Поскольку memset записывает байты, я обычно использую его для установки массива int на ноль, например:

int a[100];
memset(a,0,sizeof(a));

, или вы можете использовать его для установки массива char, поскольку char - это точно байт:

char a[100];
memset(a,'*',sizeof(a));

Более того, массив int также может быть установлен в -1 с помощью memset:

memset(a,-1,sizeof(a));

Это потому, что -1 равно 0xffffffff в int и 0xff в char (байт).

...