Начальная C ++ неинициализированная локальная переменная

У меня есть функция:

VOID GetOSVersion(PDWORD major, PDWORD minor, PDWORD build) { OSVERSIONINFO osver; ZeroMemory(&osver, sizeof(OSVERSIONINFO)); osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osver); if(major) *major = osver.dwMajorVersion; if(minor) *minor = osver.dwMinorVersion; if(build) *build = osver.dwBuildNumber; } 

И я хотел вызвать его так:

 PDWORD major; PDWORD minor; PDWORD build; GetOSVersion(major, minor, build); 

Я получаю сообщение об ошибке: неинициализированная локальная переменная: для всех трех аргументов. В моей голове это было похоже: я объявляю майор, минор, строю, и они заполняются функцией. Пространство уже выделено для них в течение первых трех строк вызывающего кода.

Я, конечно, чего-то не вижу. Может ли кто-нибудь объяснить это мне?

проблема в том, что:

 DWORD major; DWORD minor; DWORD build; GetOSVersion(&major, &minor, &build); 

Fix:

 VOID GetOSVersion(PDWORD major, PDWORD minor, PDWORD build) { OSVERSIONINFO osver = {}; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osver); if(major) *major = osver.dwMajorVersion; if(minor) *minor = osver.dwMinorVersion; if(build) *build = osver.dwBuildNumber; } DWORD major = 0; DWORD minor = 0; DWORD build = 0; GetOSVersion(&major, &minor, &build); 

PDWORD – указатель на DWORD. Все три параметра являются выходными параметрами. В C / C ++ это обычное использование: если вы хотите вернуть более одного значения из функции, вам нужно передать указатель (или ссылку в случае c ++) на переменную:

 int var = 0; if(some_function_that_can_fail_and_write_result(&var)) ;//do something 

В вашем случае вы передаете неинициализированный указатель на функцию по значению. Это то же самое, что:

 void foo(int parameter); // ... int a; foo(a); 

У вас есть много способов:

Передать неинициализированный указатель по ссылке:

 VOID GetOSVersion(PDWORD& major, PDWORD&, PDWORD&) { //... major = new DWORD(osver.dwMajorVersion); } // usage: PDWORD major; GetOSVersion(major, ....); //... delete major; 

Передайте все параметры по ссылке:

 VOID GetOSVersion(DWORD& major, DWORD&, DWORD&) { //... major = osver.dwMajorVersion; } // usage: DWORD major = 0; GetOSVersion(major, ....); 

Используйте свою версию GetOSVersion (), но с исправлением в этом ответе в начале

Вы делаете ошибку, которую многие делают, когда дело доходит до функций, требующих аргументов указателя.

Если для функции требуется указатель в качестве аргумента, это не означает, что вы слепо объявляете указатель и передаете его функции. То, что запрашивает функция, – это address-of существующей, действительной сущности.

 DWORD major, minor, build; GetOSVersion(&major, &minor, &build); 

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

Другая ошибка, связанная с этим (а не ошибка, так как она даст желаемые результаты, но все же «ошибка») заключается в объявлении указателя, укажите, где она действительна, и затем передайте ее функции. Другими словами:

 PDWORD major, minor, build; major = new DWORD; minor = new DWORD; build = new DWORD; GetOSVersion(major, minor, build); delete major; delete minor; delete build; 

Я видел код, написанный таким образом. Это указывает на то, что программист не имеет четкого представления о том, что означает, когда функция требует указателя в качестве аргумента. Программист считает, что должен быть объявлен указатель, если он указывает где-то действительным, а затем передать этот указатель. Да, вы получаете результаты без сбоев, но тратить время на вызов распределителя.

Таким образом, самый простой способ – это первый пример выше. Просто объявляйте типы без указателей и просто передавайте адрес.

Вы, вероятно, хотели иметь свои объявления переменных и назвать свою функцию следующим образом

 DWORD major; DWORD minor; DWORD build; GetOSVersion(&major, &minor, &build); 

Вы используете указатели для ссылки на выходные параметры, поэтому их нужно указывать на действительные адреса памяти. Вы можете обратиться к этим переменным, чтобы получить действительный указатель, используя оператор ‘address-of’ ( & ), как показано выше.


С помощью c ++ вы можете использовать ссылочные параметры, которые сделают вещи более понятными

 VOID GetOSVersion(DWORD& major, DWORD& minor, DWORD& build) { OSVERSIONINFO osver; ZeroMemory(&osver, sizeof(OSVERSIONINFO)); osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osver); // Note there's no check needed if the pointers are valid! major = osver.dwMajorVersion; minor = osver.dwMinorVersion; build = osver.dwBuildNumber; } DWORD major; DWORD minor; DWORD build; GetOSVersion(major, minor, build); 

Не нужно вызывать new() распределитель (и беспокоиться о правильном управлении распределением динамической памяти) с любым из приведенных выше образцов на 1-м месте.

Это указатели. Они не указывают на какую-либо память, которую вы выделили. Они не получают «заполнены» в функции, они привыкают к доступу (неинициализированной) памяти.

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

Если вы выполнили, ваша программа будет segfault, потому что вы записываете в память, на которую они указывают, но поскольку они не инициализированы, они содержат недопустимые / случайные адреса.

Возможное решение

 PDWORD major = new DWORD; PDWORD minor = new DWORD; PDWORD build = new DWORD; 

что PDWORD определяется как *DWORD .

Не забудьте удалить!

edit: На самом деле гораздо разумнее выделить их в стеке – см. ответ пользователя2451677.

  • Являются ли переменные стека согласованными GCC __ атрибутом __ ((aligned (x)))?
  • std :: array vs array performance
  • Работа с анонимными типами C #
  • генерировать EF orderby Выражение строкой
  • Как использовать C ++ с Objective-C в XCode
  • Уничтожение объектов в C ++
  • Список инициаторов не работает с вектором в Visual Studio 2012?
  • Обнаружено, что
  • DataTrigger не изменяет свойство Text
  • Есть ли причина не использовать целые типы фиксированной ширины (например, uint8_t)?
  • Как определить текущее разрешение экрана?
  • Давайте будем гением компьютера.