Разное значение указателя до и после возврата

#c #pointers

#c #указатели

Вопрос:

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

Вот код (заботьтесь только о 3 последних строках):

 char* InfoFile_getValue(char* projectName, char* key)
{
    char* returnValue = NULL;

    char projectInfoPath[200];
    sprintf(projectInfoPath,"projects/%s/info.txt",projectName);

    FILE* fp = fopen(projectInfoPath,"r");

    if (fp != NULL) {
        char* ptr;
        size_t len = 0;
        char* lineFromFile = NULL;
        while(getline(amp;lineFromFile, amp;len, fp)!=-1)
        {
            if (strstr(lineFromFile,key))
            {
                ptr = lineFromFile   strlen(key);
                ptr = ptr   strspn(ptr, ": ");
                returnValue = malloc(strlen(ptr) 1);
                strcpy(returnValue,ptr);
                break;
            } 
        }
        free(lineFromFile);
        fclose(fp);
    }
    
    printf("Inside size: %dn",sizeof(returnValue));
    printf("String address inside function %pn", returnValue);
    return returnValue;
}
 

И затем я вызываю эту функцию, используя:

 char* baseString = InfoFile_getValue(projectName, "base");
printf("Outside size: %dn",sizeof(baseString));
printf("String address outside function: %pn",baseString);
printf("%sn", baseString);
 

Я выполнил это, используя флаги CC и ниже:

pkg-config --cflags gtk -3.0 -Wno-несовместимые-типы указателей -Wno-int-преобразование -Wno-отброшенные-квалификаторы pkg-config --libs gtk -3.0 -lpthread -lm

И это дает следующие результаты:

 Inside size: 8
String address inside function 0x5567d9ff4540
Outside size: 8
String address outside function: 0xffffffffd9ff4540
Naruszenie ochrony pamięci (zrzut pamięci)
 

Похоже, что он сокращает этот адрес до 4 байт и добавляет к нему префикс 0xff, но я понятия не имею, почему, я никогда не сталкивался с подобной проблемой. Любые предложения будут полезны.

Комментарии:

1. sizeof(returnValue) = размер указателя, а не то, на что он указывает. Просто говорю. Не знаю, было ли это намерением, но это то, что вы получили независимо.

2. Да, я хотел проверить размер указателя, чтобы убедиться, что он составляет 8 байт здесь и там. Просто чтобы убедиться.

3. Обычно вы видите что-то подобное только тогда, когда вызывающий не подготовлен должным образом к вызову. Т.Е. Это не является надлежащим прототипом и предполагает int возврат, который восторженные предупреждающие sqelchers «исправляют», жестко отбрасывая его. В таком случае, если int и char* имеют разные размеры (типично для платформ x64), фактически извлекается только 32 бит из 64-битного результата; остальное переносится из msb фантома int . Но я не вижу здесь таких махинаций. С прототипом этот код выглядит правильно. Если то, что вы утверждаете, верно, это должно повторяться в InfoFile_getValue основном с потрошением. Это правда?

4. Спасибо, ребята, всем вам, вы такие быстрые! Да, я пропустил правильный прототип функции. InfoFile_getValue было в отдельном файле, и я забыл включить «infoFile.h». Мне нужно, наконец, научиться читать предупреждения или каким-то образом рассматривать неявное предупреждение об объявлении как ошибку. Еще раз спасибо, вы потрясающие!

5. Хммм. Ваш компилятор должен был сказать вам, что это отсутствует. Если нет, вам нужно включить уровень предупреждения : -Wall -Wextra . И, конечно, вам нужно позаботиться о предупреждениях.

Ответ №1:

Кажется, вы получаете расширение знака младших 32 бит адреса. Просто дикое предположение, но я думаю, что компилятор рассматривал InfoFile_getValue как функцию, возвращающую указатели int while длиной 64 бита.

Правило заключается в том, что функция должна быть объявлена до ее использования. Если это не так, C предполагает, что он объявлен как int func() , то есть функция, принимающая любые параметры и возвращающая значение int . Вы должны убедиться, что у вас есть:

 char* InfoFile_getValue(char* projectName, char* key);
 

перед функцией (возможно, основной), содержащей char* baseString = InfoFile_getValue(projectName, "base");

Ответ №2:

Это похоже на расширение знака, так что это очень похоже на ошибку неявного ввода C90. То есть компилятор считает, что функция возвращает int значение 0xd9ff4540, которое в 32-разрядной системе, скорее всего, является отрицательным числом дополнения 2. Затем каким-то образом он преобразуется в 64-разрядный из-за %p и вы получаете расширение знака.

Самый простой способ решить эту проблему — прекратить использование C90, ему уже 30 лет, он сломан и опасен. Избавление от неявного int само по себе является достаточной причиной для переноса вашего кода на стандартный C.

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

Ответ №3:

Вы испытываете усечение значения и расширение знака из-за плохо подготовленного вызывающего. Т.Е. вызывающий не знает о фактическом типе возвращаемой функции из-за отсутствия надлежащего прототипа. Это может усугубить ситуацию, если вы не будете внимательно следить за C, особенно при работе с базой кода, которая, кажется, отлично работает на платформах, где int и void* имеют одинаковый размер.

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

main.c

 #include <stdio.h>

int main()
{
    void *p = foo();
    printf("main: p = %pn", p);
}
 

foo.c

 #include <stdio.h>

void *foo()
{
    static int x;
    void *p = amp;x;
    printf("foo: p = %pn", p);
    return p;
}
 

Выполнение приведенного выше кода после сборки как для x86 (где int и void* обычно имеют одинаковый размер), так и для x64 (где int обычно 32 бит и void 64 бит) выявит как проблему, так и тонкость того, как вы можете пропустить это в этом первом случае. Оба должны показывать предупреждения, подобные этому (которые вы в любом случае должны рассматривать как ошибки)

 1>main.c(13): warning C4013: 'foo' undefined; assuming extern returning int
1>main.c(13): warning C4047: 'initializing': 'void *' differs in levels of indirection from 'int'    
 

Результаты выполнения на обеих платформах (очевидно, что значения здесь могут отличаться в вашей системе)

x86

 foo: p = 00A38138
main: p = 00A38138
 

x64

 foo: p = 00007FF7E7B0C160
main: p = FFFFFFFFE7B0C160
 

Надеюсь, вы сможете увидеть, насколько важны две вещи:

  1. Всегда проверяйте, чтобы функции, которые вы вызываете в своем источнике, были правильно прототипированы перед их использованием.
  2. Всегда компилируйте с предупреждениями как ошибками, чтобы выявить подобные проблемы.

Вероятно, самое главное, что нужно держать в голове, это то, что, не выполняя оба пункта выше, код по-прежнему нормально работает на x86 и все еще компилируется на x64. Первое может вызвать у вас ложное чувство выполненного долга, а второе только подтверждает это, делая поиск подобных проблем особенно раздражающим. Пусть компилятор поможет вам. Убедитесь, что (1) и (2) присутствуют в вашей программе.

Комментарии:

1. Спасибо, чувак, именно это и произошло. Я перешел с x86 на x64, и после этого я столкнулся с этой проблемой. Теперь я узнал кое-что новое и добавил -Werror-implicit-function-declaration, чтобы предотвратить подобные ошибки в будущем.