Указатели: инициализация против объявления
Я С++ noob, и я уверен, что это глупый вопрос, но я просто не совсем понимаю, почему возникает ошибка (не возникает) из следующего кода:
#include <iostream>
using namespace std;
int main()
{
int a,*test;
*test = &a; // this error is clear to me, since an address cannot be
// asigned to an integer
*(test = &a); // this works, which is also clear
return 0;
}
Но почему это тоже работает?
#include <iostream>
using namespace std;
int main()
{
int a, *test= &a; // Why no error here?, is this to be read as:
// *(test=&a),too? If this is the case, why is the
// priority of * here lower than in the code above?
return 0;
}
Ответы
Ответ 1
Основное различие между этими двумя строками
*test= &a; // 1
int a, *test= &a; // 2
- это первое выражение, состоящее из вызовов операторов с известными правилами приоритета:
operator=
/\
/ \
/ \
operator* operator&
| |
test a
тогда как второе - объявление переменной и инициализация и эквивалентно объявлению int a;
, за которым следуют:
int* test = &a
// ^^ ^^ ^^
//type variable expression giving
// name initial value
Ни во время operator*
, ни operator=
не используется во второй строке.
Значение токенов *
и =
(и &
, а также ,
) зависит от контекста, в котором они появляются: внутри выражения они стоят за соответствующие операторы, но в декларация *
обычно появляется как часть типа (что означает "указатель на" ), а =
используется для обозначения начала инициализации (копирования) (,
разделяет несколько деклараций, &
как "ссылка to" также является частью типа).
Ответ 2
int a, *test= &a;
эквивалентно:
int a;
int* test = &a;
и отлично действует при инициализации test
, который имеет тип указателя на int с адресом переменной a
, который имеет тип int.
Ответ 3
Вы смешиваете два использования для *.
В первом примере вы используете его для разыменования указателя.
Во втором примере вы используете его для объявления "указателя на int".
Итак, когда вы используете * в объявлении, он должен сказать, что вы объявляете указатель.
Ответ 4
Фактически вы делаете инициализацию в первом случае,
int *test = &a;
Это означает, что вы инициализируете указатель, для которого вы указываете *
, чтобы сообщить компилятору, что его указатель.
Но после инициализации *test
(со звездочкой) означает, что вы пытаетесь получить доступ к значению по адресу, назначенному указателю test
.
Другими словами, выполнение *test
означает, что вы получаете значение a
, потому что адрес a
хранится в указателе test
, который выполняется просто с помощью &a
.
&
- это оператор для получения адреса любой переменной. И *
- это оператор для получения значения по адресу.
Значения инициализации и выводятся по-разному компилятором, даже если в обоих случаях присутствует звездочка *
.
Ответ 5
Вы просто ударили по двум ужасным языковым пятнам: сжатие объявлений в одну строку и повторное использование символа *
для несвязанных целей. В этом случае *
используется для объявления указателя (когда он используется как часть сигнатуры типа int a,*test;
) и для обозначения указателя (когда он используется как оператор *test = &a;
). Хорошей практикой было бы объявлять переменные по одному, использовать автоматический вывод типа вместо типа copypasting и использовать выделенный метод addressof
:
#include <memory> // for std::addressof
int a{};
auto const p_a{::std::addressof(a)};
Ответ 6
Там тонкая разница.
Когда вы объявляете int a, * test, вы говорите "объявляете как целое число и объявляете тест как указатель на целое число, причем оба они неинициализированы".
В первом примере вы устанавливаете * test на & right после деклараций. Это означает: "Установите целое число, на которое указывает контрольные точки (адрес памяти), на адрес". ". Это почти наверняка приведет к сбою, потому что тест не был инициализирован, поэтому он будет либо нулевым указателем, либо тарабарщиной.
В другом примере int a, * test = & a переводит: "объявляет a как неинициализированное целое число и объявляет тест как указатель, инициализированный адресом a." Это верно. Более подробно он переводится на:
int a, *test;
test = &a;