Двойные указатели в качестве аргументов в C - PullRequest
1 голос
/ 11 марта 2020

У меня есть некоторые проблемы с двойными указателями в качестве аргументов в C.

Из того, что я знаю до сих пор: когда у меня есть функция, которая принимает указатель в качестве аргумента, скажем, функция с именем functionX(int *y) затем, когда я вызываю функцию X, скажем, по: functionX(&randomvar), где randomvar - целое число, содержащее определенное значение (скажем, 5), тогда C создаст указатель, который также называется randomvar, который содержит адрес randomvar. Таким образом, вывод *randomvar будет равен 5.

Правильно ли мое понимание?

Если так, то когда функция имеет двойные указатели: functionX(int **y) и при выполнении functionX(&randomvar) создает 2 указатели, один из которых содержит адрес randomvar, другой содержит адрес первого указателя.

Я немного озадачен здесь, я не уверен, правильно ли это.

Ответы [ 6 ]

3 голосов
/ 11 марта 2020

Из того, что я знаю до сих пор: когда у меня есть функция, которая принимает указатель в качестве аргумента, говорит функцию с именем functionX(int *y), то когда я вызываю functionX, скажем, по: functionX(&randomvar), где randomvar - это целое число, содержащее определенное значение (скажем, 5), тогда C создаст указатель, который также называется randomvar, который содержит адрес randomvar. Таким образом, результат *randomvar будет равен 5.

Правильно ли мое понимание?

Нет.

Ситуация, которую вы описываете, будет иметь следующий вид:

void functionX(int *y) {
    // ...
}

int main(void) {
    int randomvar = 5;
    functionX(&randomvar);
}

Выражение &randomvar в функции main() этого кода вычисляет адрес локальной переменной этой функции randomvar. Выражение имеет тип int *, аналогично параметру y для функции functionX(), поэтому оно хорошо подходит для использования в качестве аргумента этой функции. Пока все хорошо.

Но выражение &randomvar обозначает только значение , а не объект. Для него не зарезервировано хранилище, и поэтому у него нет адреса. У него также нет имени, и, в частности, оно не называется «randomvar». Внутри функции functionX() (копия) к этому значению можно получить доступ как y, а выражение *y будет иметь значение 5. Нигде в представленном коде нет *randomvar семантически допустимого выражения.

Если так

Это не так.

, тогда когда функция имеет двойные указатели: functionX (int ** y) и, выполняя functionX (& randomvar) создает 2 указателя, один из которых содержит адрес randomvar, а другой - адрес первого указателя.

Совсем нет. &randomvar - это одно выражение. Оценка его дает одно значение, которое, как я уже рассмотрел, имеет тип int *. Это не соответствует параметру функции типа int **. int ** является указателем на int *. Чтобы получить его, вы можете взять адрес объекта (не значение) типа int *:

void functionY(int **z) {
    // ...
}

int main(void) {
    int randomvar = 5;
    int *varptr = &randomvar;
    functionY(&varptr);
}

Когда все сделано таким образом, действительно есть два указателя, один - int * и другой int **, но первый должен быть объявлен явно.

2 голосов
/ 11 марта 2020

Визуализируйте это так:

void foo(int a);
void bar(int *b);
void baz(int **c);

// main function
{
    int x = 5;
    int *p = &x;

    foo(x);
    bar(p);
    baz(&p);
}
// **main** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
ABCD:4000                  x                    5
ABCD:4008                  p                    ABCD:4000
// **foo** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:2000                  a                    5
// a new variable is created.
// any changes made on 'a' will not affect 'x' in 'main'
// **bar** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:4000                  b                    ABCD:4000
// a new pointer is created pointing to 'x'
// 'b' points to 'x' and '*b' means 'the value stored in ABCD:4000'
// any changes made on '*b' will affect 'x' in main
// **baz** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:8000                  c                    ABCD:4008
// a new pointer is created pointing to 'p'
// 'c' points to 'p' and '*c' means 'the value stored in ABCD:4008'
// any changes made on '*c' will change the value of 'p' in main
// if '**c = 7' is executed, x will be assigned '7' 
// if '*c = ABCD:8000' is executed, p will no longer point to 'x'
1 голос
/ 11 марта 2020

Вы можете просто продолжать цепочку указателей, если хотите. Вот некоторый код, который демонстрирует это:

#include<stdio.h>

void foo(int ***A, int **B, int *C) {
    printf(" %p A\n %p *A\n %p **A\n %d ***A\n", A, *A, **A, ***A);
    printf(" %p B\n %p *B\n %d **B\n", B, *B, **B);
    printf(" %p C\n %d *C\n ", C, *C);
}

int main(void) {

    int D = 8;
    int* C = &D;
    int** B = &C;
    int*** A = &B;

    foo (A,B,C);
    printf("%p &D (in main)\n", &D);
    return 0;

Это даст такие результаты для разыменованных указателей, каждый '*' отрывается в процессе работы, поэтому обратите внимание, как **A = *B = C = &D

0x7ffc81b06210 A
0x7ffc81b06218 *A
0x7ffc81b06224 **A                                                                  
8 ***A                                                                              

0x7ffc81b06218 B
0x7ffc81b06224 *B
8 **B

0x7ffc81b06224 C
8 *C                                                                                

0x7ffc81b06224 &D (in main)

Обратите внимание, наконец, что вызов foo (&B, &C, &D) даст тот же результат.

1 голос
/ 11 марта 2020

Нет указателя, сделанного с тем же именем, что и переданная переменная, при вызове функции с адресом переданной переменной в качестве аргумента. Он просто передает адрес определенной переменной, когда ему предшествует оператор &. Внутри вызываемой функции указатель, который содержит этот адрес, может иметь любой допустимый идентификатор.


, когда функция имеет двойные указатели: functionX(int **y) и при выполнении functionX(&randomvar) создает 2 указателя, один из которых содержит адрес randomvar, другой, который содержит адрес первого указателя.

Вы не можете передать адрес объекта int типа int*, когда функция ожидает объект типа int**.

Говоря о передаче двойные указатели в общем, концепция такая же, как сказано выше.

1 голос
/ 11 марта 2020

Когда я вызываю функцию X, скажем, по: functionX (& randomvar), где randomvar - это целое число, содержащее определенное значение (скажем, 5), тогда C создаст указатель, который также называется 'randomvar ', который содержит адрес randomvar

Имя указателя в функции functionX равно y, потому что вы сами написали, что это имя параметра функции functionX(int *y).

Если это так, то когда функция имеет двойные указатели: functionX (int ** y) и, выполняя functionX (& randomvar), создает 2 указателя, один из которых содержит адрес randomvar, другой - адрес первый указатель Если у вас есть функция, объявленная, например, как

Оператор & создает один указатель, а не два указателя.

Если у вас есть функция, объявленная, например, как

void functionX(int **y);

и переменная, объявленная как

int randomvar = 5;

, тогда такой вызов функции, такой как

functionX( &randomvar );

, генерирует ошибку компиляции, потому что тип аргумента равен int *, тогда как тип параметра в соответствии с объявлением функции: int **.

Вы не можете писать, например, как

functionX( &&randomvar );

, потому что использование первого оператора & создает временный объект типа int *. И вы не можете применять оператор & к временному объекту.

Для вызова функции вы можете написать

int *p = &randomvar;

functionX( &p );

В этом случае тип аргумента будет int **, так как требуется типом параметра.

Вот демонстрационная программа.

#include <stdio.h>

void f( int *p )
{
    printf( "The value of p is %p\n"
            "the pointed value is %d\n", 
            ( void * )p, *p );
}

void g( int **p )
{
    printf( "The value of p is %p\n"
            "the pointed value is also a pointer %p\n"
            "the pointed value by the dereferenced pointer is %d\n", 
            ( void * )p, ( void * )*p, **p );
}


int main(void) 
{
    int x = 5;

    f( &x );

    putchar( '\n' );

    int *p = &x;

    g( &p );

    return 0;
}

Ее вывод может выглядеть, например, как

The value of p is 0x7ffced55005c
the pointed value is 5

The value of p is 0x7ffced550060
the pointed value is also a pointer 0x7ffced55005c
the pointed value by the dereferenced pointer is 5
1 голос
/ 11 марта 2020

Нет, ваше понимание неверно. C ничего не «создает» в том смысле, в котором вы предполагали - большинство переменных являются просто метками мест в памяти (не только тем, что они могут «псевдонимить», либо меткой регистров ЦП) - ваш randomvar в область действия, где вызывается functionX, является меткой области памяти, выделенной для хранения целого числа и которая интерпретируется как целое число. Значение &randomvar унарного выражения (оператор & с одним операндом randomvar) представляет собой адрес данных. Этот адрес - то, что передается в functionX как y, что означает, что область памяти (или регистр ЦП) зарезервирована и хранит адрес (не значение) randomvar.

В качестве примера Допустим, ваша программа объявляет переменную наподобие int randomvar; - при выполнении часть ОЗУ - 4 байта для int обычно - резервируется для хранения значения переменной randomvar. Адрес не известен, пока программа не выполняется в памяти, но для примера давайте представим, что адрес 0xCAFEBABEDEADBEEF (8 байт) - это тот, который указывает на 4 байта для хранения целочисленного значения. До того, как переменной будет присвоено значение, значение по адресу является неопределенным - объявление переменной только резервирует пространство для хранения значения, ничего не записывает по адресу, поэтому перед тем, как назначить значение переменной вы даже не должны использовать это значение вообще (и большинство C компиляторов могут предупредить вас об этом).

Теперь, когда адрес передан в functionX, это означает, что для функции, метка y - это 8 байт, зарезервированных в некоторой ячейке памяти для хранения адреса целочисленной переменной. При вызове типа functionX(&randomvar), y сохраняет 0xCAFEBABEDEADBEEF. Однако y также [обычно] имеет адрес - прежнее значение (адрес randomvar) должно храниться где-то ! Если регистр ЦП не хранит значение, в этом случае, естественно, адрес [RAM] отсутствует.

Для указателя на указатель типа int * * y, y обозначает область памяти, зарезервированную для хранения адреса по адресу целочисленной переменной.

...