Отсутствие декларации рудиментарной переменной ломает код

 

Столкнулся со странной ситуацией. Вот такая функция:

datetime GetMTime(string FileName) {
  char p[]; //strange var, but it is needed
  FindFileData result;
  SystemTime filedatetime;
  StringReplace(FileName, "/", "\\");
  int handle = FindFirstFileW(FileName, result);
  if (handle == -1) return(-1);
  FileTimeToSystemTime(result.LastWriteTime, filedatetime);
  return StringToTime(IntegerToString(filedatetime.wYear)+"."+IntegerToString(filedatetime.wMonth)+"."+IntegerToString(filedatetime.wDay) +
    " "+IntegerToString(filedatetime.wHour)+":"+IntegerToString(filedatetime.wMinute)+":"+IntegerToString(filedatetime.wSecond)) -
    TimeGMTOffset();
}


Переменная char p[] чудом осталась задекларирована после переделки функции. Я говорю "чудом", потому что она несет критическую важность для функционирования кода. Стоит только ее удалить или закомментировать, как код выдает Access violation write to...

Вот не осталась бы эта переменная, я бы долго гадал, что не так с этим кодом... впрочем, я и сейчас ничего не понял, какое такое ее мистическое значение для работоспособности кода?

 

Проверьте:

Alert( sizeof(FindFileData) );
Alert( sizeof(SystemTime ) );

У меня 320 и 16 соответственно. Скорее всего, неправильно объявили структуры, в итоге выходите за пределы стека.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/aa365740(v=vs.85).aspx

 

Почему 320? У меня 318...

typedef struct _WIN32_FIND_DATA {
  DWORD    dwFileAttributes; 4
  FILETIME ftCreationTime; 8
  FILETIME ftLastAccessTime; 8
  FILETIME ftLastWriteTime; 8
  DWORD    nFileSizeHigh; 4
  DWORD    nFileSizeLow; 4
  DWORD    dwReserved0; 4
  DWORD    dwReserved1; 4
  TCHAR    cFileName[MAX_PATH]; 260
  TCHAR    cAlternateFileName[14]; 14
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;

4+8+8+8+4+4+4+4+260+14 = 318

И где я потерял два байта? Неужели тут cFileName[MAX_PATH] и cAlternateFileName[14] надо на 1 байт больше для терминального нуля? ;)

 

У меня компилятор gcc, структуры объявлены следующим образом:

typedef struct _WIN32_FIND_DATAW {    // sizeof(WIN32_FIND_DATAW) == 592
        DWORD dwFileAttributes;             // 4
        FILETIME ftCreationTime;            // 8
        FILETIME ftLastAccessTime;          // 8
        FILETIME ftLastWriteTime;           // 8
        DWORD nFileSizeHigh;                // 4
        DWORD nFileSizeLow;                 // 4
#ifdef _WIN32_WCE
    DWORD dwOID;                        // 4  неактивно
#else
        DWORD dwReserved0;                  // 4
        DWORD dwReserved1;                  // 4
#endif
        WCHAR cFileName[MAX_PATH];          // 520
#ifndef _WIN32_WCE
        WCHAR cAlternateFileName[14];       // 28
#endif
} WIN32_FIND_DATAW,*PWIN32_FIND_DATAW,*LPWIN32_FIND_DATAW;


typedef struct _WIN32_FIND_DATAA {    // sizeof(WIN32_FIND_DATAW) == 320
        DWORD dwFileAttributes;             // 4
        FILETIME ftCreationTime;            // 8
        FILETIME ftLastAccessTime;          // 8
        FILETIME ftLastWriteTime;           // 8
        DWORD nFileSizeHigh;                // 4
        DWORD nFileSizeLow;                 // 4
#ifdef _WIN32_WCE
    DWORD dwOID;                        // 4  неактивно
#else
        DWORD dwReserved0;                  // 4
        DWORD dwReserved1;                  // 4
#endif
        CHAR cFileName[MAX_PATH];           // 260
#ifndef _WIN32_WCE
        CHAR cAlternateFileName[14];        // 14
#endif
} WIN32_FIND_DATAA,*PWIN32_FIND_DATAA,*LPWIN32_FIND_DATAA;

1.  Вы используете FindFirstFileW(), значит используйте первую форму. 

2. Почему 320? Дело в выравнивании. Для второй структуры выравнивание == 4. Это значит, что структура растёт гранулами по 4 байта. Следовательно, компилятор не может выделить 14 байт для cAlternateFileName[] (размер должен быть кратен выравниванию) и компилятор добавляет два пустых байта до или после структуры cAlternateFileName[] (по-моему, где конкретно (до или после) не регламентировано). В МКЛ выравнивание всегда == 1, в случаи необходимости заполнители писать самому. Если интересно, поищите что-нибудь типа: "C++ выравнивание данных/адресов/памяти".

 
Да, похоже, Вы оказались правы. Использовал первую структуру, закомментировал ту злосчастную переменную и все заработало, как надо. Спасибо за подсказки.
Причина обращения: