Вы забыли, что строки C заканчиваются нулем. sizeof "AB"
равно 3, а sizeof "XYZ"
равно 4 из-за неявного завершающего байта.(Тип строкового литерала "AB"
равен char[3]
, а тип "XYZ"
равен char[4]
.)
Если бы вы не указали никакой длины для buf1
,он также будет иметь размер 3 байта:
char buf1[] = "AB"; // here exactly the same as char buf1[3] = "AB";
Структура памяти будет
buf1
v
+-------+-------+-------+
| [0] | [1] | [2] |
+-------+-------+-------+
| 'A' | 'B' | '\0' |
+-------+-------+-------+
Теперь strcpy
копирует завершающий нулевой символ (C11 7.24.2.3p2 ):
Функция strcpy
копирует строку, на которую указывает s2
(, включая завершающий нулевой символ ), в массив, на который указывает s1
.Если копирование происходит между объектами, которые перекрываются, поведение не определено.
, что означает, что всего скопировано 4 байта, но есть место только для 3 символов, поэтому код имеет неопределенное поведение , и компилятор выдает диагностические сообщения, C11 7.1.4 Использование библиотечных функций стр.2 :
[...] Если аргумент функции описывается как массив, указатель фактически передается функциидолжно иметь такое значение, чтобы все вычисления адресов и доступ к объектам (которые были бы действительными, если бы указатель действительно указывал на первый элемент такого массива) были действительно действительными. [...]
В реальном коде неявный доступ к buf1[3]
фактически недействителен .
Структура памяти после strcpy
:
buf1
v
+-------+-------+-------+-------+
| [0] | [1] | [2] | ??? |
+-------+-------+-------+-------+
| 'X' | 'Y' | 'Z' | '\0' |
+-------+-------+-------+-------+
Причина, по которойпредупреждение __builtin_memcpy
вызвано тем, что компилятор C сильно оптимизировал этот код - он заменил strcpy
строки известной длины на memcpy
известной длины, так как memcpy
будет генерировать более эффективный код.
И, наконец, вы можете вставить 3 символа в char buf1[3];
с помощью strncpy
, но буфер не может соответствовать завершающему нулевому символу, и, следовательно, он не может быть напечатан с использованием printf("%s")
, но вы можете распечатать его с указаниемявная ширина поля, которая меньше или равна длине массива - однако значение на выходе будет дополнено:
#include <stdio.h>
#include <string.h>
int main() {
char buf1[3] = "AB";
printf("buf1 val: >%-3s<\n", buf1);
printf("buf1 addr: %p\n", &buf1);
strncpy(buf1, "XYZ", 3);
printf("buf1 val: >%-3s<\n", buf1);
}
И скомпилируем, запустив его:
% gcc strncpy.c -Wall -Wextra
% ./a.out
buf1 val: >AB <
buf1 addr: 0x7ffd7f6aecc5
buf1 val: >XYZ<
, нопосле AB
напечатан один дополнительный пробел