I puntatori C ++. parte 2





указатели с++, C ++ puntatori

il первой части мы рассмотрели, как используя указатель и оператор nuovo можно выделить участок памяти необходимого размера непосредственно в процессе работы программы. Так же узнали, как этот участок памяти можно освободить, utilizzando cancellare. Увидели как параметры передаются в функцию по указателю. И то, что это даёт возможность внести изменения в значение переменных, которые передаются в функцию.

Сейчас мы рассмотрим пример где будем передавать в функцию указатели на строки (указатели типа carbonizzare). При этом сама функция возвращает указатель. Задача следующая: Есть два указателя на строки, под которые выделены необходимые им участки памяти. Необходимо объединить эти две строки. То есть надо выделить новый участок памяти для первой строки, чтобы стало возможным дописать в неё вторую строку.

prima, как набирать код, чтобы вас незасыпалоошибкамиустановите свежую версию среды Microsoft Visual Studio. Например Microsoft Visual Studio 2013 Esprimere. Если вы используете более раннюю версию, то вместо функций strcat_s() применяйте strcat() e strcpy() invece di strcpy_s().

Идем по порядку. on line 4 находится прототип функции. Её задачавыделить новый участок памяти для строки, в конец которой запишется другая строка. Об этом мы поговорим ниже, когда дойдем до определения этой функции. Переходим к строкам 11 – 12. В них определены переменные, которые будут хранить значения длины строк "fila 1 " e "+ fila 2". Длина подсчитывается с помощью встроенной функции strlen(). Она вернет значение длины строки без учета символа \0 . Поэтому при инициализации переменных strSize1 e strSize2 мы прибавляем к тому что вернет strlen(…) единицу.

on line 14 определён указатель pStr1 на первую строку. Чтобы записать строку, сначала необходимо выделить под нее память: carbonizzare* pStr1 = nuovo carbonizzare[strSize1]; Ниже копируем строку в выделенную память: strcpy_s(pStr1, strSize1, "fila 1 "); . Первый параметр, который принимает функция strcpy_s()указатель на адрес куда надо скопировать строку; secondo – размер строки; третийсама строка. Второй указатель pStr2 определяем точно так же.

В стр. 20 – 21, просим показать на экран записанные строки. Для этого достаточно обратиться к ним по имени указателей. Указатель хранит адрес нулевой ячейки. Когда мы просим показать его на экранбудут поочередно отображаться символы массива (участка памяти, который был выделен ранее), пока не встретится символ конца строки\0 .

Стр. 23-24 – смотрим сколько байт памяти занимает каждая строка. fila 26 исключена комментарием. В ней осуществлена попытка дописать вторую строку в первую. Попробуйте удалить // и откомпилировать программу. Выполнение программы прервется ошибкой. Вы увидите на экране следующее:

указатели на строки с++, указатели на строки c++, nuovo, cancellarenaturalmente – ведь выделенный участок памяти под первую строку слишком мал, для того чтобы дописать в него еще какие-либо данные. on line 28 записываем в переменную requiredSize реально необходимый размер памяти для записи двух строк: int requiredSize = (strSize1 + strSize2) - 1; Единицу отнимаем потому, что в переменных strSize1 e strSize2 уже включены два \0 , а нам необходим только один.

Переместимся к определению функции giveNewMem() – pp. 42 – 51. Она примет первую строку (указатель на неё) и целое число (достаточный размер памяти). vedere – нам надо в этой функции выделить новый участок памяти для записи в него символов. Значит функция должна вернуть указатель на этот новый участок памяти. Так мы сможем записать в указатель pStr1 новый адрес, по которому расположена память для записи двух строк. Поэтому в заголовке функции пишем так carbonizzare* giveNewMem(carbonizzare *pstr1, int reqSize)

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

Создаем новый указатель и сразу выделяем под него память, достаточную для размещения символов обеих строк (pp. 44). Далее копируем в выделенную память символы первой строки: strcpy_s(strInFunc, reqSize, pstr1); Строка скопировананужно освободить память, которую она занимала, чтобы не произошло утечки памяти (pp. 48). Возвращаем из функции указатель на новый участок памяти: ritorno strInFunc;

Si scopre, когда мы вызываем эту функцию из principale() (pp. 31) pStr1 = giveNewMem(pStr1, requiredSize);в указатель pStr1 запишется адрес нового участка памяти, который способен вместить две строки. Осталось только дописать в эту память вторую строку (pp. 33) и показать её на экран. Перед выходом из программы освобождаем память. Сначала ту что была выделена в функции, а потом ту где располагается вторая строка.

Откомпилируем программу:

указатели на строки с++, указатели на строки c++, nuovo, cancellare

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

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

Зачем нужны указатели в C++?

  • с помощью указателей, возможно выделение динамической памяти. Так память под данные выделяется в процессе работы программы, а не на этапе компиляции. Это очень выгодно, когда мы не знаем до начала работы программы сколько реально будет использовано данных (variabili). Чтобы выделять память и освобождать ее, используются операторы nuovo e cancellare.
  • указатели часто используются для доступа к объемным участкам данных из функций. К данным символьных и числовых массивов например, которые определены вне функции. Такой подход пришел из программирования на языке C. В C++ относительно числовых переменных и массивов удобнее использовать передачу по ссылке. Вскоре мы с вами рассмотрим эту тему. Относительно строк в стиле Си лучше применять передачу по указателю.
  • используя указателимы работаем с памятью по адресам напрямую. Это быстрее, чем обращение по имени переменных.

Объявление указателей, взятие адреса, разыменование

Рассмотрим подробно на примере:

on line 8 определена обычная целочисленная переменная. on line 9 закомментировано объявление и инициализация указателя. Попробуйте удалить знаки комментирования // и откомпилировать. Нам сообщат об ошибке. Все правильно. Ведь указатель должен хранить адрес (шестнадцатеричное число), а не значение (десятеричное число или символ). Для того, чтобы получить адрес переменной используется операция взятия адреса & (E commerciale: Spostamento + 7). on line 10 создаем указатель и записываем в него адрес переменной:

инициализация указателя

Чтобы определить указатель, надо объявить его тип, за типом поставить звездочку * e dargli un nome. Инициализировать указатель можно только адресом. Если вы не присваиваете адрес указателю при его объявлении, обязательно инициализируйте его нулем. Тогда такой указатель не сможет привести к сложным ошибкам, которые тяжело найти. Он просто ни на что не будет указывать.

Обычно нам не особо интересно, какой адрес хранит указатель. Нас интересуют данные, расположенные по этому адресу. Чтобы посмотреть эти данные (или внести в них изменения) к имени указателя надо применить операцию разыменования. Это такая же звездочка *, как и при объявлении.

разыменование указателя

Только в потоке cout она трактуется иначене так как при объявлении указателя. Она помогает обратиться к данным, хранящимся по адресу. Это продемонстрировано в строке 15 fonte.

on line 18 определен целочисленный schieramento на пять элементов. Ниже определен указатель. Si prega di notare – чтобы инициализировать указатель адресом массива, operazione & не применяется. Questo perché, что имя массива ссылается на адрес нулевой ячейки. Получается что запись

инициализация указателя

вполне нормально компилируется. В переменную-указатель pFirstArr будет записан адрес нулевой ячейки массива firstArr. Эта запись аналогична следующей

инициализация указателя

on line 25 показан пример обращения к данным элементов массива через указатель. Используется нотация массивов. Cioè, non abbiamo bisogno di applicare l'operazione dereference, чтобы обратиться к данным массива:

cout << "pfirstArr[0] = " << pFirstArr[0] << endl << endl;

lattina, конечно обращаться к данным массива, используя нотацию указателей, но это неудобно. guardare, как бы выглядело присвоение значений элементам массива и показ значений на экран:

linee 27 – 28 fonte – определение Si-stroki и определение указателя на эту строку. Указатели замечательно справляются с работой со строками. Когда мы в потоке cout обращаемся по имени к указателю на символьный массив, он нам покажет всю строку. Так же как и в случае с массивами, компилятор будет выводить символы на экран, пока не обнаружит в массиве символ конца строки \0

Посмотрите на итог работы программы и на исходный код еще раз. Постарайтесь понять как он работает.

инициализация указателя, разыменование указателя, & взятие адреса

Еще одно отступление от темы, чтобы подбодрить тех, кому тяжело дается тема указателей :) Вы не одни. Всё приходит с практикой! И к вам дойдет! Не паникуйте, если это выглядит слишком запутанным для вас. Решайте как можно больше задач по программированию. Даже если вы что-то будете делать не так в процессе написания кода, наши замечательные среды разработки дадут об этом знать.

Обязательно посмотрите видео об указателях (с 12-й минуты), если вы не смотрели его в первой части статьи:

Указатели, как параметры (argomenti) funzione:




Iscriviti ai nuovi articoli sul nostro sito preavviso:


Accetto di ricevere messaggi da purecodecpp.com sulla mia e-mail

data
pagina
Указатели на строки в C++
valutazione
5

7 pensieri su "I puntatori C ++. parte 2

  1. Не могу понять , in 15 строке кода – strcpy_s(pStr1, strSize1, “fila 1 “);
    в функции strcpy_s используется 3 аргумента, ранее при описании этой функции в ней указывалось только 2 аргумента (куда скопировать и что скопировать).
    Собственно вопрос : почему теперь в ней 3 аргумента? Потому что мы сейчас используем указатель как первый аргумент? Или потому что это новая версия этой функции (strcpy_s вместо strcpy)? Или этой особенность visual studio? inutilmente. в code:: blocks функция strcpy принимает 2 аргумента <>.

    1. В примере кода использована функция strcpy_s(), а не strcpy(), которая действительно имеет 2 аргумента. Смотрите имена внимательнее.

      P.S. Это вовсе не означает, что так следует делать: strcpy_s() – funzione, не входящая в стандарты ни POSIX, ни C, ни Linux и т.д. и т.п. … и используется только в операционных системах Windows (вы не найдёте даже её описания в литературе). Но конкретный этот примеркорректен.

      1. Понял только то что описания strcpy_s я не найду, а почему этой функции нужно знать размер строки не понятно.
        Размер строки ей надо знать потому что первым аргументом указатель? Или это особенность обновленной функции? И сколько аргументов эта функция вообще принимает (может принимать) ?

      2. У меня на code::blocks тоже не берет.Может это из-за того,что среду разработки нужно другую?более новую?Здесь написано про нее, пишут что более безопасная функция,так как предупреждает переполнение буфера! https://msdn.microsoft.com/ru-ru/library/8ef0s5kh.aspx

    2. Указывать размер копируемой строки очень полезно: если строка источник, по случайности, длиннее приёмника, то вы получите ошибку доступа к памяти с крахом всего приложения.

      Посмотрите описание библиотечной и стандартной функции strncpy(), которая безопаснее и чаще применяется в профессиональном коде чем strcpy().

  2. Среда разработки DEV CPP 5.11.
    Первый пример работает и без функции перевыделения памяти :)

Lascia un Commento

Inserire il codice nei tag: <pre class="lang:C ++ decodifica:true ">IL TUO CODICE</pre>