C и C ++ недопонимание памяти

0

Пожалуйста, я хочу знать реальность того, как переменные, массивы и строки хранятся в памяти на языке C. Пожалуйста, исправьте мое понимание памяти в приведенном ниже примере:

В C, когда мы объявляем строку, мы выполняем следующие действия:

char string[]="string";

Что происходит в памяти? Каждый символ хранится в памяти и каждый случай памяти имеет свой адрес?

Например, адрес

1600 char[0]='S'
1602 char[2]='t'

и так далее. Это правда? Если нет, пожалуйста, дайте мне правильную схему того, что на самом деле происходит.

Мой второй вопрос касается C++: В C++ они изобрели новый тип данных, который является string. Например:

string variable("This is a string");

Как этот текст ("Это строка") хранится в памяти?

  • 0
    Символы хранятся в байтах один за другим в непрерывном блоке памяти с добавленным нулевым байтом. char [0] = 's' char [1] = 't' ... char [6] = 0. Строковый тип c ++ оборачивает интерпретацию C во все известные мне реализации компилятора. Это означает, что вы можете получить стандартный строковый массив символов C из строкового объекта.
  • 0
    AFAIR, в C ++ выражение "string" по-прежнему представляет собой массив из 7 элементов. Класс String , OTOH, обладает гораздо большей функциональностью.
Теги:

4 ответа

3

Посмотрите строки в глубину:

[...] Точная реализация макета памяти для класса строк не определена стандартом C++. Эта архитектура призвана быть достаточно гибкой, чтобы допускать различные реализации поставщиков компилятора, но гарантировать предсказуемое поведение для пользователей. [...]

[...] В C++ отдельные строковые объекты могут занимать или не занимать уникальные физические области памяти, но если подсчет ссылок используется для предотвращения дублирования копий данных, отдельные объекты должны выглядеть и действовать так, как если бы они выполнялись исключительно собственные уникальные регионы хранения. [...]

Вы можете узнать, как string реализована вашим компилятором следующим образом. Для меня (тест под VS2010), это будет

string variable("This is a string");
printf("%p\n", &variable[0]);        // 006751C0
printf("%p\n", &variable[1]);        // 006751C1
printf("%p\n", &variable[2]);        // 006751C2
printf("%p\n", &variable[3]);        // 006751C3
printf("%p\n", &variable[4]);        // 006751C4
printf("%p\n", &variable[5]);        // 006751C5
... ...
2

Строка хранится как массив с нулевым завершающим символом (каждый символ в ASCII-коде имеет ширину 8 бит и таким образом он занимает ровно один байт), так что им нужен еще один байт, чтобы сохранить символ "\ 0" ("привет") требуется массив из трех байтов). Если вы используете std :: string в c++, память, используемая для хранения данных, такая же, но вокруг нее есть оболочка, которая позволяет автоматически управлять памятью, так что когда вы делаете:

string s("hello");
string t("world");
s+=t;

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

s.c_str();

вы получаете указатель на нуль-завершенный массив символов, содержащийся в экземпляре s std :: string, этот указатель является лишь временным указателем, так что это

char* text=s.c_str();
s="very long string that probably results in a complete reallocation of the array";
printf("%s",text);

вероятно, приведет к неопределенному поведению (ошибка сегментации)

  • 0
    В C ++ есть два основные типа строк: 1. std::basic_string и 2. Null-terminated strings .
  • 0
    Я не знал, какой тип используется, когда вы просто объявляете std :: string?
Показать ещё 1 комментарий
0

Предисловие:

Каждый раз, когда вы создаете (компилируете и связываете) свой проект, создается исполняемый образ, разделенный на три раздела:

  • Code-раздел

  • Данных Раздел

  • стек

Теперь будет относиться к четырем различным сценариям:

Сценарий №1 - локальный массив (объявленный внутри функции):

int func()
{

    char array[]="string";
    ...
}

После загрузки и запуска программы каждый раз, когда вызывается функция, 7 байтов, расположенных где-то в стеке, инициализируются следующими значениями: 's', 't', 'r', 'i', 'n', ' g ', 0. Адрес в стеке, где расположены эти байты, является значением регистра SP при вызове функции. Следовательно, каждый раз, когда вызывается функция, эти 7 байтов могут находиться в другом адресе в памяти.

Сценарий №2 - глобальный массив (объявленный вне функции):

char array[]="string";

Во время компиляции (перед загрузкой и запуском программы) 7 байтов в разделе данных задаются со следующими значениями: 's', 't', 'r', 'i', 'n', 'g', 0. Эти байты "жестко закодированы" в исполняемом изображении, и как только они загружаются в память (т.е. Всякий раз, когда вы запускаете программу), они находятся в одном и том же адресе во время выполнения программы.

Сценарий № 3 - указатель на локальный массив (объявленный внутри функции):

int func()
{
    char* array="string";
    ...
}

То же, что и сценарий № 2, за исключением того факта, что эти 7 байтов находятся в разделе кода (который является разделом только для чтения), а не в разделе данных (который является секцией чтения/записи). Кроме того, указатель (array) выделяется в стеке и инициализируется (устанавливается так, чтобы указывать на адрес "string" в разделе кода) каждый раз, когда вызывается функция. Размер этого указателя обычно составляет 4 байта при использовании 32-разрядной ОЗУ или 8 байтов при использовании 64-разрядной ОЗУ.

Сценарий №4 - указатель на глобальный массив (объявленный вне функции):

char* array="string";

То же, что и сценарий № 3, за исключением того факта, что указатель (array) находится в секции данных вместо стека и инициализируется один раз (во время компиляции), а не каждый раз, когда вызывается функция (во время выполнения).

  • 0
    «Каждый раз, когда вы строите (компилируете и связываете) свой проект, создается исполняемый образ» - по крайней мере, в некоторых реализациях, но это не всегда так, и это не способствует сути ответа. Я предлагаю вам удалить это (и связанные с ним части) и перефразировать ваш ответ так, чтобы лучше описать абстрактное поведение.
0

они сохраняются в виде байтов: char: 1 byte short: 2 bytes int: 4 bytes long: 4 bytes float: 4 bytes double: 8 bytes

  • 2
    Откуда ты знаешь, что short - это два байта? что int это 4? что long всегда 4? Потому что это не так.
  • 2
    Размеры основных типов определяются реализацией. В Windows (с использованием Visual Studio) long составляет 32 бита на 32- и 64-разрядных платформах, в то время как при использовании GCC long составляет 32 бита на 32-разрядных платформах и 64-разрядных на 64-разрядных платформах. Кроме того, хотя sizeof(char) определен так, чтобы возвращать 1 , char может фактически быть чем-то иным, чем байтом (8 бит).
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню