Проверьте, имеет ли массив 2d указателей пользовательское значение в C?

Образец кода:

float** a;  
a = (float**) malloc(numNodes * sizeof(float*));  
for(int i=0; i<`numNodes`; i++)  
{  
    a[i] = (float*)malloc((numNodes-1) * sizeof(float));  
}

Я создаю динамический массив 2d выше. Перед заполнением я заметил, что каждый блок в массиве уже содержит это значение: -431602080.000000, а не NULL. Почему это так?
Бывают ситуации, когда используются не все пробелы в массиве.
Итак, мой запрос прост: есть ли элегантный способ проверить, имеет ли каждый блок это значение по умолчанию или значение, определенное пользователем? ?

Заранее спасибо.


person Freddy    schedule 31.12.2010    source источник


Ответы (5)


В C автоматические переменные не инициализируются автоматически. Вам нужно явно установить для вашей переменной значение 0, если это то, что вы хотите.

То же самое верно для malloc, который не инициализирует пространство в куче, которую он выделяет. Вы можете использовать calloc, если хотите его инициализировать:

a = malloc( numNodes*sizeof(float*) ); // no need to initialize this
for ... {
  a[i] = calloc( numNodes-1, sizeof(float) );
}
person peoro    schedule 31.12.2010
comment
Peoro: если он его не инициализирует, то что это -431602080.000000? что каждый блок, кажется, есть? - person Freddy; 31.12.2010
comment
Если вы работаете в режиме отладки, то это должно быть волшебное значение, которое поможет вам увидеть, что вы используете неинициализированные данные. - person Axel Gneiting; 31.12.2010
comment
Это может быть что угодно. Это может быть значение, которое часть памяти содержала до вашего вызова. - person peoro; 31.12.2010
comment
@ Фредди, кто знает? Это зависит от вашей операционной системы, реализации malloc и того, что уже произошло в вашем приложении. В следующем заезде может быть иначе. Он не определен, не полагайтесь на него и не беспокойтесь об этом — вам все равно придется инициализировать выделенное пространство - person The Archetypal Paul; 31.12.2010
comment
Если вы попробуете калькулятор здесь babbage.cs.qc.edu/IEEE-754/ Decimal.html вы обнаружите, что это шестнадцатеричное значение 0xCDCDCDCD. Предположительно что-то инициализирует это для отладки? - person The Archetypal Paul; 31.12.2010
comment
Спасибо, Пеоро, Пол и другие. Я не могу продвигать ваши комментарии. Хорошие объяснения. - person Freddy; 31.12.2010
comment
Да, это обычная практика при отладке сборок — использование характерного магического значения для указания неинициализированной переменной, чтобы помочь диагностировать проблему. GCC использует для этой цели 0xBAADF00D, но это справедливо только для отладочных сборок. Оптимизированные сборки не имеют неявной инициализации (за исключением случаев, когда стандарт языка говорит об этом - статические глобальные переменные, IIRC). - person Kos; 31.12.2010
comment
@peoro - помните, что calloc устанавливает все биты в ноль независимо от типа. К счастью, битовая комбинация 0x00000000 соответствует нулю в IEEE-754 с плавающей запятой, но помните, что вы не сможете отличить это начальное нулевое значение от 0.0f, которое будет введено позже. У Matteo была хорошая идея использовать для этого специальное значение NaN, чтобы решить эту проблему. - person Kos; 31.12.2010
comment
@Kos - я только что заметил эту проблему, когда массив не может различать введенный пользователем ноль и 0,0000 (т.е. NULL) в блоках массива после использования calloc для инициализации. Как вы предлагаете решить эту проблему с помощью NaN? - person Freddy; 31.12.2010
comment
@Freddy: простой способ сделать это - просто создать цикл for после инициализации a[i], где вы устанавливаете любой a[i][j] в NaN. - person peoro; 31.12.2010

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

Таким образом, в целом нет «значения по умолчанию» и нет способа проверить, была ли изменена ваша память; однако вы можете инициализировать блоки памяти, которые вы используете, с магическими значениями, которые, как вы уверены, не будут использоваться как «реальные данные», но это будет просто внутреннее соглашение вашего приложения.

К счастью, для переменных с плавающей запятой есть несколько волшебных значений, таких как тихий NaN, которые вы можете использовать для этой цели; в общем, вы можете использовать макрос NAN, определенный в <math.h>, чтобы установить float в NaN.

Кстати, вам не следует читать неинициализированные floats и doubles, так как обычный формат, в котором они хранятся (IEEE 754), содержит некоторые магические значения (например, сигнальный NaN), которые могут вызывать арифметические исключения при их чтении, поэтому, если ваш неинициализированный память содержит такой битовый шаблон, что ваше приложение, вероятно, рухнет.

person Matteo Italia    schedule 31.12.2010
comment
+1, сомнение: вы сказали, что «ос очищает страницы памяти, используемые другим проком», так ли это? Я имею в виду, почему ОС должна беспокоиться об установке длинного диапазона памяти на ноль. Есть ли какая-то (очевидная) причина для этого? - person Vikram.exe; 31.12.2010
comment
@Vikram.exe: представьте, что у вас есть приложение, которое обрабатывает номера кредитных карт; были бы вы счастливы, если бы ваши освобожденные страницы памяти остались нетронутыми для совершенно не связанных процессов? - person Matteo Italia; 31.12.2010
comment
Нет, я бы не стал, поэтому я сам явно отключу содержимое памяти (из своей программы). Базовая ОС не будет. правильно? Это дополнительные накладные расходы для ОС на выполнение таких (возможно, ненужных) операций. - person Vikram.exe; 31.12.2010
comment
@Vikram.exe: AFAIK, все современные упреждающие ОС делают это (или, по крайней мере, так говорит Таненбаум :) ); IIRC - это поток с низким приоритетом, который очищает неиспользуемые страницы, когда больше нечего делать (или когда они необходимы). Имейте в виду, что страницы памяти повторно используются не только при освобождении памяти, но и когда памяти не хватает, и ОС необходимо заменить страницы памяти, чтобы предоставить физическую оперативную память другому процессу, который в ней нуждается; у вашего процесса нет возможности самостоятельно очищать конфиденциальные данные (также потому, что переключение задач прозрачно для приложений). - person Matteo Italia; 31.12.2010

Среды выполнения C не обязаны инициализировать какую-либо память, которую вы не инициализировали сами, и значения, которые они содержат, по сути являются случайным мусором, оставшимся с момента последнего использования этой памяти. Вам придется сначала установить их все в NULL явно или использовать calloc.

person Puppy    schedule 31.12.2010

Расширение хорошего ответа Matteo Italia:

Код инициализации одного массива будет выглядеть так:

float* row;

row = malloc( numNodes*sizeof(float) );
for (int i=0; i<numNodes; ++i) {
    row[i] = nanf(); // set a Not-a-Number magic value of type float
}

(я оставлю вам право изменить это для вашего многомерного массива)

Потом где-то:

float value = ...; // read the array
if (isnan(value)) {
    // not initialized
} else {
    // initialized - do something with this
}

Важно помнить одну вещь: NaN == NaN даст false, поэтому для проверки наличия этого значения лучше использовать isnan(), а не ==.

person Kos    schedule 31.12.2010
comment
Ого, всегда очень весело иметь дело с магическими значениями fp :) - person Matteo Italia; 31.12.2010

Перед заполнением я заметил, что каждый блок в массиве уже содержит это значение: -431602080.000000, а не NULL. Почему это?

malloc() не инициализирует выделяемую память. Вам нужно использовать calloc(), если вы хотите 0 инициализацию

void *calloc(size_t nelem, size_t elsize);

Функция calloc() выделяет неиспользуемое пространство для массива из nelem элементов, размер каждого из которых в байтах равен elsize. Пространство должно быть инициализировано всеми битами 0.

person Prasoon Saurav    schedule 31.12.2010
comment
Спасибо, Прасун. Я вижу, что теперь мне нужно вызывать calloc всякий раз, когда я использую malloc. - person Freddy; 31.12.2010