Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Язык C

Строки в C: null-терминированные массивы символов, функции библиотеки string.h и лучшие практики

Что такое строка в C?

В C нет встроенного строкового типа. Строка — это массив char, заканчивающийся нуль-терминатором '\0' (код 0). Длина строки = количество символов до терминатора.

char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // явная инициализация
char str2[] = "Hello";                          // неявный \0, размер 6
char *str3 = "Hello";                           // указатель на строковый литерал (read-only!)

Память:

Стандартная библиотека <string.h>

ФункцияОписаниеВозврат
strlen(s)Длина строки (без \0)size_t
strcpy(dest, src)Копирует src в dest (с \0). Не проверяет границы!dest
strncpy(dest, src, n)Копирует до n символов. Не дописывает \0, если src длиннее!dest
strcat(dest, src)Дописывает src в конец dest. Не проверяет границы!dest
strncat(dest, src, n)Дописывает до n символов, гарантирует \0.dest
strcmp(s1, s2)Лексикографическое сравнение<0, 0, >0
strncmp(s1, s2, n)Сравнение первых n символов<0, 0, >0
strchr(s, c)Поиск первого вхождения символа cУказатель или NULL
strrchr(s, c)Поиск последнего вхожденияУказатель или NULL
strstr(haystack, needle)Поиск подстрокиУказатель или NULL
strtok(str, delim)Разбиение на токены (модифицирует строку, не потокобезопасна)Указатель на токен
memcpy(dest, src, n)Копирует n байт (может перекрываться? нет, используйте memmove)dest
memmove(dest, src, n)Копирует n байт, корректно при перекрытииdest
memset(ptr, val, n)Заполняет n байт значением valptr
memcmp(s1, s2, n)Сравнивает n байт<0, 0, >0

Безопасные аналоги (C11 Annex K — опционально)

strcpy_s(dest, destsz, src);
strcat_s(dest, destsz, src);
strncpy_s(dest, destsz, src, n);

Проверяют границы, возвращают код ошибки. Не все компиляторы поддерживают (MSVC да, GCC/Clang — через __STDC_LIB_EXT1__).

Примеры использования

Конкатенация с проверкой размера

char buf[100] = "Hello";
const char *add = ", World!";
if (strlen(buf) + strlen(add) + 1 <= sizeof(buf)) {
    strcat(buf, add);
} else {
    // ошибка: не хватает места
}

Безопасное копирование

// strncpy не гарантирует \0!
char dest[10];
strncpy(dest, source, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; // принудительный терминатор

Поиск и парсинг

char *line = "user:admin:12345";
char *colon = strchr(line, ':');
if (colon) {
    *colon = '\0'; // разделяем: line="user", colon+1="admin:12345"
    printf("User: %s\n", line);
    // дальше парсим colon+1...
}

strtok — осторожно!

char str[] = "apple,banana,cherry"; // должен быть изменяемым массивом!
char *token = strtok(str, ",");
while (token) {
    printf("Token: %s\n", token);
    token = strtok(NULL, ","); // NULL = продолжить ту же строку
}
// strtok НЕ потокобезопасна (хранит состояние в static переменной).
// Альтернатива: strtok_r (POSIX) или strsep (BSD).

Динамические строки

Для строк неизвестной/меняющейся длины используйте структуру с буфером:

typedef struct {
    char *data;
    size_t len;
    size_t capacity;
} String;

void str_init(String *s) {
    s->data = malloc(16);
    s->data[0] = '\0';
    s->len = 0;
    s->capacity = 16;
}

void str_append(String *s, const char *src) {
    size_t need = s->len + strlen(src) + 1;
    if (need > s->capacity) {
        s->capacity = need * 2;
        s->data = realloc(s->data, s->capacity);
    }
    strcpy(s->data + s->len, src);
    s->len = strlen(s->data);
}

void str_free(String *s) {
    free(s->data);
    s->data = NULL;
    s->len = s->capacity = 0;
}

UTF-8 и Unicode

Форматированный ввод-вывод строк

// Вывод
printf("Name: %s, Age: %d\n", name, age);
snprintf(buf, sizeof(buf), "Value: %.2f", val); // безопасный sprintf

// Ввод
char name[100];
scanf("%99s", name); // %99s — максимум 99 символов + \0 (защита от переполнения)
// fgets(name, sizeof(name), stdin); // читает строку с пробелами, включает \n

Частые ошибки и UB

Лучшие практики

  1. Всегда проверяйте границы буфера. Используйте snprintf, strncat, ручное управление размером.
  2. Для динамических строк — структура с len/capacity + realloc.
  3. Используйте const char * для параметров-функций, не меняющих строку.
  4. Избегайте gets() (удалена в C11) — используйте fgets().
  5. Для сложной работы со строками — подключайте библиотеку (например, sds — simple dynamic strings от Redis, или bstring).

Язык C

  1. Строки в C#: основы, операции и продвинутые техники
  2. Освоение строк в Java: создание, методы и лучшие практики
  3. Строковые представления в Java Enums: настройка toString() и лучшие практики
  4. Создание списков строк в VHDL: лучшие практики и пример
  5. Функции строк в C++: strcpy, strcat, strlen, strcmp — практические примеры
  6. Строки в C просто: объявление, ввод, вывод и библиотечные функции
  7. Понимание String.charAt() в Java: синтаксис, возвращаемый тип, исключения и пример
  8. Освоение String.endsWith() в Java: проверка суффиксов строки с примерами
  9. Освоение строковых операций в Python: replace, join, split, reverse, преобразование регистра
  10. Мастерство метода str.count() в Python: как подсчитывать символы и подстроки с примерами