Как можно * объявить указатели и указатели разыменования?

Я не понимаю, как работает оператор разыменования в объявлениях указателей.

Рассмотрим оба этих сценария:

int y = 5;
int *x = &y;
int y = 5;
int *x;
x = &y;

Второй сценарий (отдельное объявление и инициализация) имеет для меня смысл. Прочитав множество подобных вопросов, я заметил, что объявление похоже на использование. То есть *x — целое число, поэтому x — указатель на целое число. Следовательно, присвоение x указателю y имеет смысл.

Что меня смущает, так это первый сценарий (комбинированное объявление и инициализация). Я изо всех сил пытаюсь понять, почему int *x = &y; должно работать. *x не является указателем, так зачем же его инициализировать указателем?

У меня было несколько попыток рационализировать этот первый сценарий. В основном я пытаюсь определить, имеет ли объявление в C специальную грамматику, где значение, присвоенное с помощью int *x = &y;, равно x, а не *x. Ничто другое не имеет для меня смысла.

Одно из объяснений, которое я видел и которое меня не очень удовлетворило, - это объяснение более высокого уровня «относиться к int* как к типу». Меня это не устраивает, потому что оно не подходит для int* x, z.

int* (сам по себе) не является типом. int — это тип. Рассмотрим int b, *x;. b — это int, но x — это указатель на int. Вы можете сделать: int y = 5, *x = &y;* привязано справа (а не слева). Когда вы сказали: *x не указатель, это неверно. x — указатель. Когда вы делаете: int *x = &y;, это инициализация x. Когда вы делаете: int *x; x = &y;, это присваивание (а не инициализация).
Craig Estey 03.04.2024 03:58

Вопрос упускает из виду тот факт, что * также является оператором двоичного умножения. Почему это не добавляет путаницы? Или, говоря иначе, почему бы это не прояснить путаницу? Синтаксис объявления и синтаксис выражений совершенно разные, как это определено грамматикой языка.

paddy 03.04.2024 04:05

Учитывая int* x,y,z;, это оптическая иллюзия. Похоже, мы объявляем три указателя int: int *x; int *y; int *z; Но это неверно. Лучший способ записать это: int *x,y,z; и на самом деле: int *x; int y; int z; (указатель и два скаляра). Если нам нужны три указателя, мы бы использовали: int *x,*y,*z;

Craig Estey 03.04.2024 04:06

Значение символов в деклараторах отличается от значения тех же символов в выражениях.

M.M 03.04.2024 04:47

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

David C. Rankin 03.04.2024 05:10

Это можно прояснить на простом примере (поскольку он пояснит, почему лучше привязывать * к переменной, а не к типу). Избегайте писать int* a, b, c;. Почему? Что декларируется? Это, конечно, НЕ 3 указателя на int. Вместо этого он объявляет один указатель на int (a) и два целых числа (b, c). Написание int *a, b, c; проясняет это....

David C. Rankin 03.04.2024 05:14

@CraigEstey «int* (сам по себе) не является типом». Это неправильно. C17 6.2.5, формальное определение типа указателя: «Тип указателя может быть производным от типа функции или типа объекта, называемого ссылочным типом. Тип указателя описывает объект, значение которого предоставляет ссылку на объект ссылочный тип. Тип указателя, производный от ссылочного типа T, иногда называют «указателем на T».

Lundin 03.04.2024 08:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
90
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Унарный * всегда представляет собой разыменование.

В выражении это может показаться знакомым и интуитивно понятным: *x означает получить значение, на которое указывает x. Напротив, вам может показаться, что объявление менее интуитивно понятно; что означает * в int *x?

Об этом можно думать так: декларация дает представление о том, как что-то будет использоваться. В декларации int *x говорится, что *x будет использоваться как int. Другими словами, когда мы получаем то, на что указывает x, это int. * в объявлении означает то же самое, что и в выражении: разыменование указателя.

Керниган и Ритчи рассказывают нам об этом в книге «Язык программирования C», 1978, стр. 90:

Объявление указателя px новое.

int *px;

предназначен для мнемоники; он говорит, что комбинация *px является int, то есть, если px встречается в контексте *px, это эквивалентно переменной типа int. По сути, синтаксис объявления переменной имитирует синтаксис выражений, в которых может появиться эта переменная.

В качестве более сложного примера рассмотрим int (*p)[];. Это говорит нам о том, что (*p)[] — это int. Поскольку [] используется для доступа к элементам массива, это означает, что (*p) должен быть массивом int. А это значит, что p должен быть указателем на массив int. Как и *, [] не имеет обратного значения в объявлениях. Это не означает «является массивом» вместо «доступа к элементу»; это все еще образ того, как эта вещь будет использоваться в выражении.

В int *x = &y; символ = не означает присваивание. Это означает инициализацию. И инициализируется не выражение в левой части =, потому что в левой части нет реального выражения. В объявлении int *x, *x не является выражением; это всего лишь изображение того, как x будет использоваться в выражении. В int *x = &y;int *x объявляет x, поэтому инициализируется x, а не *x присваивается.

Понятно, спасибо, что прояснили мою путаницу по поводу инициализации/назначения.

tnr 03.04.2024 03:57

Для полноты картины вы можете поговорить о разнице между int y = 5; int *x = &y; *x = 7; и int y = 5; int *x; x = &y; *x = 7;, то есть int *x = ... и *x = ....

Craig Estey 03.04.2024 04:15

Другие вопросы по теме