На втомлену голову переплутав розмір масиву з його крайнім елементом і написав:
#include <stdio.h>
#include <string.h>
int main() {
char src[3] = "abc";
char dest[3] = "zxc";
strcpy(dest, src);
printf("dest: %s\n", dest);
return 0;
}
І отримав:
dest: abc�Nr�
Тому що функція strcpy набирає з початкового масиву доти, доки не зустріне нуль \0.
І потім abc разом зі сміттям, що після нього, успішно копіюється в dest.
Ще не розібравшись, що відбувається, я зрозумів, що можна полагодити це неподобство, “обрізавши” dest додаванням туди нуля:
char src[3] = "abc";
char dest[3] = "zxc";
strcpy(dest, src);
dest[sizeof(dest)] = '\0'; // <--
printf("dest: %s\n", dest);
І справді помилку не видно:
dest: abc
Але під капотом я цим додаванням заліз на чужу, а точніше, незвідану територію в пам’яті, і перетер своїм нулем чиюсь комірку.
Адже я виділив 3 байти на масив, а не 4.
І потім, не спитавшись у компілятора, поліз на четвертий байт, який не був зарезервований за мною.
“Куди по копаному?!”
Отже, під час ініціалізації:
char src[3] = "abc";
відбувається укладання угоди, що мені потрібен масив розміром в 3 байти, і компілятор виділяє для цього мені пам’ять.
Ніхто не виділяє мені пам’ять, коли я вручну щось почну писати за межами цих трьох байт.
І якщо я допишу нуль в елемент поза масивом, це буде порушенням угоди.
Отже, як правильно?
Правильно буде у розмірах завжди закладатися на нуль як невід’ємну частину рядка:
char src[4] = "abc";
Бачиш 3 елементи, але закладаєшся на 4.
І не після, а під час укладання угоди.
І не пишіть код на втомлену голову.
(особлива подяка Furdog за допомогу у розслідуванні)