Функция strdup выделяет память из кучи

Обновлено: 06.07.2024

В предыдущей главе уже обсуждалось, что локальные переменные кладутся на стек и существую до тех пор, пока мы не вышли из функции. С одной стороны, это позволяет автоматически очищать память, с другой стороны, существует необходимость в переменных, время жизни которых мы можем контролировать самостоятельно. Кроме того, нам необходимо динамическое выделение памяти, когда размер используемого пространства заранее не известен. Для этого используется выделение памяти на куче. Недостатков у такого подхода два: во-первых, память необходимо вручную очищать, во-вторых, выдеение памяти – достаточно дорогостоящая операция.

Для выделения памяти на куче в си используется функция malloc (memory allocation) из библиотеки stdlib.h

Функция выделяет size байтов памяти и возвращает указатель на неё. Если память выделить не удалось, то функция возвращает NULL. Так как malloc возвращает указатель типа void, то его необходимо явно приводить к нужному нам типу. Например, создадим указатель, после этого выделим память размером в 100 байт.

После того, как мы поработали с памятью, необходимо освободить память функцией free.
Используя указатель, можно работать с выделенной памятью как с массивом. Пример: пользователь вводит число – размер массива, создаём массив этого размера и заполняем его квадратами чисел по порядку. После этого выводим и удаляем массив.

Здесь (int *) – приведение типов. Пишем такой же тип, как и у указателя.
size * sizeof(int) – сколько байт выделить. sizeof(int) – размер одного элемента массива.
После этого работаем с указателем точно также, как и с массивом. В конце не забываем удалять выделенную память.

Теперь представим на рисунке, что у нас происходило. Пусть мы ввели число 5.

Функция malloc выделила память на куче по определённому адресу, после чего вернула его. Теперь указатель p хранит этот адрес и может им пользоваться для работы. В принципе, он может пользоваться и любым другим адресом.
Когда функция malloc "выделяет память", то она резервирует место на куче и возвращает адрес этого участка. У нас будет гарантия, что компьютер не отдаст нашу память кому-то ещё. Когда мы вызываем функцию free, то мы освобождаем память, то есть говорим компьютеру, что эта память может быть использована кем-то другим. Он может использовать нашу память, а может и нет, но теперь у нас уже нет гарантии, что эта память наша. При этом сама переменная не зануляется, она продолжает хранить адрес, которым ранее пользовалась.

Это очень похоже на съём номера в отеле. Мы получаем дубликат ключа от номера, живём в нём, а потом сдаём комнату обратно. Но дубликат ключа у нас остаётся. Всегда можно зайти в этот номер, но в нём уже кто-то может жить. Так что наша обязанность – удалить дубликат.

Иногда думают, что происходит "создание" или "удаление" памяти. На самом деле происходит только перераспределение ресурсов.

Освобождение памяти с помощью free

Т еперь рассмотри, как происходит освобождение памяти. Переменная указатель хранит адрес области памяти, начиная с которого она может им пользоваться. Однако, она не хранит размера этой области. Откуда тогда функция free знает, сколько памяти необходимо освободить?

  • 1. Можно создать карту, в которой будет храниться размер выделенного участка. Каждый раз при освобождении памяти компьютер будет обращаться к этим данным и получать нужную информацию.
  • 2. Второе решение более распространено. Информация о размере хранится на куче до самих данных. Таким образом, при выделении памяти резервируется места больше и туда записывается информация о выделенном участке. При освобождении памяти функция free "подсматривает", сколько памяти необходимо удалить.

Работа с двумерными и многомерными массивами

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

  • 1. Создавать массивы "неправильной формы", то есть массив строк, каждая из которых имеет свой размер.
  • 2. Работать по отдельности с каждой строкой массива: освобождать память или изменять размер строки.

Создадим "треугольный" массив и заполним его значениями

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

calloc

Ф ункция calloc выделяет n объектов размером m и заполняет их нулями. Обычно она используется для выделения памяти под массивы. Синтаксис

realloc

Е щё одна важная функция – realloc (re-allocation). Она позволяет изменить размер ранее выделенной памяти и получает в качестве аргументов старый указатель и новый размер памяти в байтах:

Функция realloc может как использовать ранее выделенный участок памяти, так и новый. При этом не важно, меньше или больше новый размер – менеджер памяти сам решает, где выделять память.
Пример – пользователь вводит слова. Для начала выделяем под слова массив размером 10. Если пользователь ввёл больше слов, то изменяем его размер, чтобы хватило места. Когда пользователь вводит слово end, прекращаем ввод и выводим на печать все слова.

Хочу обратить внимание, что мы при выделении памяти пишем sizeof(char*), потому что размер указателя на char не равен одному байту, как размер переменной типа char.

Ошибки при выделении памяти

1. Бывает ситуация, при которой память не может быть выделена. В этом случае функция malloc (и calloc) возвращает NULL. Поэтому, перед выделением памяти необходимо обнулить указатель, а после выделения проверить, не равен ли он NULL. Так же ведёт себя и realloc. Когда мы используем функцию free проверять на NULL нет необходимости, так как согласно документации free(NULL) не производит никаких действий. Применительно к последнему примеру:

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

2. Изменение указателя, который хранит адрес выделенной области памяти. Как уже упоминалось выше, в выделенной области хранятся данные об объекте - его размер. При удалении free получает эту информацию. Однако, если мы изменили указатель, то удаление приведёт к ошибке, например

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

3. Использование освобождённой области. Почему это работает в си, описано выше. Эта ошибка выливается в другую – так называемые висячие указатели (dangling pointers или wild pointers). Вы удаляете объект, но при этом забываете изменить значение указателя на NULL. В итоге, он хранит адрес области памяти, которой уже нельзя воспользоваться, при этом проверить, валидная эта область или нет, у нас нет возможности.

Эта программа отработает и выведет мусор, или не мусор, или не выведет. Поведение не определено.

Если же мы напишем

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

4. Освобождение освобождённой памяти. Пример

Здесь дважды вызывается free для переменной a. При этом, переменная a продолжает хранить адрес, который может далее быть передан кому-нибудь для использования. Решение здесь такое же как и раньше - обнулить указатель явно после удаления:

5. Одновременная работа с двумя указателями на одну область памяти. Пусть, например, у нас два указателя p1 и p2. Если под первый указатель была выделена память, то второй указатель может запросто скомпрометировать эту область:

Рассмотрим код ещё раз.

Теперь оба указателя хранят один адрес.

А вот здесь происходит непредвиденное. Мы решили выделить под p2 новый участок памяти. realloc гарантирует сохранение контента, но вот сам указатель p1 может перестать быть валидным. Есть разные ситуации. Во-первых, вызов malloc мог выделить много памяти, часть которой не используется. После вызова ничего не поменяется и p1 продолжит оставаться валидным. Если же потребовалось перемещение объекта, то p1 может указывать на невалидный адрес (именно это с большой вероятностью и произойдёт в нашем случае). Тогда p1 выведет мусор (или же произойдёт ошибка, если p1 полезет в недоступную память), в то время как p2 выведет старое содержимое p1. В этом случае поведение не определено.

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

Различные аргументы realloc и malloc.

При вызове функции malloc, realloc и calloc с нулевым размером поведение не определено. Это значит, что может быть возвращён как NULL, так и реальный адрес. Им можно пользоваться, но к нему нельзя применять операцию разадресации.
Вызов realloc(NULL, size_t) эквиваленте вызову malloc(size_t).
Однако, вызов realloc(NULL, 0) не эквивалентен вызову malloc(0) :) Понимайте это, как хотите.

Примеры

1. Простое скользящее среднее равно среднему арифметическому функции за период n. Пусть у нас имеется ряд измерений значения функции. Часто эти измерения из-за погрешности "плавают" или на них присутствуют высокочастотные колебания. Мы хотим сгладить ряд, для того, чтобы избавиться от этих помех, или для того, чтобы выявить общий тренд. Самый простой способ: взять n элементов ряда и получить их среднее арифметическое. n в данном случае - это период простого скользящего среднего. Так как мы берём n элементов для нахождения среднего, то в результирующем массиве будет на n чисел меньше.

Это простой пример. Большая его часть связана со считыванием данных, вычисление среднего всего в девяти строчках.

2. Сортировка двумерного массива. Самый простой способ сортировки - перевести двумерный массив MxN в одномерный размером M*N, после чего отсортировать одномерный массив, а затем заполнить двумерный массив отсортированными данными. Чтобы не тратить место под новый массив, мы поступим по-другому: если проходить по всем элементам массива k от 0 до M*N, то индексы текущего элемента можно найти следующим образом:
j = k / N;
i = k - j*M;
Заполним массив случайными числами и отсортируем

3. Бином Ньютона. Создадим треугольную матрицу и заполним биномиальными коэффициентами

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

email

Всё ещё не понятно? – пиши вопросы на ящик

будущие направления string.h можно найти в C11 7.31.13 String handling <string.h> :

имена функций, которые начинаются с str , mem или wcs и строчная буква может быть добавлена к объявлениям в .

(b) изменение в основном будет заменять if (d == NULL) return NULL; С:

может быть, код немного быстрее, чем с strcpy() как char не нужно искать снова (это уже было с strlen() ).

нет смысла повторять другие ответы, но обратите внимание, что strdup() можно делать все, что хочет, с точки зрения C, так как он не является частью стандарта C. Однако он определяется POSIX.1-2001.

С strdup man:

The strdup() функция возвращает указатель на новую строку, которая является дубликатом строки, на которую указывает s1 . Возвращенный указатель может быть передан в free() . Если новая строка не может быть создана, возвращается нулевой указатель.

Он делает дубликат строки, переданной с помощью запуска malloc и strcpy переданной строки. Функции malloc и объед буфер возвращается вызывающему объекту, следовательно, необходимо работать свободный на возвращаемое значение.

strdup () выполняет динамическое выделение памяти для массива символов, включая конечный символ '\0', и возвращает адрес памяти кучи:

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

strdup и strndup определяются в POSIX-совместимых системах как:

The strdup () функция выделяет достаточно памяти для копии строка str , делает копию и возвращает указатель на него.

указатель может впоследствии использоваться в качестве аргумента функции free .

если недостаточно памяти, NULL и errno установлено ENOMEM .

в strndup() функция копирует не более len символы из строки str всегда null завершает скопированную строку.

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

функция strdup () является сокращением для дубликата строки, она принимает параметр как строковую константу или строковый литерал и выделяет достаточно места для строки и записывает соответствующие символы в выделенное пространство и, наконец, возвращает адрес выделенного пространства вызывающей подпрограмме.

Это просто strcpy(ptr2, ptr1) эквивалентно while(*ptr2++ = *ptr1++)

где: strdup эквивалентно

ptr2 = malloc(strlen(ptr1)+1);

strcpy(ptr2,ptr1);

поэтому, если вы хотите, чтобы строка, которую вы скопировали, использовалась в другой функции (так как она создана в разделе кучи) , вы можете использовать strdup, иначе strcpy достаточно

Я пишу класс C++ для книги, которая содержит имя: class Book < private: char* nm; . . . . >; Мне не разрешается использовать std::string в этом задании. Итак, здесь я использую strdup для копирования значения имени параметра в nm в конструкторе.

Я новичок в C языке. Я пытаюсь сравнить два возвращаемых значения из strdup с функцией strncmp , но получаю следующую ошибку: ошибка: передача аргумента 2 ‘strncmp ' делает указатель из целого числа без приведения [- Wint-conversion] мой код: if (0 == strncmp( strdup(str_to_dup1).

Именно так это звучит, предполагая, что вы привыкли к сокращенному способу, которым C и UNIX присваивают слова, он дублирует строки :-)

Имея в виду, что на самом деле он не является частью самого стандарта ISO C (а) (это вещь POSIX), он фактически делает то же самое, что и следующий код:

Он пытается выделить достаточно памяти для хранения старой строки (плюс символ "\0", чтобы отметить конец строки).

Если распределение не удалось, он устанавливает errno в ENOMEM и немедленно возвращает NULL . Установка errno на ENOMEM -это то, что malloc делает в POSIX, поэтому нам не нужно явно делать это в нашем strdup . Если вы не соответствуете POSIX, то ISO C на самом деле не санкционирует существование ENOMEM , поэтому я не включил это здесь (b) .

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

Имейте в виду, что это концептуальное определение. Любой библиотечный писатель, достойный своей зарплаты, мог предоставить сильно оптимизированный код, ориентированный на конкретный используемый процессор.

(a) однако функции, начинающиеся с str и строчной буквы, зарезервированы стандартом для будущих указаний. От C11 7.1.3 Reserved identifiers :

Каждый заголовок объявляет или определяет все идентификаторы, перечисленные в связанном с ним подпункте, а *optionally объявляет или определяет идентификаторы, перечисленные в связанном с ним подпункте направления будущей библиотеки.**

Будущие направления для string.h можно найти в C11 7.31.13 String handling <string.h> :

Имена функций, начинающиеся с str , mem или wcs и строчной буквы, могут быть добавлены к объявлениям в заголовке <string.h> .

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

(b) это изменение в основном будет заключаться в замене if (d == NULL) return NULL; на:

В любом случае, если вы решите пойти по этому пути, вы сделаете что-то вроде:

Возможно, код немного быстрее, чем с strcpy() , так как символ \0 не нужно искать снова (это уже было с strlen() ).

Нет смысла повторять другие ответы, но, пожалуйста, обратите внимание, что strdup() может делать все, что захочет с точки зрения C, поскольку он не является частью какого-либо стандарта C. Однако он определяется POSIX.1-2001.

Я работаю над реализацией strdup() в C, я новичок в C и пытаюсь создать свою собственную библиотеку. Я использую фактический strdup() из string.h , чтобы проверить свой, и я сталкиваюсь с ошибкой шины, когда я тестирую свой источник против основной программы, но нет, когда я использую фактический.

Функция strdup() возвращает указатель на новую строку, которая является дубликатом строки, на которую указывает s1 . Возвращаемый указатель может быть передан в free() . Указатель null возвращается, если новая строка не может быть создана.

strdup() выполняет динамическое выделение памяти для массива символов, включая конечный символ '\0', и возвращает адрес памяти кучи:

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

Он создает дубликат переданной строки, запустив malloc и strcpy переданной строки. Буфер malloc'ed возвращается вызывающему объекту, следовательно, необходимо свободно работать с возвращаемым значением.

strdup и strndup определяются в системах, совместимых с POSIX, как:

Функция strdup() выделяет достаточно памяти для копирования строки str , выполняет копирование и возвращает указатель на нее.

Указатель впоследствии может быть использован в качестве аргумента функции free .

Если памяти недостаточно, то возвращается NULL , а для errno устанавливается значение ENOMEM .

Функция strndup() копирует не более len символов из строки str всегда null, завершающей скопированную строку.

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

эквивалентно (кроме того факта, что это изменяет указатели):

Итак, если вы хотите, чтобы скопированная вами строка использовалась в другой функции (как она создается в разделе кучи), вы можете использовать strdup , иначе достаточно strcpy ,

Функция strdup() является сокращением для дубликата строки, она принимает параметр в виде Строковой константы или строкового литерала и выделяет достаточно места для строки, записывает соответствующие символы в выделенное пространство и, наконец, возвращает адрес выделенного пространства вызывающей подпрограмме.

Похожие вопросы:

У меня есть какой-то код C++0x. Я смог воспроизвести его ниже. Приведенный ниже код прекрасно работает без -std=c++0x , однако он мне нужен для моего реального кода. Как включить strdup в C++0x? с.

Когда я использую strdup в Microsoft Visual C++, он предупреждает меня: предупреждение C4996: 'strdup': имя POSIX для этого элемента устарело. Вместо этого используйте имя, соответствующее ISO C++.

Я пишу класс C++ для книги, которая содержит имя: class Book < private: char* nm; . . . . >; Мне не разрешается использовать std::string в этом задании. Итак.

Я новичок в C языке. Я пытаюсь сравнить два возвращаемых значения из strdup с функцией strncmp , но получаю следующую ошибку: ошибка: передача аргумента 2 ‘strncmp ' делает указатель из целого числа.

Я работаю над реализацией strdup() в C, я новичок в C и пытаюсь создать свою собственную библиотеку. Я использую фактический strdup() из string.h , чтобы проверить свой, и я сталкиваюсь с ошибкой.

Большинство программистов C знакомы с функцией strdup . Многие из них примут это как должное, но это не является частью стандарта C (ни C89, ни C99, ни C11). Он является частью POSIX и может быть.

На этом шаге мы приведем примеры использования строковых функций.

Определение длины строк.

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

следующий оператор установит переменную len равной длине строки, адресуемой указателем с :

Затем используйте функцию strlen() для установки целой переменной len , равной числу символов в литеральной строке, скопированной в буфер:

Копирование строк.

Оператор присваивания для строк не определен. Если с1 и с2 - символьные массивы, вы не сможете скопировать один в другой следующим образом:

Но если с1 и с2 объявить как указатели типа char * , компилятор согласится с этим оператором, но вряд ли вы получите ожидаемый результат. Вместо копирования символов из одной строки в другую оператор с1 = с2 скопирует указатель с2 в указатель с1 , перезаписав, таким образом, адрес в с1 , потенциально потеряв информацию, адресуемую указателем.

Чтобы скопировать одну строку в другую, вместо использования оператора присваивания вызовите функцию копирования строк strcpy() . Для двух указателей с1 и с2 типа char * оператор

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

Аналогичная функция strncpy() ограничивает количество копируемых символов. Если источник ( source ) и приемник ( destination ) являются указателями типа char * или символьными массивами, то оператор

скопирует до 10 символов из строки, адресуемой указателем source , в область памяти, адресуемую указателем destination . Если строка source имеет больше 10 символов, то результат усекается. Если же меньше - неиспользуемые байты результата устанавливаются равными нулю.

Замечание. Строковые функции, в имени которых содержится дополнительная буква n , объявляют числовой параметр, ограничивающий некоторым образом действие функции. Эти функции безопаснее, но медленнее, чем их аналоги, не содержащие букву n . Программные примеры содержат следующие пары функций: strcpy() и strncpy() , strcat() и strncat() , strcmp() и strncmp() .

Дублирование строк.

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

Текст примера 2 образет один маленький модуль с единственной функцией GetStringAt() , которая удовлетворяет этим требованиям, используя другую строковую функцию strdup() для того, чтобы сделать копию выводимой строки. Другие примеры этого раздела используют модуль c64_2.cpp .

Файл c64_2.h - заголовочный: он содержит только объявления для включения в другие модули. Файл c64_2.cpp - это отдельный модуль, содержащий функцию (их может быть несколько), используемую в программе. Ни один из этих файлов не является законченной программой, поэтому не пытайтесь компилировать и запускать их. Ниже мы вспомним, как это можно сделать (см. пример 4 шага 53).

Пример 2а. Текст заголовочного файла c64_2.h .

Пример 2б. Текст модуля c64_2.cpp .

Указанный модуль включает свой собственный заголовочный файл, который определяет константу MAXLEN и объявляет прототип функции GetStringAt() . Внутри этой функции расположено объявление статической строки buffer . Использование ключевого слова static создает переменную, которая постоянно хранится в сегменте данных, но доступна только внутри функции GetStringAt() .

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

Оператор if проверяет параметр size . Если его значение находится вне заданного диапазона, то ему присваивается значение MAXLEN .

После позиционирования курсора с помощью вызова функции gotoxy() в цикле while вызывается функция getchar() , которая ожидает, пока вы введете символ. Программа присваивает этот символ переменной c и проверяет на совпадение с EOF ( end of file - конец файла, означающий, что источник ввода закрыт) или с управляющим символом \n ("новая строка", означающая, что вы нажали клавишу Enter ). Если одно из этих условий оказалось выполненным, программа устанавливает параметр size равным нулю, завершая тем самым цикл while . В противном случае программа присваивает значение переменной c элементу массива buffer с индексом i , инкрементированным оператором ++ для подготовки к вводу следующего символа.

Замечание. Выполните цикл while по шагам, используя клавишу F7 , наблюдая при этом за значениями переменных buffer , c и i .

Затем добавляется нулевой символ (литеральное выражение '\0') за последним символом, запомненным в строке buffer . После этого вызывается функция fflush() , с аргументом stdin , который является встроенным символом, представляющим стандартный файл ввода. Вызов функции fflush() очищает "повисший" символ новой строки, который может привести к тому, что функция getchar() (и другие функции ввода) не будет делать паузу для ожидания ввода.

И, наконец, в строке return strdup(buffer); возвращается результат функции GetStringAt() . Этот оператор передает строку buffer функции strdup() , которая создает копию символов и возвращает адрес дубликата строки.

Функция strdup() (ее название говорит само за себя) возвращает адрес дубликата строки, адресуемой ее аргументом. Строка создается с помощью вызова функции malloc() и занимает столько памяти, сколько необходимо. Если переменная c имеет тип char * , то оператор

выделит ровно 16 байт памяти кучи, скопирует в эту область памяти 15-символьную строку "Двойная тревога" плюс завершающий нуль и возвратит адрес этой области. По окончании работы с этой строкой следует освободить эту область памяти обычным способом:

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

Пример 3. Иллюстрация использования функции GetStringAt() .

Пример 3 демонстрирует применение функции GetStringAt() , объявленной в файле c64_2.h и описанной в файле c64_2.cpp . Чтобы создать законченную программу, вы должны скомпилировать c64_3.cpp и скомпоновать с модулем c64_2 . Произведем эти операции из командной строки, получив в результате DOS-приложение.

Первая команда компилирует модуль c64_2.cpp , создавая объектный файл с именем c64_2.obj который содержит скомпилированный код для функции GetStringAt() . Вторая команда компилирует модуль c64_3.cpp , создавая объектный файл с именем c64_3.obj , а также компонует эти два объектных файла и создает окончательный исполняемый файл c64_3.exe , который можное запустить в DOS .

осуществляется вызов функции GetStringAt() , с передачей ей трех аргументов: координаты х, координаты у и максимальной длины результата. Функция располагает курсор в заданном месте (полезная вещь при разработке экранов ввода данных) и ограничивает ввод требуемым числом символов.

программа проверяет результат функции GetStringAt() . Если она возвращает нуль, это значит, что функция strdup() не смогла создать копию ввода, возможно, из-за недостатка памяти в куче. Обратите внимание также на освобождение памяти после того, как она оказывается больше не нужной (конструкция free(s) ).

Сравнение строк.

Используя функцию GetStringAt() из предыдущего раздела, можно написать программу, которая предлагает ввести пароль. Чтобы определить правильность введенного пароля, воспользуемся функцией strcmp() , которая сравнивает две строки.

Пример 4. Иллюстрация использования функции strcmp() .

показано, как сравнивать две строки, в данном случае для того, чтобы определить правильность введенного пароля. Здесь переменная done получает значение "истина" (что соответствует любому ненулевому значению), если строка, адресуемая указателем s , и PASSWORD (макроопределение, которое расширяется в литеральную строку) равны. Если i - переменная типа int и если a и b - указатели на char или символьные массивы, то оператор

установит i равной -1 или другому отрицательному числу, если строка, адресуемая указателем a , в алфавитном порядке меньше строки, адресуемой указателем b . Если строки в точности совпадают, функция возвратит нуль. Она вернет +1 или другое положительное число, если строка a в алфавитном порядке больше строки b .

Функция strcmp() чувствительна к регистру букв - она считает строчные буквы больше их прописных эквивалентов (так как буквы нижнего регистра имеют большие значения кода ASCII, чем буквы верхнего регистра). Для сравнения двух строк без учета регистра вызовите функцию stricmp() . Буква i символизирует приказ ignore case ( игнорировать регистр ) . Эта функция действует аналогично функции strcmp() , но перед сравнением преобразует все буквы в прописные. Строка Apple алфавитно окажется меньше строки apple , если сравнение выполнялось с помощью функции strcmp() . Если же для сравнения использовать функцию stricmp() , то эти строки будут считаться идентичными.

Чтобы сравнить только часть двух строк, используйте функцию strncmp() . Например, оператор

установит целую переменную i равной нулю только в том случае, если первые два символа строк, адресуемых указателями s1 и s2 , в точности совпадают. Для безрегистрового сравнения вызывайте функцию strnicmp() .

Конкатенация строк.

Конкатенация двух строк означает их сцепление , при этом создается новая, более длинная строка. При объявлении строки

превратит значение первоначальной строки original в "Проверка один, два, три!"

При вызове функции strcat() убедитесь, что первый аргумент типа char * инициализирован и имеет достаточно места, чтобы запомнить результат. Если c1 адресует строку, которая уже заполнена, а c2 адресует ненулевую строку, оператор strcat(c1, c2); перезапишет конец строки, вызвав серьезную ошибку.

Функция strcat() возвращает адрес результирующей строки (совпадающий с ее первым параметром) и может использоваться как каскад нескольких вызовов функций:

Этот оператор добавит строку, адресуемую c2 , и строку, адресуемую c3 , к строке, адресуемой c1 , что эквивалентно двум отдельным операторам:

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

Пример 5. Иллюстрация использования функции strcat() .

Приведенная программа демонстрируют важный принцип конкатенации строк: всегда инициализируйте первый строковый аргумент . В данном случае символьный массив rez инициализируется вызовом функции strcpy() , которая вставляет fam в rez . После этого программа добавляет пробелы и две другие строки - im и otch . Никогда не вызывайте функцию strcat() с неинициализированным первым аргументом.

Если вы не уверены в том, что в строке достаточно места для присоединяемых подстрок, вызовите функцию strncat() , которая аналогична функции strcat() , но требует числового аргумента, определяющего число копируемых символов. Для строк s1 и s2 , которые могут быть либо указателями типа char * , либо символьными массивами, оператор

присоединяет и максимум четыре символа из s2 в конец строки s1 . Результат обязательно завершается нулевым символом.

Существует один способ использования функции strncat() , гарантирующий безопасную конкатенацию. Он состоит в передаче функции strncat() размера свободной памяти строки-приемника в качестве третьего аргумента. Рассмотрим следующие объявления:

Вы можете присоединить s2 к s1 , формируя строку "Кот в шляпе" , с помощью функции strcat() :

Если вы не уверены, что в s1 достаточно места, чтобы запомнить результат, используйте альтернативный оператор:

Этот способ гарантирует, что s1 не переполнится, даже если s2 нужно будет урезать до подходящего размера. Этот оператор прекрасно работает, если s1 - нулевая строка.

Читайте также: