64-битная архитектура - указатель на символ усекается при возврате из функции - PullRequest
6 голосов
/ 20 января 2011

Окружающая среда:

Windows x64 bit с 5 ГБ оперативной памяти. Мой двоичный файл - 64-битный, построенный с компилятором версии - «Оптимизирующий компилятор Microsoft (R) C / C ++ версии 14.00.50727.762 для x64»

Настройка среды:

Microsoft предлагает установить указанный ниже раздел реестра для тестирования 64-битных приложений, и я установил его в своем ящике. Проблема не возникает, если я не установил следующий реестр, потому что программа размещена по низкому адресу. Тот же ключ реестра упоминается в обсуждении - Как программист, о чем мне следует беспокоиться при переходе на 64-битные окна?

Чтобы принудительно распределять выделения с более высоких адресов перед более низкими адресами в целях тестирования, укажите MEM_TOP_DOWN при вызове VirtualAlloc или задайте для следующего значения реестра значение 0x100000:

HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Диспетчер сеансов \ Управление памятью \ AllocationPreference

Пример кода:

char *alloc_str()
{
    char *temp;
    temp = (char *) malloc(60);
    /* copy some data to temp */
    return temp;
}

main()
{
    char *str;
    str = (char *)alloc_str();
}

Анализ:

malloc возвращает адрес 0x000007fffe999b40, который хранится в temp, но когда указатель возвращается к main(), str получает только вторую половину - 0xfffffffffe999b40, и я не могу получить доступ к данным в этом месте.

Ответы [ 4 ]

6 голосов
/ 21 января 2011

Два момента о стиле, которые могут помочь в диагностике такого рода проблем.Поскольку вы используете malloc, я предполагаю, что он скомпилирован как программа на Си.В этом случае не вводите данные, когда это не нужно.Чрезмерные типы могут подавлять предупреждения о том, что вы не правильно объявили свои функции.Если прототипа нет, а определение функции находится в другом модуле, то ваш вызов str = (char *)alloc_str(); подавит предупреждение о том, что функция используется без объявления и будет использоваться объявление по умолчанию C, т.е. все параметры и возвращаемое значение будут рассматриваться какint.То же самое с malloc, если вы забыли правильное включение, ваш typecast отключит предупреждение, и компилятор предположит, что функция возвращает int.Это может быть уже причиной усечения.Другой момент, в C, пустой список параметров объявляется с (void), а не (), что имеет другое значение (неопределенные параметры).Это 2 точки, которые отличаются между C и C ++.

3 голосов
/ 20 января 2011

Параметр реестра, к которому вы обращаетесь, выбирает распределение памяти сверху вниз.Вы заявляете, что этот параметр «поместит программу в большое адресное пространство».Это не будет делать это.Это заставляет диспетчер памяти Windows выделять память из верхней части адресного пространства.

Более того, поскольку вы работаете под 64-битной Windows, я не вижу, куда входит PAE.Это было бы что-то используемое на 32-битной платформе.

Я бы предположил, что вы компилируете 32-битное приложение, и поэтому ваши указатели неизбежно имеют ширину 32 бита.Но это только предположение из-за недостатка информации.

Короче говоря, на ваш вопрос невозможно ответить, потому что вы не сказали нам, что делаете.

2 голосов
/ 25 января 2011

Дэвид, спасибо за ваше время и предложение. Пример программы не работал, потому что я не включил stdlib.h в мой C-файл, и поэтому указатель, возвращаемый malloc, был 32-битным. Проблема, с которой я столкнулся, была с производственным кодом, который немного отличался от примера кода.

tristopia, ваше объяснение абсолютно верно. Я столкнулся с той же проблемой.

В моих производственных файлах C я столкнулся с проблемой, как показано ниже

a.c

call_test1()
{
 char* temp;
 temp = (char *)test1();
}

b.c

include stdlib.h
char* test1()
{
 char *str;
 test = (char *)malloc(60);
 /* copy some data to test*/
 return str;
}

Когда str был возвращен test1(), указатель содержал 64-битный адрес (я проанализировал регистр rx (в котором хранится возвращаемое значение функции) в windbg, используя "r rx"), но когда он был назначен temp, оно было усечено до 32-битного адреса).

Проблема возникла из-за

  1. Не включая подпись test1() в файле a.c
  2. Тип, возвращая возвращаемое значение в символ *

Модифицированный источник

a.c

char * test1();
call_test1()
{
 char* temp;
 temp = test1();
}

b.c

include stdlib.h
char* test1()
{
 char *str;
 test = (char *)malloc(60);
 /* copy some data to test*/
 return str;
}

Я получил решение методом проб и ошибок, но Тристопия объяснила причину.

64-битные ноты

  1. Используйте% p вместо% lx, чтобы напечатать адрес указателя в 64-битной среде.
  2. Включите сигнатуру функции и попытайтесь избежать указателей типов
  3. В Windows, Если вы перекомпилируете свой код C / C ++ в 64-битный EXE или DLL, вы должны протестировать его с установленным значением реестра AllocationPreference.

Чтобы принудительно распределять выделения с более высоких адресов перед более низкими адресами в целях тестирования, укажите MEM_TOP_DOWN при вызове VirtualAlloc или установите следующее значение реестра на 0x100000:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\AllocationPreference
1 голос
/ 20 января 2011

Мне нужно спросить, используете ли вы правильные настройки компилятора и ссылаетесь на правильную библиотеку времени выполнения C.

...