Имеет ли параметр функции, который принимает строковую ссылку непосредственно на строковую переменную или данные в куче в Rust - PullRequest
1 голос
/ 09 июля 2020

Я взял этот снимок и код из The Rust Book .

Почему s указывает на s1, а не только на данные в самой куче?

Если да, то как это работает? Как s указывает на s1. Это выделенная память с полем ptr, которое содержит адрес памяти s1. Затем s1, в свою очередь, указывает на данные.

В s1 мне кажется, что я смотрю на переменную с указателем, длиной и емкостью. Фактическим указателем здесь является только поле ptr?

Это мой первый язык системного уровня, поэтому я не думаю, что сравнение с C / C ++ поможет мне в этом разобраться. Я думаю, что отчасти проблема в том, что я не совсем понимаю, что такое указатели и как ОС выделяет / освобождает память.

Указатель

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

1 Ответ

2 голосов
/ 09 июля 2020
  • память - это просто огромный массив, который можно индексировать любым смещением (например, u64).
  • Это смещение называется адрес ,
  • и переменная, в которой хранится адрес, называемый указателем .
  • Однако обычно выделяется только небольшая часть памяти, поэтому не каждый адрес имеет смысл (или действителен).
  • Распределение - это запрос на создание (последовательного) диапазона адресов, значимых для программы (чтобы она могла получить доступ / изменить).
  • Каждый объект (и под объектом я подразумеваю любой тип) расположен в выделенной памяти (поскольку невыделенная память не имеет смысла для программы).
  • Ссылка на самом деле является указателем который гарантирован (компилятором) как действительный (т.е. полученный из адреса некоторого объекта, известного компилятору). Взгляните также на std do c.

Вот пример этих концепций ( игровая площадка ):

// This is, in real program, implicitly defined,
// but for the sake of example made explicit.
// If you want to play around with the example,
// don't forget to replace `usize::max_value()`
// with a smaller value.
let memory = [uninitialized::<u8>(); usize::max_value()];

// Every value of `usize` type is valid address.
const SOME_ADDR: usize = 1234usize;

// Any address can be safely binded to a pointer,
// which *may* point to both valid and invalid memory.
let ptr: *const u8 = transmute(SOME_ADDR);

// You find an offset in our memory knowing an address
let other_ptr: *const u8 = memory.as_ptr().add(SOME_ADDR);

// Oversimplified allocation, in real-life OS gives a block of memory.
unsafe { *other_ptr = 15; }

// Now it's *meaningful* (i.e. there's no undefined behavior) to make a reference.
let refr: &u8 = unsafe { &*other_ptr };

Я надеюсь, что это проясняет большинство вещей, но давайте рассмотрим вопросы подробно.

Почему s указывает на s1, а не только на данные в самой куче?

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

Как s указывает на s1. Выделена ли память с полем ptr, которое содержит адрес памяти s1.

Цепочка «указателей» все еще сохраняется, поэтому вызов s.len() внутренне преобразован в s.deref().len, и доступ к некоторому байту строкового массива, преобразованного в s.deref().ptr.add(index).deref().

На картинке отображаются 3 блока памяти: &s, &s1, s1.ptr разные (если не оптимизированы) адреса памяти. И все они хранятся в выделенной памяти. Первые два фактически хранятся в предварительно выделенной (т.е. перед вызовом функции main) памяти, называемой стек , и обычно она не называется выделенной памятью (практика, которую я проигнорировал в этом ответьте хоть). Указатель s1.ptr, напротив, указывает на память, которая была выделена явно пользовательской программой (т.е. после ввода main).

В s1, похоже, я смотрю на переменная с указателем, длиной и емкостью. Фактическим указателем здесь является только поле ptr?

Да, именно так. Длина и емкость - это обычные целые числа без знака.

...