Как правильно склеить две строки в Си?
Проблема в том, что функция strcat не выделяет память, а копирует из одной строки в другую. Поскольку длина первой строки меньше, чем длина второй, то память перетирается. Нужно изменить код, например, так.
Чтобы склеить 2 строки в Си, нужно выполнить следующие действия:
При помощи функции malloc выделить блок памяти ( result ), достаточный для сохранения в него обеих строк (и не забыть про место, для завершающего нуля)
При помощи функции memcpy скопировать первую строку s1 в начало выделенного блока
При помощи всё той же функции memcpy скопировать вторую строку s2 , вместе с её нулём, в выделенный блок, со смещением на размер первой строки
После того, как вы совершили какие-то манипуляции с полученной строкой, её нужно освободить, при помощи функции free
Для strcat первый аргумент должен указывать на буфер достаточной длины, чтобы в него могла поместиться итоговая (склеенная) строка. Переменные first и second являются указателями на строковые литералы, т.е. данные по таким указателям изменять нельзя. Это приводит к неопределенному поведению (UB).
Исходный пример может быть переписан следующим образом:
Самый простой вариант — явно зарезервировать под первую строку (к которой будет «приклеиваться» вторая строка) памяти с запасом:
Если содержимое строк известно на этапе компиляции, то и склеивание можно организовать на этапе компиляции
Но это уж зависит от того, что именно вам нужно.
Для склеивания во время выполнения использовать функцию strcat — не самая лучшая идея из-за того, что при каждом склеивании функция снова и снова выполняет сканирование уже построенной части строки. (По этой причине strcat — фактически бесполезная функция.) Лучше уж воспользоваться для этих целей обычным snprintf
А уж какой способ резервирования памяти для строки-получателя вам больше подходит — зависит от ваших конкретных обстоятельств и требований.
Раз уж пошло такое массовое отвечание некропоста, то внесу и свою лепту.
В GNU есть очень удобный аналог sprintf — asprintf, которая размещает результат «вывода» своих аргументов в динамической памяти (использует malloc).
Соответственно, программа может быть такой:
А если у вас ее (asprintf) нет, то несложно и самому написать
(или скопипастить) и использовать уже без всяких #define _GNU_SOURCE даже в винде (по крайней мере MinGW успешно транслирует).
- Выделяем нужное количество памяти
- Склеиваем и возвращаем указатель на начало блока памяти.
Потому что функция strcat() добавляет
-
строку, на которую указывает второй параметр ( «second» )
к строке, на которую указывает первый параметр ( «first» )
и затем вращает первый параметр — указатель на первую строку, но к которой теперь добавлена и вторая строка,
нужно для первого параметра выделить достаточную память для соединенной строки ( «firstsecond» ) — или во время компиляции, или динамически.
Как склеить строки в си
Для того, чтобы точно знать, сколько выделить памяти для хранения строки, часто нам нужно получить длину конкретной строки. Разного рода функции иногда предлагают не просто ввести строку, а указать какое количество символов из неё мы хотели бы использовать. Чаще всего мы хотим ввести всю строку, поэтому есть смысл не считать вручную, а просто измерить длину строки и подставить в функцию.
#include<iostream.h>
#include<string.h>
>
Функция strlen() возвращает количество символов в строке. Например для строки "машина" она вернёт число 6.
Вопрос: Как сложить две строки?
Помните мы писали программу, которая c нами здоровалась? Там была строка:
И умная программка добавляла в конец ваше имя.
А если мы хотим сложить две строки, которые заранее не известны? Для этого в программировании применяется операция "склеивания" — конкатенции (знаю — жуткое слово!), которая позволяет просто сложить две строки вместе и получить одну. В языке СИ такой операции нет, но зато в библиотеке string.h есть специальная функция strncat(), которая позволяет склеивать исходную строку с подстрокой.
void main()
<
char kuda[20];
char *otkuda="Федерация";
strcpy(kuda, "Российская "); //копирование в строку kuda
strncat(kuda, otkuda, strlen(otkuda)); //склеиваем две строки, результат — в строке kuda
cout<<kuda; //на экране фраза "Российская Федерация".
Функция strcpy(to, from) копирует строку from в строку to. Кстати, это ещё один из способов присвоения строке значения. Функция strncat объединяет две строки, дописывая в конец строки kuda строку otkuda. Этот процесс можно сравнить с локомотивом, в конец которого на товарной станции прицепляют ещё один вагончик. Для каждого нового вагона нам понадобится функция strncat.
Обратите внимание, что у функции strncat три аргумента. Функция гибка и позволяет скопировать не всю строку, а только её часть. Если мы точно знаем сколько символов в строке otkuda, можно третьим аргументом задать число, но проще всего измерить длину строки с помощью известной нам уже функции strlen().
Вопрос: КАК из строки сделать число?
Для чего это нужно? Часто программа получает данные от пользователя в виде строки. Например, данные полученные из текстовых полей Windows — всегда строковые. И даже если пользователь ввёл число, оно всё равно будет представлено, как строка. Чтобы подставить это число в форумулу, вам придётся его конвертировать в строку.
#include <stdlib.h>
#include <iosatream.h>
void main()
<
int n;
char *str="123456"; //объявляем строку
n=atoi(str); //конвертируем в число
В некоторых классах Windows есть методы, позволяющие сразу получать целые числа из текстовых полей, но метода, позволяющего сразу считывать числа с плавающей запятой, нет, поэтому в любом случае вам понадобится конвертирование. Для преобразования строки в число с плавающей запятой существует функция atof(char *str), которая по своему применению аналогична.
В этом примере мы используем новую для нас библиотеку "stdlib.h" — в данной библиотеке содержится большинство функций конвертирования, а также множество других функций самого разного назначения. Это библиотека очень широкого профиля.
После получения строки из поля ввода, конвертирования в число, подстановки в формулу, или куда-то ещё, ответ зачастую придётся выводить в какое-нибудь окно. Естественно, напрямую число в окно мы не сможем — оно нас просто не поймёт, и заставит выполнять обратное преобразование — теперь уже из числа в строку.
Иногда ответ должен сопровождаться каким-то текстом, например: "U = 127,24 v ". Для создания такого дружественного интерфейса удобнее использовать не саму операцию конвертирования, а записать в один буфер и число и текст.
Функция sprintf аналогична функции printf, с той лишь разницей, что вывод информации осуществляется не в какое-то устройство вывода, а в буфер данных. Используя эту функцию, мы сможем комбинировать различные типы данных в любой последовательности.
void main()
<
char buffer[25]; //строка, длиной 25 символов
const double f=127.24; //число с плавающей точкой
sprintf(buffer, "U = %3.2f V\n", f);
Однако, если нам необходимо просто преобразовать число в строку, можно использовать функцию конвертирования, описанную в следующем разделе.
Вопрос: КАК из числа сделать строку?
#include<stdio.h>
void main()
<
int number = 123456;
char *string;
itoa(number, string, 10); //число 10 заботится о добавлении минуса отрицательными числам
printf("Число = %d Строка = %s", number, string);
>
Конвертирование — очень важная задача, и в математических программах без неё никуда. Если ваша программа что-то считает, получая данные от пользователя, вы будете использовать конвертирование постоянно.
Вопрос: КАК что-то найти в строке?
Иногда нам нужно проверить строку на наличие в ней какого-нибудь символа. Например, если пользователь вводит свой E-mail хорошо бы проверить, есть ли в нём символ "at", который в России чаще называют собакой @? Кроме того, в каждом адресе электронной почты есть по крайней мере одна точка.
Следующая программа последовательно проверяет наличие в строке знака "@" и точки.
void main()
<
char *ptr;
ptr = (char *)memchr(email, ‘@’, strlen(email));
ptr = (char *)memchr(email, ‘.’, strlen(email));
printf("\n%s не является E-mail адресом", email);
>
else <
printf("\nE-mail адрес: %s успешно введён!", email);
Переменная flag — своеобразный выключатель, который имеет два состояния "истина" и "ложь". Такие переменные программисты часто так и называют "флагами" или "флажками". Они обозначают состояние. Первоначально переменная flag = 0. Но если программа не обнаруживает в строке знака "@" или точки, значение flag поменяется на 1. Условий может быть и больше (например, мы могли бы проверить, есть ли какие-то символы до знака "@" и находится ли точка после знака @, а не до него), однако работать они будут по тому же принципу.
В конце программы проверяется значение переменной flag, и если она изменила своё значение, E-mail адрес признаётся некорректным.
При разработки больших программ подобные проверки необходимо производить буквально на каждом шагу, они делают программу более надёжной и отказоустойчивой, страхуя нас от недопустимого типа данных. Согласитесь, если мы попытаемся отправить письмо на некорректный адрес, приятного в этом будет мало! Однако во избежание увеличения кода, мы заключим процедуру проверки адреса в маленькую проверочную функцию вида:
int IsEmail(char *email)
которая будет возвращать 0, если E-mail адрес был введён недостаточно корректно.
Часто производители программного обеспечения требуют регистрации и предлагают пользователю заполнить форму, в которой в том числе есть просьба указать электронный адрес. Знать ваш E-mail адрес производителю жизненно важно, чтобы впоследствии заваливать вас своей рекламой. Поэтому если вы введёте свой адрес неправильно или вообще проигнорируете эту просьбу, электронная форма откажется вас регистрировать.
Функция поиска в строке незаменима при работе с файлами (а храним мы данные именно в них — лучшего пока ничего не придумали). Есть и другие применения поиска: когда программа предлагает пользователю сохранить файл, с которым он работал, необходимо ввести его имя. Однако пользователи попадаются с совершенно разным профессиональным уровнем: кто-то вводит имена файлов с расширениями: "picture.bmp" , а кто-то и не знает, что такое расширение. Нужно предусмотреть оба случая, и в случае, если пользователь просто ввёл имя: "моя картинка. " добавить к этому имени расширение. Пример решения этой проблемы приведён ниже.
#include<stdio.h>
#include<string.h>
#include<conio.h>
void main()
<
char *ptr;
printf("Введите имя файла: ");
scanf("%s", filename);
ptr = (char *)memchr(filename, ‘.w’, strlen(filename));
printf("\nИмя файла: %s", filename);
Вопрос: КАК сравнить две строки?
Часто, когда вы получаете от пользователя не числовые данные, а строковые, вам надо сравнить строку с уже имеющимся эталоном. Допустим, вы хотите, чтобы пользователь зарегистрировал свою версию вашей программы и ввёл пароль. Для этого и нужна функция сравнения строк strncmp(). Она возвращает ноль, если строки одинаковы.
Следующая ниже программа вполе функциональна. Она ни за что не пропустит построннего на ваш компьютер. При желании, вы можете включить её в файл autoexec.bat. Доступ посторонним будет закрыт навеки! (Конечно если вы всё ещё пользуетесь Windows 98).
#include <stdio.h>
#include <string.h>
char *password="BUSH"; //наш пароль
char enter[256]; //строка, которую будет вводить пользователь
//сравниваем две строки по всей длине
ptr = strncmp(enter, password, strlen(password));
//если строки одинаковы
if (ptr == 0)
<
printf("Доступ открыт");
return(0);
>
else <
printf("Введён неверный пароль!");
>
>while(ptr!=0); //цикл повторяется, пока ptr не будет =0.
return 0;
>
Конечно в реальности никто не будет прописывать пароль в коде программы, иначе мало-мальски грамотный хаккер запросто его оттуда "достанет". Настоящие программы генерируют пароль из имени пользователя, таким образом, чтобы пароли никогда не повторялись. Также популярна онлайн регистрация, при которой пользователь получает пароль, регистрируясь на сайте фирмы. Получив такой пароль, программа преобразует его по сложному алгоритму и проверяет на соответствие какому-то правилу — например сумма кодов символов должна всегда быть равна 100 (или что-то в этом роде). Дружеский совет — никогда не давайте "говорящие" пароли, как в данном примере. Такие пароли разгадываются элементарно, путём простого перебора символов. Хороший пароль должен содержать цифры и буквенные символы разного регистра, также неплохо было бы включить в него специальные символы. В результате должно получиться нечто вроде: "$&weh!60Om17rv".
Библиотеки и функции для работы со строками в си
Библиотека string.h предоставляет функции для работы со строками (zero-terminated strings) в си, а также несколько функций для работы с массивами, которые сильно упрощают жизнь. Рассмотрим функции с примерами.
Копирование
Копирует участок памяти из source в destination, размером num байт. Функция очень полезная, с помощью неё, например, можно скопировать объект или перенести участок массива, вместо поэлементного копирования. Функция производит бинарное копирование, тип данных не важен. Например, удалим элемент из массива и сдвинем остаток массива влево.
Функция меняет местами две переменные
Здесь хотелось бы отметить, что функция выделяет память под временную переменную. Это дорогостоящая операция. Для улучшения производительности стоит передавать функции временную переменную, которая будет создана один раз.
Копирует блок памяти из source в destination размером num байт с той разницей, что области могут пересекаться. Во время копирования используется промежуточный буфер, который предотвращает перекрытие областей.
Копирует одну строку в другую, вместе с нулевым символом. Также возвращает указатель на destination.
Можно копировать и по-другому
Копирует только num первых букв строки. 0 в конец не добавляется автоматически. При копировании из строки в эту же строку части не должны пересекаться (при пересечении используйте memmove)
Конкатенация строк
Добавляет в конец destination строку source, при этом затирая первым символом нулевой. Возвращает указатель на destination.
Добавляет в конец строки destination num символов второй строки. В конец добавляется нулевой символ.
Сравнение строк
Возвращает 0, если строки равны, больше нуля, если первая строка больше, меньше нуля, если первая строка меньше. Сравнение строк происходит посимвольно, сравниваются численные значения. Для сравнения строк на определённом языке используется strcoll
сравнение строк по первым num символам
Пример — сортировка массива строк по первым трём символам
Трансформация строки в соответствии с локалью. В строку destination копируется num трансформированных символов строки source и возвращается её длина. Если num == 0 и destination == NULL, то возвращается просто длина строки.
Поиск
Проводит поиск среди первых num байтов участка памяти, на который ссылается ptr, первого вхождения значения value, которое трактуется как unsigned char. Возвращает указатель на найденный элемент, либо NULL.
Возвращает указатель на место первого вхождения character в строку str. Очень похожа на функцию memchr, но работает со строками, а не с произвольным блоком памяти.
Возвращает адрес первого вхождения любой буквы из строки str2 в строке str1. Если ни одно включение не найдено, то возвратит длину строки.
Пример — найдём положение всех гласных в строке
Здесь обратите внимание на строку i++ после printf. Если бы её не было, то strcspn возвращал бы всегда 0, потому что в начале строки стояла бы гласная, и произошло зацикливание.
Для решения этой задачи гораздо лучше подошла функция, которая возвращает указатель на первую гласную.
Функция очень похожа на strcspn, только возвращает указатель на первый символ из строки str1, который есть в строке str2. Выведем все гласные в строке
Возвращает указатель на последнее вхождение символа в троку.
Возвращает длину куска строки str1, начиная от начала, который состоит только из букв строки str2.
Пример — вывести число, которое встречается в строке.
Возвращает указатель на первое вхождение строки str2 в строку str1.
Разбивает строку на токены. В данном случае токенами считаются последовательности символов, разделённых символами, входящими в группу разделителей.
Ещё функции
Самая популярная функция
Возвращает длину строки — число символов от начала до первого вхождения нулевого.
Конверсия число-строка и строка-число.
Переводит строку в целое
Переводит строку в число типа double.
Переводит строку в число типа long Все функции такого рода имеют название XtoY, где X и Y — сокращения типов. A обозначает ASCII. Соответственно, имеется обратная функция itoa (больше нет:)). Таких функций в библиотеке stdlib.h очень много, все их рассматривать не хватит места.
Форматированный ввод и вывод в буфер
Можно также выделить две функции sprintf и sscanf. Они отличаются от printf и scanf тем, что выводят данные и считывают их из буфера. Это, например, позволяет переводить строку в число и число в строку. Например
Вообще, работа со строками — задача более глобальная, чем можно себе представить. Так или иначе, практически каждое приложение связано с обработкой текста.
Работа с локалью
Устанавливает локаль для данного приложения. Если locale равно NULL, то setlocale может быть использована для получения текущей локали.
Локаль хранит информацию о языке и регионе, специфичную для работы функций ввода, вывода и трансформации строк. Во время работы приложения устанавливается локаль под названием «C», которая совпадает с настройками локали по умолчанию. Эта локаль содержит минимум информации, и работа программы максимально предсказуема. Локаль «C» также называется «». Константы category определяют, на что воздействует изменение локали.
| Имя | На что влияет |
|---|---|
| LC_ALL | На всю локаль |
| LC_COLLATE | На поведение strcoll и strxfrm. |
| LC_CTYPE | На поведение функций, работающих с символами. |
| LC_NUMERIC | На десятичный разделитель в числах. |
| LC_TIME | На поведение strftime. |
Строка locale содержит имя локали, например «En_US» или «cp1251»

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