Пример программы на си со строками

Библиотека string.h предоставляет функции для работы со строками (zero-terminated strings) в си, а также несколько функций для работы с массивами, которые сильно упрощают жизнь. Рассмотрим функции с примерами.

  • Копирование
  • Конкатенация (склеивание)
  • Сравнение
  • Поиск
  • Длина строки и заполнение символами
  • Перевод строка-число и число-строка
  • Форматированный ввод и вывод в буфер
  • Работа с локалью

Копирование

void * memcpy (void * destination, const void * source, size_t num);

Копирует участок памяти из source в destination, размером num байт. Функция очень полезная, с помощью неё, например, можно скопировать объект или перенести участок массива, вместо поэлементного копирования. Функция производит бинарное копирование, тип данных не важен. Например, удалим элемент из массива и сдвинем остаток массива влево.

#include #include #include #define SIZE 10 int main() < int a[SIZE] = ; unsigned index; int i; printf(«Enter index [0 .. 9]»); scanf(«%ud», index = index < SIZE? index: SIZE-1; memcpy(a[index+1], sizeof(int) * (SIZE — index — 1)); for (i = 0; i < SIZE; i++) < printf(«%d «, a[i]); >getch(); >

Функция меняет местами две переменные

Язык Си с нуля — Урок 46 — Подробно про строки. Строковые литералы. Склеивание строк.

#include #include #include #include void swap(void* a, void* b, size_t size) < void *tmp = malloc(size); memcpy(tmp, a, size); memcpy(a, b, size); memcpy(b, tmp, size); free(tmp); >int main()

Здесь хотелось бы отметить, что функция выделяет память под временную переменную. Это дорогостоящая операция. Для улучшения производительности стоит передавать функции временную переменную, которая будет создана один раз.

#include #include #include #include void swap(void* a, void* b, void* tmp, size_t size) < memcpy(tmp, a, size); memcpy(a, b, size); memcpy(b, tmp, size); >int main()

void* memmove (void * destination, const void * source, size_t num);

Копирует блок памяти из source в destination размером num байт с той разницей, что области могут пересекаться. Во время копирования используется промежуточный буфер, который предотвращает перекрытие областей.

#include #include #include void main ()

char* strcpy (char * destination, const char* source );

Копирует одну строку в другую, вместе с нулевым символом. Также возвращает указатель на destination.

#include #include #include #include void main ()

Можно копировать и по-другому

#include #include #include #include void main ()

char* strncpy (char* destination, const char* source, size_t num);

Копирует только num первых букв строки. 0 в конец не добавляется автоматически. При копировании из строки в эту же строку части не должны пересекаться (при пересечении используйте memmove)

#include #include #include #include void main ()

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

char* strcat (char * destination, const char * source);

Добавляет в конец destination строку source, при этом затирая первым символом нулевой. Возвращает указатель на destination.

Строки в языке программирования С++


char* strncat (char * destination, const char * source, size_t num);

Добавляет в конец строки destination num символов второй строки. В конец добавляется нулевой символ.

#include #include #include void main () < char a[255]; char b[128]; scanf(«%127s», a); scanf(«%127s», b); strncat(a, b, strlen(b)/2); printf(«%s», a); getch(); >

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

int strcmp (const char * str1, const char * str2);

Возвращает 0, если строки равны, больше нуля, если первая строка больше, меньше нуля, если первая строка меньше. Сравнение строк происходит посимвольно, сравниваются численные значения. Для сравнения строк на определённом языке используется strcoll

int strcoll (const char * str1, const char * str2);
int strncmp (const char * str1, const char * str2, size_t num);

сравнение строк по первым num символам
Пример — сортировка массива строк по первым трём символам

#include #include #include #include int cmp(const void *a, const void *b) < return strncmp((char*) a, (char*) b, 3); >void main() < char words[][128] = < «Solar», «Obscurus», «Tempestus», «Ultima», «Pacificus» >; int i; qsort(words, 5, 128, cmp); for (i = 0; i < 5; i++) < printf(«%sn», words[i]); >getch(); >
size_t strxfrm (char * destination, const char * source, size_t num);

Трансформация строки в соответствии с локалью. В строку destination копируется num трансформированных символов строки source и возвращается её длина. Если num == 0 и destination == NULL, то возвращается просто длина строки.

#include #include #include #include void main() < char input[128]; char output[128]; scanf(«%127s», input); //Выводи введённую строку printf(«%sn», input); //Проводим преобразование, ничего не меняется strxfrm(output, input, 128); printf(«%sn», output); //Изменяем локаль setlocale(LC_ALL, «.1251»); strxfrm(output, input, 128); printf(«%sn», output); getch(); >

Поиск

void* memchr (void * ptr, int value, size_t num);

Проводит поиск среди первых num байтов участка памяти, на который ссылается ptr, первого вхождения значения value, которое трактуется как unsigned char. Возвращает указатель на найденный элемент, либо NULL.

#include #include #include #include void main() < char str[] = «Hello World!»; char *ptr = NULL; ptr = (char*) memchr(str, ‘’, 4000); if (ptr != NULL) < printf(«first zero byte address is %p, strlen = %d», ptr, ptr — str); >else < printf(«no null byte in memory block»); >getch(); >
char* strchr (char * str, int character);

Возвращает указатель на место первого вхождения character в строку str. Очень похожа на функцию memchr, но работает со строками, а не с произвольным блоком памяти.

size_t strcspn ( const char * str1, const char * str2 );

Возвращает адрес первого вхождения любой буквы из строки str2 в строке str1. Если ни одно включение не найдено, то возвратит длину строки.
Пример — найдём положение всех гласных в строке

#include #include #include void main() < char str[] = «So if you want to love men» «Then darling don’t refrainn» «Or I’ll just end up walkingn» «In the cold November rainn»; char vowels[] = «aeiouy»; int i; i = 0; while (str[i]) < i = i + strcspn( printf(«%d «, i); i++; >getch(); >

Читайте также:
Чем хороша программа Телеграмм

Здесь обратите внимание на строку i++ после printf. Если бы её не было, то strcspn возвращал бы всегда 0, потому что в начале строки стояла бы гласная, и произошло зацикливание.
Для решения этой задачи гораздо лучше подошла функция, которая возвращает указатель на первую гласную.

char* strpbrk (char * str1, const char * str2)

Функция очень похожа на strcspn, только возвращает указатель на первый символ из строки str1, который есть в строке str2. Выведем все гласные в строке

#include #include #include void main() < char str[] = «Cos’ it’s a bittersweet symphony this life. n» «Trying to make ends meet, you’re a slave to the money then you die.»; char vowels[] = «aeiouy»; char *p = NULL; p = strpbrk(str, vowels); while (p) < printf(«%c «, *p); p++; p = strpbrk(p, vowels); >getch(); >
char* strrchr (char * str, int character );

Возвращает указатель на последнее вхождение символа в троку.

size_t strspn (const char * str1, const char * str2);

Возвращает длину куска строки str1, начиная от начала, который состоит только из букв строки str2.
Пример — вывести число, которое встречается в строке.

#include #include #include void main() < char str[] = «on 21st of May»; char nums[] = «0123456789»; char number[10]; uintptr_t i; //Определяем, где начинаются цифры size_t start = strcspn(str, nums); //Определяем, где они заканчиваются, относительно start size_t end = strspn( for (i = 0; i < end; i++) < printf(«%c», str[start+i]); >getch(); >
char* strstr (char * str1, const char * str2);

Возвращает указатель на первое вхождение строки str2 в строку str1.

#include #include #include void main()

char* strtok (char * str, const char * delimiters);

Разбивает строку на токены. В данном случае токенами считаются последовательности символов, разделённых символами, входящими в группу разделителей.

#include #include #include void main() < char str[] = «After working in India during the late 1970s and 1980s, » «Shankar’s profile in the West began to rise again in the mid-1990s » «as his music found its way into club DJ sets, particularly in London.»; char delim[] = » tn,.-«; char *p = strtok(str, delim); while (p != NULL) < printf («%sn»,p); p = strtok (NULL, delim); >getch(); >

Ещё функции

void * memset (void * ptr, int value, size_t num);

Заполняет блок памяти символами value числом num штук. Например, можно заполнить массив или структуру нулями.

#include #include #include void main() < int arr[10][10]; int i; memset(arr, 0, 10*10*sizeof(int)); for (i = 0; i < 10; i++) < arr[i][i] = 1; >for (i = 0; i < 10; i++) < int j; for (j = 0; j < 10; j++) < printf(«%d», arr[i][j]); >printf(«n»); > getch(); >

Самая популярная функция

size_t strlen ( const char * str );

Возвращает длину строки — число символов от начала до первого вхождения нулевого.

Конверсия число-строка и строка-число.

int atoi (const char * str);

Переводит строку в целое

#include #include #include void main()

double atof (const char* str);

Переводит строку в число типа double.

long int atol ( const char * str );

Переводит строку в число типа long Все функции такого рода имеют название XtoY, где X и Y — сокращения типов. A обозначает ASCII. Соответственно, имеется обратная функция itoa (больше нет:)). Таких функций в библиотеке stdlib.h очень много, все их рассматривать не хватит места.

Форматированный ввод и вывод в буфер

Можно также выделить две функции sprintf и sscanf. Они отличаются от printf и scanf тем, что выводят данные и считывают их из буфера. Это, например, позволяет переводить строку в число и число в строку. Например

#include #include #include void main()

Вообще, работа со строками — задача более глобальная, чем можно себе представить. Так или иначе, практически каждое приложение связано с обработкой текста.

Работа с локалью

char* setlocale (int category, const char* locale);

Устанавливает локаль для данного приложения. Если locale равно NULL, то setlocale может быть использована для получения текущей локали.

Локаль хранит информацию о языке и регионе, специфичную для работы функций ввода, вывода и трансформации строк. Во время работы приложения устанавливается локаль под названием «C», которая совпадает с настройками локали по умолчанию. Эта локаль содержит минимум информации, и работа программы максимально предсказуема. Локаль «C» также называется «». Константы category определяют, на что воздействует изменение локали.

Значения параметра category

Имя На что влияет
LC_ALL На всю локаль
LC_COLLATE На поведение strcoll и strxfrm.
LC_CTYPE На поведение функций, работающих с символами.
LC_NUMERIC На десятичный разделитель в числах.
LC_TIME На поведение strftime.

Строка locale содержит имя локали, например «En_US» или «cp1251»

email

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

Источник: learnc.info

Си работа со строками

Строка в языке Си представляет собой одномерный массив символов, последним элементом которой является символ конца строки – нуль (строка, завершающаяся нулем, то есть NULL terminated string).

Объявление переменной типа строка в языке Си возможно тремя способами, два из которых инициализируют строку во время объявления.

Первый способ:

Объявления массива символов (не забудьте добавить место для завершающего нуля):

char s[40+1];

Второй способ:

Присвоить строковой переменной начальное значение (при этом длину строки компилятор может вычислить сам):

char s[] = «Пример инициализации строки»;

Справа от знака присваивания записана строковая константа. В конце строки автоматически добавляется ноль (‘’). Константы символьных строк помещаются в класс статической памяти.

Третий способ:

Неявное указание, что используется массив. В левой части от знака присваивания указывается указатель на символ:

char *s=»Второй вариант инициализации»;

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

char *s;

Ввод строки со стандартного устройства ввода (клавиатуры)

Для работы со строками есть набор функций. Для ввода со стандартного устройства ввода (клавиатуры) чаще всего используются библиотечные функциями из модуля стандартного ввода-вывода: scanf и gets.

Для ввода строки с помощью функции scanf, использует формат «%s», причем обратите внимание на то, что перед идентификатором строки не используется знак адреса «%s», s);

Читайте также:
Лучшая бесплатная программа для просмотра изображений

Функция gets() считывает символы до тех пор, пока не достигнет символа перехода на новую строку. Функция принимает все символы вплоть до символа перевода строки, но не включает его. К концу строки добавляется завершающий ноль (‘’). Функция gets() помещает считанную с клавиатуры последовательность символов в параметр типа строка и возвращает указатель на эту строку (если операция завершилась успешно), или NULL (в случае ошибки). В приведенном ниже примере при успешном завершении операции, на экран будет выведено две одинаковые строки:

#include int main()

Попутно заметим, что функция gets часто используется для ввода лю-бых данных с клавиатуры в виде строки с целью дальнейшего преобразования функцией sscanf к нужному формату или для предварительного анализа вводимых данных, например:

#include #include #include int main() < char s[50]; int x, err; do < printf(» n Введите целое число ->»); gets(s); err=sscanf(s, «%d», if (err!=1) printf(» n Ошибка ввода. «); > while (err!=1); printf(«n Введено целое число -> %d», x); return 0; >

Вывод строк на стандартное устройство вывода (экран монитора)

Для вывода строк на стандартное устройство вывода (экран монитора) можно использовать две функции printf и puts.

В функции printf в качестве формата передается «%s». Удобство использования этой функции заключается в том, что помимо строки можно сразу выводит данные других типов. Особенность функции puts заключается в том, что после вывода строки автоматически происходит переход на следующую строку.

Функции для работы со строками

Для преобразования строк в языке Си предусмотрена библиотека string. Каждая из функций имеет свой формат записи (прототип).

Наиболее используемые функции рассмотрены в этой статье. — читать

Пример программ(листинг) работающей со строками

Источник: itmemo.ru

Немного о строках в Си, или несколько вариантов оптимизировать неоптимизируемое

Не так давно у со мной произошел довольно-таки интересный инцидент, в котором был замешан один из преподавателей одного колледжа информатики.

Разговор о программировании под Linux медленно перешел к тому, что этот человек стал утверждать, что сложность системного программирования на самом деле сильно преувеличена. Что язык Си прост как спичка, собственно как и ядро Linux (с его слов).

У меня был с собой ноутбук с Linux, на котором присутствовал джентльменский набор утилит для разработки на языке Си (gcc, vim, make, valgrind, gdb). Я уже не помню, какую цель мы тогда перед собой поставили, но через пару минут мой оппонент оказался за этим ноутбуком, полностью готовый решать задачу.

И буквально на первых же строках он допустил серьезную ошибку при аллоцировании памяти под… строку.

char *str = (char *)malloc(sizeof(char) * strlen(buffer));

buffer — стековая переменная, в которую заносились данные с клавиатуры.

Я думаю, определенно найдутся люди, которые спросят: «Разве что-то тут может быть не так?».
Поверьте, может.

А что именно — читайте по катом.

Немного теории — своеобразный ЛикБез.

Если знаете — листайте до следующего хэдера.

Строка в C — это массив символов, который по-хорошему всегда должен заканчиваться ‘’ — символом конца строки. Строки на стеке (статичные) объявляются вот так:

char str[n] = < 0 >;

n — размер массива символов, то же, что и длина строки.

Присваивание < 0 >— «зануление» строки (опционально, объявлять можно и без него). Результат такой же, как у выполнения функций memset(str, 0, sizeof(str)) и bzero(str, sizeof(str)). Используется, чтобы в неинициализированных переменных не валялся мусор.

Так же на стеке можно сразу проинициализировать строку:

char buf[BUFSIZE] = «default buffer textn»;

Помимо этого строку можно объявить указателем и выделить под нее память на куче (heap):

char *str = malloc(size);

size — количество байт, которые мы выделяем под строку. Такие строки называются динамическими (вследствие того, что нужный размер вычисляется динамически + выделенный размер памяти можно в любой момент увеличить с помощью функции realloc() ).

В случае со стековой переменной, для определения размера массива я использовал обозначение n, в случае с переменной на куче — я использовал обозначение size. И это прекрасно отражает истинную суть отличия объявления на стеке от объявление с аллоцированием памяти на куче, ведь n как правило используется тогда, когда говорят о количестве элементов. А size — это уже совсем другая история…

Думаю. пока хватит. Идем дальше.

Нам поможет valgrind

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

Давайте рассмотрим небольшой листинг, в котором реализовано что-то похожее на упомянутую мной программу, и прогоним ее через valgrind:

#include #include #include #define HELLO_STRING «Hello, Habr!n» void main() < char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf(«->t%s», str); free(str); >

И, собственно, результат работы программы:

Пока ничего необычного. А теперь давайте запустим эту программу с valgrind!

==3892== All heap blocks were freed — no leaks are possible — утечек нет, и это радует. Но стоит опустить глаза чуть пониже (хотя, хочу заметить, это лишь итог, основная информация немного в другом месте):

==3892== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
3 ошибки. В 2х контекстах. В такой простой программе. Как!?

Да очень просто. Весь «прикол» в том, что функция strlen не учитывает символ конца строки — ‘’. Даже если его явно указать во входящей строке (#define HELLO_STRING «Hello, Habr!n»), он будет проигнорирован.

Чуть выше результата исполнения программы, строки -> Hello, Habr! есть подробный отчет, что и где не понравилось нашему драгоценному valgrind. Предлагаю самостоятельно посмотреть эти строчки и сделать выводы.

Собственно, правильная версия программы будет выглядеть так:

#include #include #include #define HELLO_STRING «Hello, Habr!n» void main() < char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf(«->t%s», str); free(str); >

Пропускаем через valgrind:

Отлично. Ошибок нет, +1 байт выделяемой памяти помог решить проблему.

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

Читайте также:
Стиральная машинка не выполняет программу что делать

Однако, знаете, (strlen(str) + 1) — такое себе решение. Перед нами встают 2 проблемы:

  1. А если нам надо выделить память под формируемую с помощью, например, s(n)printf(..) строку? Аргументы мы не поддерживаем.
  2. Внешний вид. Строка с объявлением переменной выглядит просто ужасно. Некоторые ребята к malloc еще и (char *) умудряются прикручивать, будто под плюсами пишут. В программе где регулярно требуется обрабатывать строки есть смысл найти более изящное решение.

snprintf()

int snprintf(char *str, size_t size, const char *format, . ); — функция — расширение sprintf, которая форматирует строку и записывает ее по указателю, переданному в качестве первого аргумента. От sprintf() она отличается тем, что в str не будет записано байт больше, чем указано в size.

Функция имеет одну интересную особенность — она в любом случае возвращает размер формируемой строки (без учета символа конца строки). Если строка пустая, то возвращается 0.

Одна из описанных мною проблем использования strlen связана с функциями sprintf() и snprintf(). Предположим, что нам надо что-то записать в строку str. Конечная строка содержит значения других переменных. Наша запись должна быть примерно такой:

char * str = /* тут аллоцируем память */; sprintf(str, «Hello, %sn», «Habr!»);

Встает вопрос: как определить, сколько памяти надо выделить под строку str?

char * str = malloc(sizeof(char) * (strlen(str, «Hello, %sn», «Habr!») + 1));

— не прокатит. Прототип функции strlen() выглядит так:

#include size_t strlen(const char *s);

const char *s не подразумевает, что передаваемая в s строка может быть строкой формата с переменным количеством аргументов.

Тут нам поможет то полезное свойство функции snprintf(), о котором я говорил выше. Давайте посмотрим на код следующей программы:

#include #include #include void main() < /* Т.к. snprintf() не учитывает символ конца строки, прибавляем его размер к результату */ size_t needed_mem = snprintf(NULL, 0, «Hello, %s!n», «Habr») + sizeof(‘’); char *str = malloc(needed_mem); snprintf(str, needed_mem, «Hello, %s!n», «Habr»); printf(«->t%s», str); free(str); >

Запускаем программу в valgrind:

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

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

size_t needed_mem = snprintf(NULL, 0, «Hello, %s!n», «Habr») + sizeof(‘’);

выглядит еще хуже, чем в случае с strlen().

Вообще, + sizeof(‘’) можно убрать, если в конце строки формата явно указать ‘’ (size_t needed_mem = snprintf(NULL, 0, «Hello, %s!n», «Habr»);), но это возможно отнюдь не всегда (в зависимости от механизма обработки строк мы можем выделить лишний байт).

Надо что-то сделать. Я немного подумал и решил, что сейчас настал час воззвать к мудрости древних. Опишем макрофункцию, которая будет вызывать snprintf() с нулевым указателем в качестве первого аргумента, и нулем, в качестве второго. Да и про конец строки не забудем!

#define strsize(args. ) snprintf(NULL, 0, args) + sizeof(‘’)

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

Проверим наше решение на практике:

#include #include #include #define strsize(args. ) snprintf(NULL, 0, args) + sizeof(‘’) void main() < char *str = malloc(strsize(«Hello, %sn», «Habr!»)); sprintf(str, «Hello, %sn», «Habr!»); printf(«->t%s», str); free(str); >

Запускаем с valgrund:

Да, ошибок нет. Все корректно. И valgrind доволен, и программист наконец может пойти поспать.

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

Речь идет о функции asprintf:

#define _GNU_SOURCE /* See feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, . );

В качестве первого аргумента она принимает указатель на строку (**strp) и аллоцирует память по разыменованному указателю.

Наша программа, написанная с использованием asprintf() будет выглядеть так:

#include #include #include void main() < char *str; asprintf(Hello, %s!n», «Habr»); printf(«->t%s», str); free(str); >

И, собственно, в valgrind:

Все отлично, но, как видите, памяти всего было выделено больше, да и alloc’ов теперь три, а не два. На слабых встраиваемых системах использование это функции нежелательно.
К тому же, если мы напишем в консоли man asprintf, то увидим:

CONFORMING TO These functions are GNU extensions, not in C or POSIX. They are also available under *BSD. The FreeBSD implementation sets strp to NULL on error.

Отсюда ясно, что данная функция доступна только в исходниках GNU.

Заключение

В заключение я хочу сказать, что работа со строками в C — это очень сложная тема, которая имеет ряд нюансов. Например, для написания «безопасного» кода при динамическом выделении памяти рекомендуется все же использовать функцию calloc() вместо malloc() — calloc забивает выделяемую память нулями. Ну или после выделения памяти использовать функцию memset(). Иначе мусор, который изначально лежал на выделяемом участке памяти, может вызвать вопросы при дебаге, а иногда и при работе со строкой.

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

Я надеюсь, кому-то эта статья будет полезной. К чему я это все городил — никакой язык не бывает прост. Везде есть свои тонкости. И чем больше тонкостей языка вы знаете, тем лучше ваш код.

Я верю, что после прочтения этой статьи ваш код станет чуточку лучше 🙂
Удачи, Хабр!

Источник: habr.com

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru