LINUX.ORG.RU

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

 ,


1

3

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

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

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


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

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

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

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

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

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

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

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

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

int* a, b, c, d;

просто в сишечке так делать не кошерно(хотя чисто технически возможно)

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

int a = 10;
int* p = &a; // выглядит и читается нормально

int *pa = &a, b = 30; // технически допустимая, но в смысле чтения и понимания - неудобоваримая конструкция
olelookoe ★★★
()
Ответ на: комментарий от Ivan_qrt

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

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

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

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

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

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

int* разумеется. но не потому, что напиcан тип int*, а потому что написан тип int, а перед «а» стоит звездочка. если б звездочка была и перед «b», то тип b тоже был бы int*

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

Если бы сишка была продуманным языком - да.

Сишка предоставляет много «вольготности» для программиста и полагается на то, что программисту такой код удобен/нужен.
Поэтому запросто можно в исходном коде нагородить «ляпов».
Проще говоря Си - язык скорее для системного программирования, а не для разного рода прикладных задач.
У меня - так.

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

Начало какой-нибудь функции в типичном старом Си-коде могло выглядеть как-то так:

int a, b, b2, i, *s, *d; char c, *pc;

И дальше в том же духе лапша с кучей арифметики на указателях и байтоё***твом.

Вот тебе и практические соображения. Особенно когда у тебя для работы с кодом - ч/б экран 80x25 знакомест.

На фоне Паскаля или Алгола с их простынями текста, это даже могло являться киллер-фичей.

А незнание теории типов тут ни при чём. В 70-80-е системными вещами тоже не дураки занимались, теоретический фундамент для различных парадигм ЯП и их систем типов уже либо прочно существовал, либо активно развивался, в зависимости от направления.

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

Сишка предоставляет много «вольготности» для программиста и полагается на то, что программисту такой код удобен/нужен.

Тут вопрос не о вольготности, а о продуманности/логичности. То, что сделано в сишке с объявлениями переменных - это тупость, а не вольготность.

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

Я вообще плохо понимаю, с чего кто-то подумал что * это не часть типа? Ну это же прям очевидно.

struct some_type{
...
...
...

} a, *b, **c;

ну где тут обьявлен тип - двойной указатель на структуру для ‘с’? его нет. он выводится по двум звездочкам, заносится компилятором в таблицу типов, и привязывается к ‘с’.

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

там обьявление типа-функция, а не прототипа. но не переменной.

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

Я же не учу других хаскелю или перлу.

#include <stdio.h>

static int a[2] = {0, 0};

int const * foo(double x)
{
    int *p = &a[x >= 0.0 ? 0 : 1];
    (*p)++;
    return p;
}

int main()
{
    int const * (* const f)(double) = foo;
    printf("%d\n", *f(1));
    printf("%d\n", *f(-1));
    printf("%d\n", *f(2));
    printf("%d\n", *f(-2));
    return 0;
}
wandrien ★★
()
Ответ на: комментарий от alysnix

ну где тут обьявлен тип - двойной указатель на структуру для ‘с’? его нет. он выводится по двум звездочкам, заносится компилятором ы таблицу типов, и привязывается к ‘с’.

А где тут int** a объявлен двойной указатель на int для a? Его нет. Он выводится по двум звёздочкам и далее по тексту.

Как что либо из этого меняет тот факт, что * относится к типу (является квалификатором типа) и меняет только тип?

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

Вот тебе и практические соображения. Особенно когда у тебя для работы с кодом - ч/б экран 80x25 знакомест.

У меня экран больше, но это не повод транжирить место на нём на всякие украшательства. Бесит, когда то, что можно уместить на одну-две строки, растягивают в мусорную простыню на полэкрана. Какое-нить вместо

for(i=1; i<10; i++) for(j=i; j<10; j++) if(a[i][j]>5) printf("%u,%u\n", i, j);
вот такую чушь:
/* some useless
 * multiline comment */

for (i = 1; i < 10; i++)
  {
    for (j = i; j < 10; j++)
      {
        if (a[i][j] > 5)
          {
            printf ("%u,%u\n", i, j);
          }
      }
  }

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

но не того, что слева записан, а того, что будет применён к конкретной переменной.

Мда. А какой тип будет применён к переменной? Тот который записан слева с применёнными к нему квалификаторами или какой-то другой? А если именно тот, который записан слева, то с чего вдруг это не его квалификаторы, а квалификаторы чего-то другого?

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

Я знаю этот довод, т.к. много раз это слышал. Тут отчасти справедливо, но отчасти это не стали менять в новых стандартах из-за легаси кода.

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

Из реального впопенсорца, который я рефакторю. Экономия на скобочках:

static void on_copy(GtkAction* act, FmFolderView* fv)
{
    _init_quarks();

    GtkMenu *popup = g_object_get_qdata(G_OBJECT(fv), popup_quark);
    GtkWidget *win = gtk_menu_get_attach_widget(popup);
    GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));

    /* check if we copy inside the view; for desktop focus will be NULL */
    if(focus == NULL || gtk_widget_is_ancestor(focus, GTK_WIDGET(fv)))
    {
        FmPathList* files = fm_folder_view_dup_selected_file_paths(fv);
        if(files)
        {
            fm_clipboard_copy_files(win, files);
            fm_path_list_unref(files);
        }
    }
    else if(GTK_IS_EDITABLE(focus) && /* fallback for editables */
            gtk_editable_get_selection_bounds((GtkEditable*)focus, NULL, NULL))
        gtk_editable_copy_clipboard((GtkEditable*)focus);
}

И такое там везде.

wandrien ★★
()
Ответ на: комментарий от wandrien
static void on_copy(GtkAction* act, FmFolderView* fv)
{
    _init_quarks();

    GtkMenu   *popup = g_object_get_qdata(G_OBJECT(fv), popup_quark);
    GtkWidget *win   = gtk_menu_get_attach_widget(popup);
    GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));

// check if we copy inside the view; for desktop focus will be NULL
//
    if ( focus == NULL ||
         gtk_widget_is_ancestor(focus, GTK_WIDGET( fv ) )
       ) {

     FmPathList  *files = 
      fm_folder_view_dup_selected_file_paths( fv );

      if (files) {

       fm_clipboard_copy_files(win, files);
       fm_path_list_unref(files);

      }

      return;

    }

    if ( GTK_IS_EDITABLE(focus) && /* fallback for editables */
         gtk_editable_get_selection_bounds((GtkEditable *) focus, NULL, NULL)
       )
     gtk_editable_copy_clipboard((GtkEditable*)focus);

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

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

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

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

Как это не указан?

int a, *b;

Сишным по серому написано: указатель на int.

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

Твой вариант с одной стороны не такой мусорный как «плохой» из двух моих, но с другой (это уже наверно личное предпочтение у меня) фигурные скобки при теле цикла/ифа на отдельной строчке всё-таки нужны, иначе нет наглядного понимания где он кончается.

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

Квалификаторы не относятся к базовому типу непосредственно, они с ним комбинируются

А комбинируются - это не относятся? Квалификаторы буквально меняют тип, но никакого отношения к нему не имеют, понятно.

Ладно, в любом случае это всё ни на что не влияет и это обсуждение просто бессмысленное словоблудие. По делу всё уже было сказано.

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

Нет, тут есть:

1) базовый тип = int

2) «a» базового типа

3) «b» - указатель на базовый тип

«указатель на int» тут нигде цельным куском не встречается

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

Вот кстати довод против данного аргумента:

#include <iostream>

using namespace std;

typedef char* CharPtr;
// или так:
//using CharPtr = char*;

CharPtr ch0, ch1;

int main()
{
    std::cout << typeid(ch0).name() << '\n' << typeid(ch1).name() << std::endl;
}
char * __ptr64
char * __ptr64

Почему же тут тип указателя применился к двум переменным?

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

Потому что в строке typedef char* CharPtr ты применил звёздочку для имени «CharPtr», и теперь CharPtr - это тип указателя. Дальше ты его, как тип указателя, можешь применять к переменным. Но провернуть тоже самое без typedef нельзя - звёздочка всегда цепляется к имени, а не у базовому типу. Просто typedef позволяет назначить явное имя производному типу.

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

Почему же тут тип указателя применился к двум переменным?

Потому что * с т.з. интерпретатора присоединяется не к типу, а к переменной.

int *idx; тут * прилипает к переменной idx, новый тип int * не создается.

А запись typedef char* CharPtr; создает новый тип, это как если бы ты написал (что синтаксис не позволяет) (char *) ch0, ch1;.

PS: Пример – отличный!

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

В 70-80-е системными вещами тоже не дураки занимались

А при чём тут дураки, когда best practices только на опыте вырабатываются? И Дейкстра свою заметку про goto как раз писал в контексте того как в тогда в мейнстриме было принято писать код.

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