Имеет ли * различные значения в зависимости от контекста его использования? - PullRequest
0 голосов
/ 02 июля 2019

Я новичок в C и запутался в двух разных случаях использования *. Я понимаю, что использование * называется разыменованием и используется для получения значения, на которое указывает указатель.

int num = 20;
int *ip;
ip = #

print("This is the pointer's address: %p\n", ip);     // bffd8b3c
print("This is the value of the pointer: %d\n", *ip); // 20

Я думаю об этом так: * это похоже на оператор, где при применении к указателю дает значение в контексте оператора печати .

В другом месте, я видел * используется следующим образом:

void someFunction(int **IP){

    int *anotherIP = NULL;
    *IP = anotherIP;

}

Если я использую то же мышление, то * IP должно быть значением, на которое указывает IP. Но похоже, что это не использование. Скорее, он назначает локальный указатель на указатель, на который указывает IP.

Исправление: функция назначает anotherIP указателю на IP, (т.е. *IP); IP (указатель на указатель) дважды разыменовывается при передаче в функцию (**IP).

Мой вопрос: зависит ли значение * от контекста? Если да, то о каких контекстах мне следует знать при обычном использовании? Если нет, то как мне понять приведенные выше примеры?

Кроме того, в чем причина использования двойных звездочек в функции выше? Я вижу, что это часто используется в функциях.

Спасибо!

Ответы [ 3 ]

1 голос
/ 02 июля 2019

В объявлении унарный * указывает на то, что объявленная вещь имеет тип указателя:

T *p;       // p has type "pointer to T"
T *p[N];    // p has type "array of pointer to T"
T (*p)[N];  // p has type "pointer to array of T"
T *p();     // p has type "function returning pointer to T"
T (*p)();   // p has type "pointer to function returning T"

Операторы нижнего индекса [] и вызова функции () имеют более высокий приоритет, чем унарные *, поэтому такое выражение, как *p[i], будет проанализировано как *(p[i]); вы разыменовываете результат p[i]. Если p является указателем на массив чего-либо, то вы должны написать (*p)[i] - вам нужно разыменовать p и затем добавить в результат.

Вы можете иметь несколько уровней косвенности:

T **p;           // p has type "pointer to pointer to T"
T ***p;          // p has type "pointer to pointer to pointer to T"
T *(*p)[N];      // p has type "pointer to array of pointer to T"
T *(*(*p)())[N]; // p is a pointer to a function returning a pointer
                 // to an N-element array of pointer to T

В выражении унарный оператор * разыменовывает указатель для доступа к указанному объекту, как в вашем примере. Опять же, вы можете иметь несколько уровней косвенности:

int x = 10;    
int *p = &x;    // p stores the address of x
int **pp = &p;  // pp stores the address of p

После этих заявлений верно следующее:

**pp == *p ==  x == 10 // all of these expressions have type int
 *pp ==  p == &x       // all of these expressions have type int *
  pp == &p             // all of these expressions have type int **
 &pp                   // this expression has type int ***

Синтаксис объявления C построен на типе выражений . Предположим, у вас есть указатель на int с именем p и вы хотите получить доступ к этому целочисленному значению. Вы бы написали выражение вроде

x = *p;

Тип выражения *p равен int, поэтому объявление записывается как

int *p;

Если у вас есть массив указателей на int с именем p и вы хотите получить доступ к целочисленному значению, указанному i '-ым элементом, вы должны написать

x = *p[i];

Опять же, тип выражения *p[i] равен int, поэтому объявление записывается как

int *p[N];

Множественная косвенность проявляется в двух основных местах:

Вы хотите, чтобы функция записывала параметр типа указателя

Помните, что C передает все аргументы функции по значению, поэтому, если вы хотите, чтобы функция записывала параметр, вы должны передать указатель на этот параметр:

void foo( T *ptr )
{
  *ptr = new_value();  // writes a new value to the thing ptr points to
}

void bar( void )
{
  T value;
  foo ( &value );     // writes a new value to value
}

Заменим T типом указателя P *:

void foo( P **ptr )
{
  *ptr = new_value();  // writes a new value to the thing ptr points to
}

void bar( void )
{
  P *value;
  foo ( &value );     // writes a new value to value
}

Как видите, он работает так же. Мы хотим обновить содержимое value, поэтому мы должны передать указатель на него. Да, value уже имеет тип указателя, но нам нужен указатель на value самого , чтобы обновить его.

Помните, что если lvalue-выражение x имеет тип T, то выражение &x имеет тип T *. Снова замените T указателем типа P *, и вы увидите, что выражение &x имеет тип P **.

Вы хотите выделить зубчатый массив

Иногда вам нужна двумерная (или трехмерная, или выше) структура в виде массива, но вы не хотите, чтобы строки были одинакового размера. Обычный способ сделать это так:

T **arr = malloc( N * sizeof *arr ); // allocate an array to hold N objects of type T *

if ( arr )
{
  for ( size_t i = 0; i < N; i++ )
  {
    arr[i] = malloc( M * sizeof *arr[i] ); // allocate an array to hold M objects of type T, where M can vary from row to row
  }
}

arr указывает на первое в последовательности указателей на T.

1 голос
/ 02 июля 2019

Во-первых, в C объявление переменной должно выглядеть как ее доступ.Имея в виду реальное значение указателя на указатель на int, это значение int, поэтому вы получаете к нему доступ с помощью int value = **IP.

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

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

0 голосов
/ 02 июля 2019

Насколько я знаю, есть три «использования» звездочки в языке C.

Там есть умножение:

int result = 3 * 3; /* result == 9 */

Есть определение:

int *pointer_to_result = &result; /* define a pointer to an int, and initialize it with the address of the result variable */

И, наконец, для разыменования:

int copy_of_result = *pointer_to_result /* copy_of_result == 9 */

То, что вы видите с двойной звездочкой, это указатель на указатель .Читайте об этом.Они очень веселые.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...