Ответ 1
4, 5 и 6 - одно и то же, только тест - это указатель. Если вам нужны два указателя, вы должны использовать:
int *test, *test2;
Или, еще лучше (чтобы все было ясно):
int* test;
int* test2;
Недавно я решил, что мне просто нужно наконец изучить C/С++, и есть одна вещь, которую я действительно не понимаю в указателях или, точнее, их определении.
Как насчет этих примеров:
int* test;
int *test;
int * test;
int* test,test2;
int *test,test2;
int * test,test2;
Теперь, насколько я понимаю, первые три случая все делают одно и то же: Test не int, а указатель на один.
Второй набор примеров немного сложнее. В случае 4 оба теста и test2 будут указателями на int, тогда как в случае 5 только тест является указателем, тогда как test2 является "реальным" int. Как насчет случая 6? То же, что и в случае 5?
4, 5 и 6 - одно и то же, только тест - это указатель. Если вам нужны два указателя, вы должны использовать:
int *test, *test2;
Или, еще лучше (чтобы все было ясно):
int* test;
int* test2;
Белое пространство вокруг звездочек не имеет значения. Все три означают одно и то же:
int* test;
int *test;
int * test;
"int *var1, var2
" - это злой синтаксис, который просто предназначен для смущения людей и его следует избегать. Он расширяется до:
int *var1;
int var2;
Используйте "Правило спирали по часовой стрелке", чтобы помочь разобрать объявления C/C++;
Есть три простых шага:
Начиная с неизвестного элемента, двигайтесь по спирали/по часовой стрелке; при обнаружении следующих элементов замените их соответствующими английскими выражениями:
[X]
или[]
: размер массива X... или неопределенный размер массива...
(type1, type2)
: функция, передающая type1 и type2, возвращающая...
*
: указатель (и) на...- Продолжайте делать это по спирали/по часовой стрелке, пока все жетоны не будут закрыты.
- Всегда сначала разрешите что-нибудь в скобках!
Кроме того, объявления должны быть в отдельных заявлениях, когда это возможно (что верно в подавляющем большинстве случаев).
Многие рекомендации по кодированию рекомендуют только объявлять одну переменную в строке. Это позволяет избежать какой-либо путаницы в вашем роде, прежде чем задавать этот вопрос. Большинство программистов на C++, с которыми я работал, похоже, придерживаются этого.
Немного в стороне, я знаю, но что-то, что мне показалось полезным, - это прочитать декларации назад.
int* test; // test is a pointer to an int
Это начинает работать очень хорошо, особенно когда вы начинаете объявлять константные указатели, и становится сложно определить, является ли это указателем const или его указатель указывает на const.
int* const test; // test is a const pointer to an int
int const * test; // test is a pointer to a const int ... but many people write this as
const int * test; // test is a pointer to an int that const
Как упоминалось выше, 4, 5 и 6 одинаковы. Часто люди используют эти примеры, чтобы аргумент, который *
принадлежит переменной вместо типа. Хотя это проблема стиля, есть некоторые дебаты о том, следует ли вам думать и писать так:
int* x; // "x is a pointer to int"
или следующим образом:
int *x; // "*x is an int"
FWIW Я нахожусь в первом лагере, но причина, по которой другие делают аргумент для второй формы, состоит в том, что она (в основном) решает эту конкретную проблему:
int* x,y; // "x is a pointer to int, y is an int"
что потенциально вводит в заблуждение; вместо этого вы должны написать либо
int *x,y; // it a little clearer what is going on here
или если вам действительно нужны два указателя,
int *x, *y; // two pointers
Лично я утверждаю, что поддерживаю его по одной переменной в строке, тогда не имеет значения, какой стиль вы предпочитаете.
#include <type_traits>
std::add_pointer<int>::type test, test2;
В 4, 5 и 6, test
всегда является указателем, а test2
не является указателем. Белое пространство (почти) никогда не бывает значительным в С++.
На мой взгляд, лучше ставить звездочку рядом с именем указателя, а не над типом. Сравните, например:
int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2; // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors
Почему второй случай не согласуется? Потому что, например, int x,y;
объявляет две переменные одного типа, но тип упоминается только один раз в объявлении. Это создает прецедент и ожидаемое поведение. И int* pointer1, pointer2;
не согласуется с этим, потому что он объявляет pointer1
в качестве указателя, но pointer2
является целочисленной переменной. Явно подвержен ошибкам и, следовательно, его следует избегать (поставив звездочку рядом с именем указателя, а не с типом).
Тем не менее,, есть некоторые исключения, когда вы не сможете поставить звездочку рядом с именем объекта (и там, где это важно, где вы его поместили) без получения нежелательного результата - например:
MyClass * volatile MyObjName
Указатель является модификатором типа. Лучше всего прочитать их справа налево, чтобы лучше понять, как звездочка модифицирует тип. 'int *' может быть прочитан как "указатель на int". В нескольких объявлениях вы должны указать, что каждая переменная является указателем, или она будет создана как стандартная переменная.
1,2 и 3) Тест имеет тип (int *). Пробел не имеет значения.
4,5 и 6). Тест имеет тип (int *). Test2 имеет тип int. Опять пробелы несущественны.
Вы можете думать о 4, 5 и 6 следующим образом: объявление типа должно выполняться только один раз, но если вы хотите объявить указатель на этот тип (добавив звездочку), вы должны сделать это для каждой переменной.
При объявлении переменной указателя я всегда добавляю пробел между переменной и звездочкой, даже если я объявляю более одного в строке. Не делать этого заставляет меня путать его с выражением разыгрывания почти каждый раз.
Обоснование в C состоит в том, что вы объявляете переменные так, как вы их используете. Например
char *a[100];
говорит, что *a[42]
будет char
. И a[42]
a char указатель. Таким образом, a
представляет собой массив указателей char.
Это потому, что оригинальные авторы компилятора хотели использовать один и тот же парсер для выражений и деклараций. (Не очень разумная причина выбора дизайна langage)
Я бы сказал, что первоначальная конвенция заключалась в том, чтобы поместить звезду в сторону имени указателя (правая часть объявления
на языке программирования C Деннисом М. Ричи звезды находятся на правой стороне декларации.
посмотрев исходный код linux на https://github.com/torvalds/linux/blob/master/init/main.c мы видим, что звезда также находится на правой стороне.
Вы можете следовать тем же правилам, но это не имеет большого значения, если вы ставите звезды на сторону типа. Помните, что важна последовательность, поэтому всегда, но звезда на той же стороне, независимо от того, какую сторону вы выбрали.
Случаи 1, 2 и 3 одинаковы, они объявляют указатели на переменные int. Случаи 3, 4 и 5 совпадают, так как они объявляют один указатель и одну переменную int соответственно. Если вы хотите сделать два указателя в одной строке (что вам не нужно), вам нужно поставить звездочку перед каждым именем переменной:
int *test, *test2;
Нет определенного правильного способа, указывающего, куда идет звездочка. int* test
выглядит лучше, потому что нам легче представить, что добавление *
в конец типа означает "указатель на" этого типа. Однако int *test
имеет больше смысла, потому что вы можете работать с ним, как знак минуса в математике:
-(-x) = x
аналогично
*(*test) = test
Это всегда помогало мне. К сожалению, все дело в том, что иногда я использую int* test
, а иногда int *test
.
Хорошее эмпирическое правило, многие люди, похоже, понимают эти концепции: В С++ много семантического значения вызывается левым связыванием ключевых слов или идентификаторов.
Возьмем, например:
int const bla;
Константа применяется к слову "int". То же самое со звездочками указателей, они применяются к ключевому слову слева от них. И имя фактической переменной? Да, это было сказано тем, что осталось от него.