C не дбає про рядки. Насправді.
Він розглядає їх як масив символів. Просто байти у пам’яті. Щоб ефективно використовувати їх, вам потрібні покажчики. Не тому що це весело. А тому що без них ви виконуєте зайву роботу.
Рядок у C – це просто char str[100]. Це виглядає як місце для 100 символів. Але це негаразд. Це місце для 99 символів плюс термінатор. C використовує рядки з нуль-термінатором. Кожен рядок закінчується значенням ASCII 0. Записується як `\0'.
Це змінює все.
Інші мови обробляють рядки інакше. Pascal використовує байт довжини. Він точно знає скільки символів зберігається. Запитаєте довжину? Він поверне цей байт. Миттєво.
У C доводиться рахувати. Він читає дані, поки не зустріне “\0”. Це робить C повільніше у деяких випадках. Швидше за інших. Все залежить від того, що ви робите.
C немає вбудованої підтримки рядків. Ви покладаєтеся на бібліотеки. обробляє введення та виведення, такі функції як gets та puts. обробляє маніпуляції з рядками. Деякі системи використовують . Ви повинні керувати пам’яттю самостійно. Ви не можете просто привласнити один масив іншому.
`
Ось і все. Ви копіюєте елемент за елементом. Або використовуєте strcpy. Функція бібліотеки виконує основну роботу.
strcpy всюди C. Вона ініціалізує рядки. Копіює дані.
`
Після виконання цього коду s1 містить “hello”, а s2 також містить “hello”. Масив зберігає значення ASCII. Цілі числа. h – це 104. e – це 101. C мислить байтами. Ви мислите текстом. Машині все одно.
Порівняння виконується за допомогою strcmp. Вона повертає ціле число.
Нуль означає рівність. Негативне значення означає, що перший рядок менший. Позитивне значення означає, що вона більша.
`
Існують інші функції. strlen повертає довжину. strcat виконує конкатенацію. Якщо потрібно більше інформації, прочитайте сторінку посібника (man page).
Але як ці функції працюють ізсередини? Давайте подивимося на strlen.
Наївний підхід виглядає так:
`
Більшість програмістів на C ненавидять цей код. Він видається неефективним. Вони віддають перевагу покажчикам.
`
Його можна стиснути ще більше.
`
Справжній експерт, мабуть, міг би зробити його ще коротшим.
Я скомпілював ці варіанти на MicroVAX за допомогою gcc. Без оптимізації. Кожен варіант виконувався 20 000 разів на рядку завдовжки 120 символів.
Перший варіант: 12,3 секунд.
Другий варіант: 12,3 секунд.
Третій варіант: 12,9 секунд.
Вказівники не завжди перемагають.
Напишіть код, який ви розумієте. Читання важливіше кількох мікросекунд. Якщо ви не знаходитесь в тісному циклі (tight loop). Тоді можливо варто оптимізувати.
strcpy проходить аналогічну еволюцію.
Почнемо з очевидного:
`
Зверніть увагу на <=. Він копіює `\0'. Якщо пропустити його, рядок не матиме кінця. Невідома довжина. Баги пізніше. Їх важко знайти.
Ця версія є неефективною. strlen викликається на кожній ітерації. Викличте її один раз.
`
Тепер покажчики.
`
Стисніть його.
`
Чисто. Швидко. Небезпечно, якщо не перевіряти кордонів.
Ось як працюють рядки в C. Жодної магії. Просто пам'ять та вказівники. І багато акуратного підрахунку.
Вказівники проти продуктивності в strcpy
Технічно можна написати while (*s1++ = *s2++); для обробки копіювання рядків. Розрив у продуктивності між наївними реалізаціями та оптимізованими приголомшує.
Візьміть strcpy. Перша версія займає 415 секунд для копіювання рядка зі 120 символів 10 000 разів. Друга версія? 14,5 секунд. Третя знижується до 9,8 секунд. Четверта стабілізується на позначці 10,3 секунд.
Це незначна різниця. Це величезний приріст швидкості. Покажчики забезпечують цю швидкість, оскільки вони усувають непотрібні витрати.
Типи значень, що повертаються, і покажчики на рядки
Прототип strcpy у бібліотеці рядків розкриває його призначення:
`
Більшість функцій роботи з рядками повертають покажчик на рядок. strcpy повертає значення s1 як результат. Це дозволяє виконувати ланцюжок операцій або негайно використовувати скопійований рядок без пошуку.
Видалення провідних пропусків без переміщення даних
Використання покажчиків рядків часто призводить до певного поліпшення швидкості. Ви можете скористатися цим, якщо трохи подумаєте.
Припустимо, ви хочете видалити провідні прогалини з рядка. Інстинкт нагадує зрушувати символи, перезаписуючи прогалини. У C можна повністю уникнути переміщення.
`
Це набагато швидше, ніж техніка зсуву. Особливо для довгих рядків. Ви не зрушуєте байти. Ви просто змінюєте початковий покажчик. Дані залишаються дома. Висновок змінюється.
Освоєння прийомів
Ви освоїте багато інших прийомів з рядками в міру просування. Ви читатимете інший код. Ви побачите, як інші працюють із пам'яттю.
Практика – ключ до успіху. Немає короткого шляху читання коду. Вам просто потрібно дивитися, як це робиться і розуміти, чому це працює. Приріст продуктивності реальний. Крива навчання крута. Але результати кажуть самі за себе.













































