LINUX.ORG.RU

По-настоящему важный вопрос

 ,


1

3

Куда вы ставите звёздочки и амперсанды при объявлении/инициализации указателей и ссылок, и почему?

  1. T* name, T& name
  2. T * name, T & name
  3. T *name, T &name
  4. Я талиб, я везде использую передачу по значению

P.S. Это не тупой наброс, мне правда интересно кто как делает, может есть весомые причины делать так или иначе, которые открываются с опытом.

В некоторых личных проектах было вот так:

T * name, T & name

(Уже не помню, почему, наверное от какого-то существующего кода привычку унаследовал.)

В большинстве случаев делаю так:

T* name, T& name

Но во многих проектах используется:


T *name, T &name

поэтому приходится перестраиваться.

Еще из той же области вопрос про скобки:

if () или if()

somefunction () или somefunction()

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)

int* a, b; - это для тех кто думает, что int* это некий самостоятельный тип «указатель на целое». таких типов в сишечке нет, поэтому b - это целое, а не то, что иногда некоторым кажется.

int * a; - это для нерешительных.

а вот так все понятно и однозначно - int *a, *b, с;

olelookoe ★★★
()
Ответ на: комментарий от olelookoe

а не то, что иногда некоторым кажется

Да, я пару раз на таком попадался, с тех пор стараюсь указатели отдельными строками объявлять. Да и вообще, стараюсь инициализировать переменные сразу при объявлении, поэтому несколько переменных в одной строке объявляю крайне редко.

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 1)

На самом деле вопрос больше философский. Большинство выбирает между #1 и #3. Емнип - даже Страуструп писал на эту тему - первое больше «C++» style (type centristic), второе больше C-шное и более «корректно» с точки зрения синтаксиса. Если не объявлять больше одной переменной в одном statement - по-большому счёту параллельно…

bugfixer ★★★★★
()

Для меня уже привычно, что *name – это разыменование указателя, а &name – это взятие адреса. Поэтому * и & прижимаю к именам спереди именно в таком контексте.

Прижимать * и & к именам сзади не люблю, т.к. зрение уже не то :(

Поэтому практичнее оказывается как во втором варианте.

Но полностью согласен с @bugfixer о том, что если на одной строке декларируется всего одна переменная (или один параметр функции/метода), то особой разницы нет.

eao197 ★★★★★
()

не все еще врубились, что исторически звездочка как разыменование встала перед именем переменной только потому, что в си израсходовали все мыслимые символы, и для разыменования вменяемого символа не осталось и что бы ни поставить после имени (типа крышечки как алголоподобных) - все равно возникла бы неоднозначность с какой либо операцией. крышечку си забрал под XOR, а знак умножения ставить после переменной нельзя - будет путаница с обычным умножением.

поставили впереди, отчего пришлось писать (*ptr).field. и чтобы не писать так муторно, ввели ->.

карочи. в обьявлении переменной или параметра * - это не часть типа, а часть декларации переменной. оттого все эти

int *a,*b,*c

но в декларации типа * это часть типа. то есть имеется некая путаница.

карочи, по правде, эти «модификаторы декларации» надо писать у имени переменной. для того чтобы тип выглядел целостно, их пишут у типа.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от wandrien

вообще, по чисто сишной логике деклараций

const T1 *val

канонически читается так - если я разименую val, то получу значение const T.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
  • Взятие адреса только ... &name;.
  • С объявлением ситуативно type * name; или type *name; если маламестаtype*name;
  • Когда идёт разыменование то только ... *name;

Причина проста, наглядность. Если для наглядности удобнее будет сделать по другому в конкретном коде, то будет сделано по другому.

Просто это z = x ** y; выглядит прикольно, но z = x * *y выглядит понятнее.

В так когда для себя любимого, как хоца так и делаю.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от alysnix

Вообще по логике сишного const - он ставится ПОСЛЕ того, к чему он относится.

int const * - Указатель на константное целое.

int const * const - Константный указатель на константное целое.

Запись вида const int существует как альтернатива чисто для удобства, потому что в английском прилагательные принято ставить перед тем, к чему они относятся.

wandrien ★★
()

Естественно, при объявлении переменной — рядом с именем переменной. Иллюстрация, почему надо делать именно так:

int* p, q;

какой тип у q?

При этом

int *p, q;

всё очевидно.

При объявлении типов, функций и т. д. — рядом с Т. Сразу видно, что возвращает функция T* f();

alegz ★★★★
()
Последнее исправление: alegz (всего исправлений: 1)

Везде ставлю звёздочки и амперсанды рядом с типом (1), потому что они часть типа. Приклеивать их к имени переменной, имхо, что-то сильно странное, и то, что в сишке при объявлении нескольких переменных нужно указывать * для каждой из них - это косяк сишки.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от olelookoe

int* a, b; - это для тех кто думает, что int* это некий самостоятельный тип «указатель на целое».

Внезапно, но a в твоём примере - это буквально указатель на целое. Это именно то, чем переменная a является.

То, что в сишке (и как следствие плюсах) надмозговая логика объявления типов переменных не означает, что у этих переменных какой-то другой тип. Это просто косяк сишки, не более.

Ivan_qrt ★★★★★
()

ишь, понаписали

«так все понятно и однозначно - int *a, *b, с;» (с)

понятно? однозначно? поверили? так вот, я вас обманул. нихера не однозначно.

апачиму? апатамушта

int a = 10;
int *p = &a, b = 20; // огонь, да? и это при том, что
*p = 20;
p = &b; 

продолжаем разговор )

olelookoe ★★★
()
Ответ на: комментарий от wandrien

сходи кофею хлопни что ли.

смари

int *p, b; что здесь? буквально - целое *p, целое b. так, нет? нет. почему? потому что при такой форме записи получается не то что замышляется

*p = &a, b = 20, т.е. *p - это указатель, а b - это целое.

при том что вне объявления *p это вообще-то разыменованный указатель и ему можно значение присвоить, *p = 333; например.

olelookoe ★★★
()
Ответ на: комментарий от olelookoe

сходи кофею хлопни что ли.

Идея хорошая, пойду заварю. Тем более, я ночь почти не спал. Но к вопросу это отношения не имеет.

int *p, b; что здесь? буквально - целое *p, целое b. так, нет? нет. почему? потому что при такой форме записи получается не то что замышляется

Сам себя запутываешь придумыванием нерабочих аналогий.

Звёздочка в определении типа означает «указатель». Всё. Нефиг придумывать что-то сверх этого, чтобы мозги себе не пудрить.

Если тебе так удобнее, можешь мысленно вместо этой звёздочки представлять слово pointer или PTR (дух старого асма).

Объявления типов и вычислимые выражения - два разных подраздела синтаксиса языка. Нечего их смешивать.

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)

Определения типов в Си читаются справа налево и изнутри наружу:

int const * (* const f)(double);

-- константный указатель на функцию, принимающую double и возвращающую указатель на константное целое.

А теперь без подсказки объявите функцию, возвращающую массив из 10 указателей на функции, возвращающие…

Тут где-то на прошлой неделе кто-то возмущался и недоумевал, почему в каждом убийце крестов придумывают новый синтаксис объявления типов, а не берут готовый из крестов.

Вот именно поэтому.

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)

Есть легенда, что Ритчи взял синтаксис Поцкаля и издевался над ним до тех пор, пока программа не начала успешно компилировать рандомный мусор из скобочек и звёздочек. Тогда результат его устроил.

wandrien ★★
()

Переменная характеризуется типом и адресом памяти, где находится значение данного типа. Имя переменной после компиляции не будет существовать, а будет существовать только адрес переменной, тогда как тип данных, к которому относятся данные, расположенные по данному адресу, будут актуальны после компиляции и в процессе выполнения программы. Понятие ТИП ДАННЫХ !!!включает!!! ИМЯ ТИПА, КВАЛИФИКАТОРЫ const, volatile, символы обозначающие ссылку(&) или указатель(*), соответственно нельзя говорить, что символы * / & не относятся к типу.

Код

using namespace std;

char *pc;
char c;
cout << typeid(pc).name() << endl;
cout << typeid(c).name() << endl;

Вывод:

Pc
c

В данном случае видно, что тип pc - это Pc (т.е. pointer char - указатель на char), т.е. информация о типе ВКЛЮЧАЕТ указатель.

Теперь еще мой довод с прототипом. Вот прототип функции:

void f(char* ch);

Этот прототип можно РАВНОЗНАЧНО переписать без переменной:

void f(char*);

при этом символ указателя * остался. Таким образом этот пример является доводом в пользу того, что символы * и & - это составляющие типа и логически относятся к типу, а не к имени переменной.

Еще аргумент за положение символов указателя / ссылки не рядом с переменной. Запишем константный указатель на константные данные:

double d;
const double * const variable = &d;

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

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

Этот прототип можно РАВНОЗНАЧНО переписать без переменной:

Без какой «переменной»? Беда с вами…

Это параметр функции, в одном случае у него указано имя, а в другом - нет.

wandrien ★★
()
Ответ на: комментарий от rumgot

символы * и & - это составляющие типа и логически относятся к типу, а не к имени переменной.

я так долго к этому подводил а ты взял и непедагогично раскрыл все ответы.

все, топик умер, расходимся.

olelookoe ★★★
()
Ответ на: комментарий от rumgot

Таким образом этот пример является доводом в пользу того, что символы * и & - это составляющие типа и логически относятся к типу, а не к имени переменной.

при записи типа, они относятся к типу. а при обьявлении переменных(полей класса) - они относятся к обьявляемым обьектам.

языг ключница делала, и все это - кровавое наследие си.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от alysnix

а при обьявлении переменных(полей класса) - они относятся к обьявляемым обьектам.

Они относятся к типам (и ни к чему другому) объявляемых объектов. А типы объявляемых объектов уже относятся к этим самым объявляемым объектам.

В int* p ты объявляешь переменную p с типом int*.
В const int* const p ты объявляешь переменную p с типом const int* const.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Они относятся к типам (и ни к чему другому) объявляемых объектов. А типы объявляемых объектов уже относятся к этим самым объявляемым объектам.

В int* p ты объявляешь переменную p с типом int*.

тогда все переменные списка были б типа int*

int* a, b, c, d;
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

тогда все переменные списка были б типа int*

Если бы сишка была продуманным языком - да. К сожалению это не так. Но того факта, что у переменной a тип int* это никак не меняет. Если ты с этим не согласен, то скажи какой по-твоему тип у переменной a.

Это просто косяк сишки, что у этих переменных разный тип.

Ivan_qrt ★★★★★
()