Как понять указатель звезды * в C? - PullRequest
32 голосов
/ 30 марта 2011

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

Например:

int *i; // i is a pointer to an int

Но что это залогика за синтаксисом?Что означает * перед тем, как я имею в виду?Давайте возьмем следующий пример.Пожалуйста, поправьте меня, где я не прав:

char **s;
char *(*s); // added parentheses to highlight precedence

И вот тут я теряю след.* S между параграфами означает: s это указатель?Но указатель на что?И что означает * вне скобок: указатель на то, на что указывает s?

Значит, это означает: указатель на то, на что указывает s, является указателем на символ?

Я в растерянности.Знаки * интерпретируются по-разному в объявлениях и выражениях?Если так, как это интерпретируется по-другому?Куда я иду не так?

Ответы [ 10 ]

65 голосов
/ 30 марта 2011

Примите это следующим образом:

int *i означает значение, на которое указывает i, является целым числом.

char **p означает, что p является указателем, который сам по себе является указателем наВст. enter image description here

50 голосов
/ 30 марта 2011
int i; //i is an int.
int *i; //i is a pointer to an int
int **i;//i is a pointer to a pointer to an int.

Различен ли знак * в объявлениях и выражениях по-разному?

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

Некоторые примеры:

int i = 10; //i is an int, it has allocated storage to store an int.
int *k; // k is an uninitialized pointer to an int. 
        //It does not store an int, but a pointer to one.
k = &i; // make k point to i. We take the address of i and store it in k
int j = *k; //here we dereference the k pointer to get at the int value it points
            //to. As it points to i, *k will get the value 10 and store it in j
22 голосов
/ 30 марта 2011

Правило объявления в c таково: вы объявляете его так, как используете.

char *p означает, что вам нужно *p, чтобы получить чар,

char **p означает, что вам нужно **p, чтобы получить чар.

11 голосов
/ 30 марта 2011

Объявления в C ориентированы на выражения, что означает, что форма объявления должна соответствовать форме выражения в исполняемом коде.

Например, предположим, что у нас есть указатель на целое число с именем p,Мы хотим получить доступ к целочисленному значению, указанному p, поэтому мы разыменовываем указатель, например:

x = *p; 

Тип выражения *p - это int;поэтому объявление p принимает форму

int *p;

В этом объявлении int является спецификатором типа , а *p является декларатором .Декларатор вводит имя объявляемого объекта (p) вместе с дополнительной информацией о типе, не предоставляемой спецификатором типа.В этом случае дополнительная информация о типе состоит в том, что p является типом указателя.Объявление можно прочитать как «p имеет указатель типа на int» или «p является указателем на тип int».Я предпочитаю использовать вторую форму, другие предпочитают первую.

Это случайность синтаксиса C и C ++, когда вы можете написать это объявление как int *p; или int* p;.В обоих случаях он анализируется как int (*p); - другими словами, * всегда ассоциируется с именем переменной, а не с указателем типа.

Теперь предположим, что у нас есть массив указателей на int, и мы хотим получить доступ к значению, на которое указывает i-й элемент массива.Мы добавляем в массив и разыменовываем результат, как показано ниже:

x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
            // than dereference.

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

int *ap[N];

, где декларатор *ap[N] означает, что ap является массивом указателей на int.

И просто, чтобы обозначить точку, теперь предположим, что у нас есть указатель на указатель на int и мы хотим получить доступ к этому значению.Опять же, мы обращаемся к указателю, а затем к получению целого значения:

x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp

Поскольку тип выражения **pp равен int, объявление равно

int **pp;

Декларатор **pp указывает, что pp является указателем на другой указатель на int.

Двойная косвенность часто проявляется, когда вы хотите изменить значение указателя, передаваемого в функцию, например:

void openAndInit(FILE **p)
{
  *p = fopen("AFile.txt", "r");
  // do other stuff
}

int main(void)
{
  FILE *f = NULL;
  ...
  openAndInit(&f);
  ...
}

В этом случае нам нужна функцияобновить значение f;чтобы сделать это, мы должны передать указатель на f.Поскольку f уже является типом указателя (FILE *), это означает, что мы передаем указатель на FILE *, следовательно, объявление p как FILE **p.Помните, что выражение *p в openAndInit относится к тому же объекту, что выражение f в main.

И в объявлениях, и в выражениях оба значения [] и () имеют более высокий приоритет, чем унарный *.Например, *ap[i] интерпретируется как *(ap[i]);выражение ap[i] является типом указателя, а * разыменовывает этот указатель.Таким образом, ap - это массив указателей .Если вы хотите объявить указатель на массив , вы должны явно сгруппировать * с именем массива, например так:

int (*pa)[N]; // pa is a pointer to an N-element array of int

и когда вы хотите получить доступ к значениюв массиве необходимо применить pa перед применением индекса:

x = (*pa)[i];

Аналогично функциям:

int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value

int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value
9 голосов
/ 30 марта 2011

Мой любимый способ разбора сложных объявлений - правило по часовой стрелке-спирали .

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

В статье не упоминаются две вещи:

1- Вы должны отделить спецификатор типа (int, char и т. Д.) От декларатора, проанализировать декларатор и затем добавить спецификатор типа.

2- Если вы встречаете квадратные скобки, которые обозначают массив, обязательно прочитайте также следующие квадратные скобки (если они есть).

5 голосов
/ 30 марта 2011

int * i означает, что я - указатель на int (чтение назад, чтение * как указатель).char **p и char *(*p) означают указатель на указатель на символ.

Вот еще несколько примеров

int* a[3] // a - массив из 3 указателей на int

int (*a)[3] // a - указатель на массив из 3Интс

2 голосов
/ 30 марта 2011

У вас есть ответ на ваши вопросы.

Действительно, двойная звезда используется для обозначения указателя на указатель.

1 голос
/ 30 марта 2011

Объявление * в означает, что переменная является указателем на некоторую другую переменную / константу.это означает, что он может содержать адрес переменной типа.например: char *c; означает, что c может содержать адрес для некоторого символа, в то время как int *b означает, что b может содержать адрес некоторого типа int, тип ссылки важен, поскольку в арифметике указателей pointer + 1 на самом деле pointer + (1 * sizeof(*pointer)).

Выражение * in означает «значение, сохраненное в адресе», поэтому, если c является указателем на какой-либо символ, тогда *c является конкретным символом.

char *(*s); означает, что s - указатель на указатель на символ, поэтому s не содержит адрес символа, но адрес переменной, которая содержит адрес символа.

0 голосов
/ 14 января 2016

Объявление &a означает, что оно указывает на *i.Ведь это указатель на *int.Целое число должно указывать *i.Но если учесть, что j = *k является указателем на указатель, это означает, что &k будет значением k, а k будет иметь указатель на *int.

0 голосов
/ 30 марта 2011

немного информации

         variable    pointer
declaring     &a           p

reading/      a            *p

processing
...