Использование malloc для инициализации символьного указателя VS без использования malloc и просто присвоение строки непосредственно указателю char - PullRequest
0 голосов
/ 10 июля 2019

Я пытаюсь понять, как работает эта функция выделения памяти, когда дело касается символов и строк.

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

С другой стороны, мы используем malloc, когда хотим использовать кучу памяти, но я обнаружил, что вы можете просто инициализировать указатель на символ и назначить ему строку в той же самой строке объявления, поэтому у меня есть некоторые вопросы по этому вопросу:

1) Если я просто инициализирую указатель на символ и назначаю ему строку, где эта информация находится? В куче? В стеке? Пример: char *pointer = "Hello world";

2) Я пытался использовать malloc для инициализации моего указателя символа и использовать его позже, чтобы присвоить ему строку, но я не могу скомпилировать ее, я получаю ошибку, что не так с такой логикой? вот что я пытаюсь сделать:

char *pointer = malloc(sizeof(char) * 12); *pointer = "Hello world";

Не могли бы вы помочь мне понять больше об указателях и распределении памяти? Большое спасибо! :)

Ответы [ 4 ]

1 голос
/ 10 июля 2019

Я знаю, что имя объявленного массива похоже на указатель на первый элемент массива,…

Это не правильно.Массив ведет себя аналогично указателю во многих выражениях (потому что он автоматически преобразуется), но он не , как указатель на первый элемент.При использовании в качестве операнда sizeof или унарного & массив будет массивом;он не будет преобразован в указатель.Кроме того, массив не может быть назначен так же, как указатель;Вы не можете присвоить значение массиву.

… но этот массив будет находиться в стеке памяти.

Хранение массива зависит от его определения.Если объект определен вне какой-либо функции, он имеет статическую продолжительность хранения, то есть он существует для всего выполнения программы.Если он определен внутри функции без _Thread_local или static имеет автоматическую продолжительность хранения, то есть он существует до завершения выполнения соответствующего блока кода.Реализации C в подавляющем большинстве случаев используют стек для объектов с длительностью автоматического хранения (игнорируя тот факт, что оптимизация часто может сделать использование стека ненужным), но возможны альтернативы.

С другой стороны, мы используем mallocкогда мы хотим использовать кучу памяти, но я обнаружил, что вы можете просто инициализировать указатель на символ и назначить ему строку в той же самой строке объявления, поэтому у меня есть несколько вопросов по этому вопросу:

1) Если я просто инициализирую указатель на символ и назначаю ему строку, где эта информация находится?В кучу?В стеке?Пример: char * pointer = "Hello world";

Для строкового литерала "Hello world" реализация C создает массив символов со статической продолжительностью хранения, такой же, как если бы вы написали char MyString[12]; или int x; в области видимости файла.Предполагая, что этот массив не исключен или иным образом не изменен оптимизацией, он находится в некоторой общей области памяти, которую реализация C использует для данных, встроенных в программу (возможно, «.rodata» или аналогичный раздел только для чтения).

Затем для char *pointer реализация C создает указатель.Если это определение появляется вне функции, оно имеет статическую длительность хранения, и реализация C использует для этого некоторую общую область памяти.Если оно появляется внутри функции, оно имеет автоматическую продолжительность хранения, и реализация C, вероятно, использует для этого место в стеке.

Затем для = "Hello world" реализация C использует массив для инициализации pointer.Для этого он преобразует массив в указатель на свой первый элемент и использует этот указатель в качестве начального значения pointer.

2) Я попытался использовать malloc для инициализации моего charуказатель и использовать его позже, чтобы назначить ему строку, но я не могу скомпилировать ее, я получаю ошибку, что не так с этой логикой?вот что я пытаюсь сделать:

char *pointer = malloc(sizeof(char) * 12);

*pointer = "Hello world";

Вторая строка неверна, потому что *pointer играет другую рольв объявлении, чем в выражении.

В char *pointer = …;, *pointer представляет «картинку» того, что должно быть char.Вот как работают объявления в C. Он говорит, что *pointer - это char, и поэтому pointer - это указатель на char.Однако определяемая и инициализируемая вещь - это не *pointer, а pointer.

В отличие от *pointer = "Hello world";, *pointer является выражением.Он берет указатель pointer и применяет к нему оператор *.Поскольку pointer является указателем на char, *pointer является char.Затем *pointer = "Hello world"; пытается присвоить "Hello world" char.Это ошибка, поскольку "Hello world" не является char.

. Вы пытаетесь назначить указатель от "Hello world" до pointer.(Точнее, указатель на первый символ "Hello world".) Для этого используйте:

pointer = "Hello world";
1 голос
/ 10 июля 2019

1) Если я просто инициализирую указатель на символ и назначаю ему строку, где находится эта информация? В куче? В стеке? Пример: char * pointer = "Hello world";

Строковый литерал "Hello world" имеет статическую продолжительность хранения. Это означает, что он существует где-то в памяти, доступной для вашей программы, до тех пор, пока ваша программа работает.

Точное место, где он находится, зависит от реализации.

2) Я пытался использовать malloc для инициализации моего указателя на символ и позже использовать его для присвоения ему строки, но я не могу скомпилировать его, я получаю сообщение об ошибке, что не так с этой логикой? это то, что я пытаюсь сделать:

char *pointer = malloc(sizeof(char) * 12);
*pointer = "Hello world";

Это не работает, поскольку *pointer является первым char в динамически распределенной памяти (возвращается malloc()). Строковый литерал "Hello world" представляется в виде массива символов. Массив символов не может быть сохранен в одном char.

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

char *pointer = malloc(sizeof(char) * 12);
strcpy(pointer, "Hello world");            /* strcpy() is declared in standard header <string.h> *

Обратите внимание, что это не меняет данные, представляющие строковый литерал. Он копирует данные в этом строковом литерале в память, на которую указывает pointer (и динамически распределяется на malloc()).

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

const char *pointer = "Hello world";

const представляет тот факт, что изменение строкового литерала дает неопределенное поведение (и означает, что вышеизложенное не позволяет использовать pointer для изменения этого строкового литерала).

Если вы хотите написать действительно плохой код, вы также можете сделать

char *pointer = "Hello world";   /*  Danger Will Robinson !!!! */

или (тот же чистый эффект)

char *pointer;
pointer = "Hello world";     /*  Danger Will Robinson !!!! */

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

1 голос
/ 10 июля 2019

1) Если я просто инициализирую указатель на символ и назначаю ему строку, где эта информация находится?В кучу?В стеке?Пример: char *pointer = "Hello world";

Ни в стеке, ни в куче."Hello world" - это строковый литерал , обычно создаваемый в сегменте rodata (данные только для чтения) исполняемого файла.На самом деле, если вы не укажете иначе, компилятор может хранить только одну копию "Hello world", даже если вы назначите ее нескольким указателям.Хотя обычно указателям нельзя назначать строки, поскольку это строковый литерал 1012 *, вы фактически назначаете адрес самому литералу - единственная причина, которая работает.В противном случае, как отмечает P__J__, вы должны скопировать строки из одного местоположения в другое.

2) Я попытался использовать malloc, чтобы инициализировать мой указатель на символ и использовать его позже, чтобы назначить ему строку,но я не могу скомпилировать, я получаю ошибку, что не так с этой логикой? вот что я пытаюсь сделать:

char *pointer = malloc(sizeof(char) * 12);
*pointer = "Hello world";

Вы смешиваетеяблоки и апельсины здесь.char *pointer = malloc (12); выделяет хранилище для 12 символов (байтов), а затем присваивает начальный адрес для этого нового блока памяти pointer в качестве значения.(помните, указатель - это просто обычная переменная, которая содержит адрес чего-то другого в качестве значения)

Для каждого выделения должна быть проверка , чтовызов выполнен успешно, или вам нужно будет обработать ошибку.Распределение может и не получится, а при неудаче malloc, calloc * realloc все вернет NULL.Таким образом, каждый раз, когда вы выделяете, вы

char *pointer = malloc(12);       /* note: sizeof (char) is always 1 */
if (pointer == NULL) {            /* you VALIDATE each allocation */
    perror ("malloc-pointer");
    return 1;
}

Продолжая описанный выше случай, вы выделили 12 байтов и присвоили начальный адрес для нового блока памяти pointer.Затем вы необъяснимым образом разыменовываете pointer (например, *pointer, который теперь имеет тип char) и пытаетесь назначить адрес строкового литерала в качестве этого символа.

*pointer = "Hello world";    /* (invalid conversion between pointer and `char`) */

То, что вы хотите сделать, это скопировать "Hello world" в новый блок памяти, удерживаемый указателем.Для этого, поскольку вы уже знаете, что "Hello world" - это 12 символов (включая нуль-заканчивающийся символ), вы можете просто:

memcpy (pointer, "Hello world", 12);

( примечание: если у вас уже есть длина, нет необходимости вызывать strcpy и заставить его снова сканировать конец строки

Теперь ваш новый выделенный блок памяти содержит "Hello world"и память является изменяемой, поэтому вы можете изменять любые символы, которые вам нравятся.

Поскольку вы выделили хранилище, вы можете free (pointer);, когда эта память больше не используется.

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

Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.

1 голос
/ 10 июля 2019

Длинный вопрос, но ответ очень прост. Во-первых, вы должны понять, что это за указатель.

 *pointer = "Hello world";

Здесь вы пытаетесь назначить указатель на символ. Если вы удалите *, то вы назначите указатель на строковый литерал на pointer.

Если вы не перегружаете оператор присваивания, он не будет копировать его в выделенную память. Ваш malloc не имеет смысла, так как вы назначаете новое значение указателю, а память, выделенная malloc, теряется

Вам нужно вместо этого набрать

где эта информация живет?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...