Как мне изменить указатель на void через функцию? - PullRequest
1 голос
/ 08 февраля 2011

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

void newData(void * d)
{
    char data[] = "world";

    d = &data;
}

int main()
{
        char testData[] = "hello";
        void * testPointer = &testData;

        printf("TestData is %s\n", (char *)testPointer);

        // Modify the data
        newData(&testPointer);

        printf("TestData is %s\n", (char *)testPointer);
}

Который просто выводит ::

TestData is hello
TestData is hello

Я что-то упускаю здесь очевидное? Я также пытался использовать указатель на указатель, но безрезультатно.

Ответы [ 3 ]

7 голосов
/ 08 февраля 2011

Я думаю, что вам нужно

void newData(void ** d) 
{
    char data[] = "world";
    *d = &data;  
}     

Однако у этого есть свои проблемы, так как "world" является локальным стеком и не будет действительным после возврата из newData.

4 голосов
/ 08 февраля 2011

В этом две вещи неправильные:

char data[] = "world";

Является ли , вероятно, созданным как часть стекового фрейма функции.Массивы ухудшаются до указателей.Таким образом, когда функция вызывает ret, она должна была очистить свой стек и память по этому адресу исчезла.Если здесь работает какая-либо операция, то это потому, что вы еще не перезаписали память.

Что вы могли бы сделать?Объявите это static - это одно решение, которое гарантирует (по крайней мере, в соответствии с c99) существование программы на протяжении всей жизни (то есть она будет размещена не в стеке, а в сегменте данных, а библиотека c выделит ее перед main).Однако, так как я подозреваю, что это всего лишь демонстрация, стоит отметить, что:

char data[] = "world";
memcpy(d, data, 5);

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

newData(&testPointer);

Вы делаете простую ошибку здесь.Указатель в сборке - это адрес памяти, содержащий другой адрес памяти.Когда вы передаете указатель на функцию, вы хотите передать этот адрес памяти, чтобы при вызове этой функции содержимое указателя, адрес памяти, копировалось в стек в виде нового указателя.Конечно, оба этих указателя указывают на одну и ту же вещь, и именно так вы в конечном итоге добиваетесь типа «передача по ссылке».Если вы мне не верите, посмотрите на это в отладчике.

Однако вы передаете адрес указателя, поэтому вы создаете указатель на указатель на значение.Представьте себе такую ​​память:

|Address|Value
|0x120  |0x121    <-- this is what you're passing with &testPointer
|0x121  |0x122    <-- this is a pointer; it contains the address of a value
|0x122  |h        <-- this is a value.
|0x123  |e
|0x124  |l
...

Надеюсь, это прояснит ситуацию.В моей упрощенной памяти вы передаете 0x120 вместо 0x121.Можно конечно разыменование дважды, но почему?Простое решение - просто передать указатель так:

newData(testPointer);
0 голосов
/ 19 мая 2015

Я думаю, что небольшое изменение в коде может решить проблему:

void newData(void ** d)
{
    char* data = "world";

    *d = data;
}
...