void * myObject;
неинициализирован и не указывает на допустимое хранилище. Чтение его значения (для передачи его в качестве аргумента по значению Somestruct_ctor(myObject)
) является неопределенным поведением.
Тот факт, что ваш код не всегда дает сбой, говорит нам о том, что в коде генерации ICC он указывает где-то действительный, возможно, где-то в стеке. С большим файлом мы, вероятно, получаем переполнение буфера, которое перезаписывает локальную переменную и / или адрес возврата и заканчивается бесконечным циклом. Удивительно, что это все равно не сработало, поскольку это произошло случайно. (В ассемблере x86-64 из ICC с отключенной оптимизацией он просто загружает некоторую неинициализированную память стека как аргумент для Somestruct_ctor
.)
Или, возможно, указатель на структуру данных stdio, оставшуюся от init stdio до main
. Возможно, наличие fillDataFromFile
каракули по всем данным, на которые указывает FILE *stdout
(например), оставило их в «заблокированном» состоянии, поэтому ваш единственный поток застрял в ожидании чего-то еще, чтобы разблокировать мьютекс. Если вы знаете asm, может быть интересно сделать один шаг по бесконечному циклу или «тупику» внутри printf
и посмотреть, что именно произошло.
Если вы компилируете с gcc -O3
, компилятор обнуляет регистр как аргумент для fillDataFromFile
(после встраивания Somestruct_ctor
), поэтому он передает указатель NULL. Это может произойти сбой всегда, если функция разыменовывает указатель.
clang решает оставить rdi
(первый регистр передачи аргументов в x86-64 System V, вызывающей conventino) неинициализированным, поэтому он все еще остается argc
, когда main
вызывает fillDataFromFile
. Это также может привести к краху.
Вы забыли включить предупреждения компилятора.
Все основные компиляторы x86 (gcc, clang, MSVC, ICC) имеют предупреждения об этом, но они не включены по умолчанию во всех компиляторах (только в MSVC). Возможно, потому что могут быть случаи, когда компилятор не уверен в том, что var неинициализирован, если есть какие-то условные вещи. В этом случае он на 100% уверен, что он определенно используется неинициализированным, но если инициализация и использование были в разных блоках if()
, компилятор не сможет доказать, что использование произошло только в случае инициализации.
С clang и gcc вы обычно должны использовать -Wall
и отключить все предупреждения.
С ICC -diag-enable:warn
ближе к gcc -Wall
. (-Wall
ICC не включает это очень важное предупреждение. Не думайте, что вы включили все важные предупреждения с помощью icc -Wall
.)
# from icc -diag-enable:warn on your code
<source>(21): warning #592: variable "myObject" is used before its value is set
myObject = Somestruct_ctor(myObject);
^
как включить предупреждения icc / icpc? имеет некоторую информацию. Таким образом, -Wall
ICC очень минималистичен по сравнению с GCC. Так что, возможно, -Wall -Wextra
было бы полезно с icc. Он рекомендует -w2
или -w3
в качестве потенциально полезных уровней предупреждения.
Clang обычно имеет самые хорошие предупреждения, в этом случае:
<source>:21:30: warning: variable 'myObject' is uninitialized when used here [-Wuninitialized]
myObject = Somestruct_ctor(myObject);
^~~~~~~~
<source>:19:18: note: initialize the variable 'myObject' to silence this warning
void * myObject;
^
= NULL
1 warning generated.
Я получил вышеуказанные результаты, скомпилировав исходный код в проводнике компилятора Godbolt (после исправления синтаксических ошибок: пропущенная точка с запятой после структуры и использование заглавной буквы в ключевом слове Struct
). -xc
говорит компиляторам C ++ на Godbolt скомпилировать как C.
Оказывается, вам не нужно включать оптимизацию для icc и gcc, чтобы заметить эту ошибку. Некоторые предупреждения работают только с включенной оптимизацией, когда компилятор больше анализирует логику программы и может заметить больше, но они отслеживают неинициализированные даже при -O0
.
код конструктора, который будет иметь больше смысла:
// C
int main(void){
struct Somestruct myObject; // automatic storage for the object value
Somestruct_ctor(&myObject); // pass a pointer to that storage
}
Объект должен где-то жить. Мы можем получить место для него с помощью автоматического (локального), статического (static
локального или глобального) или динамического хранилища (malloc
).
Автоматическое хранение + вызов конструктора эквивалентно C ++, как это, если struct Somestruct
имеет конструктор C ++ по умолчанию, объявленный в определении структуры / класса.
// C++
int main(void){
Somestruct myObject; // calls the default constructor, if there is one
// destructor will be called at some point when myObject goes out of scope
}