Как стать автором
Поиск
Написать публикацию
Обновить

Комментарии 22

НЕ ИСПОЛЬЗУЙТЕ ССЫЛКИ и вы избежите почти всех проблем! (ц)

Ссылки и указатели это отличная тема для обсуждения дизайна языков программирования. Как лучше?

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

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

Если подумать то получаются как минимум следующие аспекты:

  • Явность/неявность при обращении (нужно или нет "разыменовывать")

  • Встроенная нуллабельность указателей и ненуллабельность ссылок

  • Возможность адресной арифметики

Адресная арифметика считается источником ошибок, хотя в низкоуровневом программировании от нее никуда не деться. Но это как раз решаемый вопрос, можно потреборвать чтобы она выполнялась только в каком нибудь "unsafe" контексте. В большинстве случаев указатели нужны лишь для динамически создаваемых объектов и там никакая арифметика не нужна.

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

Остается явность/неявность - самое непростое, поскольку касается самой идеологии написания кода. Иногда важно явно выразить что происходит именно обращение по указателю; а иногда нам все равно, что там внутри - переменная просто представляет объект, а как он там хранится - без разницы.В Си (еще до C++) придумали операцию "стрелка" "->" специально для явного обращения к полям структуры по указателю, хотя ничто не мешало задействовать операцию "точка" - конфликта не было бы, в Си указатель не может быть одновременно составным объектом. Сделали так именно для явного обращения, такова идеология языка. В последующих языках от этого отказались, там везде "точка". В Go работают оба способа: (*myPointer).field и myPointer.field

В большинстве случаев указатели нужны лишь для динамически создаваемых объектов

В прикладном коде. А так то ещё MMIO есть.

спасибо это классика )

И потом неожиданно обнаружить, что заметная доля времени уходит на апдейты счётчиков ссылок и прочие не имеющие отношения к целевой задаче вещи.

Но ведь это произойдёт только если сознательно выбрать смарт-поинтеры, реализующие модель совместного владения. Что, по идее, следует делать только если в совместном владении есть необходимость, и в таком случае оплата цены в виде счётчиков неизбежна.

Подумать о том, что даже если пойнтер сохраняется и используется в куче мест, владелец реально может быть один - это уже надо заморочиться )

Я подозреваю, что при таком отношении в результирующем приложении "заметная доля времени" будет уходить не на апдейты счётчиков ссылок, а на куда более прожорливые и неэффективные развлечения, по сравнению с которыми расходы на счётчики будут скорее гомеопатическими.

Reference is just a const T* (но не всегда)

const T* это неизменяемость объекта, что конечно же для не-const ссылки неверно.

T* const = T&

const T* const = const T&

Из статьи так и не понял зачем ключевое слово ref в Rust, если есть &

В ветках match знак & является частью описания типа, по которому производится попытка сопоставления, a ref - не является, он говорит "сопоставь с таким-то типом, а потом дай мне на него ссылку вместо сожрать насовсем".

Почему не сделать чтобы

let x=&5

let &x=5

Были эквивалентны, тогда ref вроде как будет не нужен

Хотя примерно понял, если подумал, тогда

let &x = &5

Непонятно нужно создавать ссылку или разыменовывать её

нужно для того чтобы возращать обьект типа наверно, в С++ так же можно протестить

  template<typename T>
  class Test{
  public:
  T a[3];
  Test(){}
  T& operator [](int idx) { return this->a[idx]; }
  const T* data1() const { return &a[0]; }
  T* data1() { return &a[0]; }
  };

  и например 
  Test test;
  test.data1();
  не тоже самое что test[0]

вообще изюминка в том что ссылка хранит только значение), а указатель и адрес и значение )

{
int a=1;

int *p = &a;

std::println("{} {}", p,*p);

}

таким образом граница контекста владения строго в { пока идет скобочка владеем через указатель к ресурсу } тут уже другая область, это как раз с чем типо-боролись, а это фишка ), это переплетается тесно с контекстным вызовом

тоесть просто в одном файле мы можем работать ток с указателями, если нужны и ресурсы и -O3 -ffast-math, и сложная контекстная логика, то нужен интерфейс и в том классе вся логика, тогда компилятор поймёт контекст и всё наладится такой тонкий нюанс так появился паттерн одиночка)

^ без этого в одном файле смешаются контексты и будет казаться что не правильная логика, но это на самом деле из-за контекста(владение ресурсом смешается с контекстом исполнения вызываемого класса например или тела цикла ) поэтому надо разграничить владение по контекстным вызовам, тоесть разграничения класс-интерфейс определяет контекст вот что я понял )

---

ну тоесть в С как раз доступен только указатель так что всё подпадает в логику )

---

например вызвать новый файл пронаследоваться от одиночки и в нём вызывать ресурсы, некие хендлеры ) тогда всё встаёт на свои места) и файл мейн тогда вызывает корректно контекст исполнения одиночки )

для меня это открытие )

"изюминка в том что ссылка хранит только значение"

Если мы говорим о C++ то это неверно, ссылка вообще не имеет никакой ячейки в памяти, поэтому ничего хранить не может, в данном случае вы помещаете в *p адрес a, & - операция взятия адреса, обратная разыменовыванию, не более того

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

Если мы говорим о C++ то это неверно, ссылка вообще не имеет никакой ячейки в памяти, поэтому ничего хранить не может,

А если взять и посмотреть в ассемблерный код

Давайте посмотрим, не вопрос, я уже смотрел, не выделяется никакая память под ссылки как минимум в варианте C++11, даже с move семантикой, просто берется адрес, в худшем варианте - косвенной адресацией и передается, физически ни в данных программы, ни в стеке, ни в куче ничего не видел чтобы выделялось

Если вы утверждаете обратное - приведите пример где C++ под ссылку выделяет память аналогично указателю, хоть один пример, почему я должен доказывать то, чего никогда не видел

Например, передача в функцию и возврат по ссылке.

Еще можно массив ссылок создать

ну вот борьба с указателями приведёт к тому в итоге что ими надо будет пользоваться и вы привели пример что в расте без них никак

а что ссылка хранит окей адрес в адресе значение.

а указатель и адрес показать может, и значение, и еще указатель может в арифметику указателей

Упустил я этот пример, если вам в Rust нужен именно сырой указатель, скорее всего вы делаете что-то не то, дайте пример, где он? Если умный указатель, то их там полно - Box, Rc, Arc, RefCell, Weak, на любой вкус и цвет, только это не указатель из Си, как и ссылка в Rust это не ссылка из С++

"указатель и адрес показать может, и значение, и еще указатель может в арифметику указателей"

И ещё в кучу багов, которые чтобы поймать можно потеть больше чем писал, например uaf, если вы откроете какой-нибудь крупный проект, например chromium вы обнаружите там за 1 месяц май 2025-го года тысячи уязвимостей use after free, некоторые из них написано "дают не авторизованному пользователю выполнить произвольный код на целевой платформе'

Зарегистрируйтесь на Хабре, чтобы оставить комментарий