Подтвердить что ты не робот

Как понять звезду указателя * в C?

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

Например:

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

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

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

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

Таким образом, значение этого параметра: Указатель, указывающий на то, что указывает s, является указателем на char?

Я в недоумении. Является ли знак * интерпретирован по-разному в декларациях и выражениях? Если да, то как оно интерпретируется по-разному? Где я ошибаюсь?

4b9b3361

Ответ 1

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

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

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

Ответ 2

Возьмите это так:

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

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

Ответ 3

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

Ответ 4

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

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

x = *p; 

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

int *p;

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

Это случайность синтаксиса 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

Ответ 5

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

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

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

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

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

Ответ 6

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

Здесь приведены некоторые другие примеры

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

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

         variable    pointer
declaring     &a           p

reading/      a            *p

processing

Ответ 10

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