Как реализуется strcpy?

У меня есть вопрос об использовании strcpy. Я знаю, что стандарт ANSI C говорит: источник и место назначения не должны перекрываться, иначе поведение будет непредсказуемым. Я покажу вам фрагмент кода, который работает так, как я ожидаю, если он скомпилирован с использованием старого компилятора gnu C под Linux.

#include <string.h>
#include <stdio.h>

char S[80],*P;

int main() {
    strcpy(S,"abcdefghi\r\njklmnopqr\r\nstuvwxyz\r\n");
    for (P=S; P=strchr(P,'\r'); P++) strcpy(P,P+1);
    printf("%s\n",S);
    return 0;
}

Эта последовательность удаляет все \r (возврат каретки) из входной строки. Я знаю (от Кернигама и Ритчи), что очень простая реализация для strcpy выглядит следующим образом:

while (*t++=*s++) ;

Теперь я скомпилировал свою программу, используя gcc (Gentoo 4.5.4 p1.0, pie-0.4.7) 4.5.4, и она печатает это:

abcdefghi
jklmnpqr          <-- missing 'o'
stuvwxxyz         <-- doubled 'x'

Я предполагаю, что этот компилятор (фактически его библиотека) использует очень сложную последовательность для strcpy, и я не понимаю причину.


person Nelu Cozac    schedule 17.10.2012    source источник
comment
Хех, @jsalonen опередил меня в редактировании   -  person Earlz    schedule 17.10.2012
comment
вы можете увидеть реализацию, найдя файл .asm в своей системе.   -  person elyashiv    schedule 17.10.2012
comment
Вероятно, он использует оптимизации, которые копируют большие (многобайтовые) фрагменты. Обычный метод заключается в том, чтобы привести указатели к самой длинной доступной целочисленной единице (например, long long *) и скопировать ее. Это означает, что копия перезаписывает то, что копируется.   -  person Some programmer dude    schedule 17.10.2012
comment
Странный результат: abcdefghi, затем jklmnpqr, затем stuvwxxyz. Во второй строке отсутствует o, а в третьей строке x удваивается.   -  person Nelu Cozac    schedule 17.10.2012
comment
Я просмотрел S с помощью gdb (отладчик gnu): каждый '\ r' отбрасывается, а S - abcdefghi\njklmnpqr\nstuvwxxyz\n   -  person Nelu Cozac    schedule 17.10.2012
comment
Я должен отметить, что вы на самом деле не задаете вопрос здесь.   -  person Jonathan Grynspan    schedule 17.10.2012
comment
Valgrind предупреждает об этой ошибке. См. stackoverflow.com/questions/ 4823664/   -  person nh2    schedule 09.12.2012


Ответы (2)


Вас предупредили, чтобы вы этого не делали. Причина в том, что побайтовое копирование на самом деле довольно медленное и требует большого количества циклов для обработки строки. Компилятор может легко оптимизировать это (например, копируя фрагмент размером int за раз или используя распараллеливание для конкретной платформы).

Но если строки перекрываются, то эти оптимизации делают предположения о ваших данных, которые больше не действительны. В результате они дают вам неопределенные результаты. Скорее всего, ваш старый GCC просто не делал таких оптимизаций.

Поскольку в документации для strcpy() не рекомендуется использовать перекрывающиеся строки, не делайте этого.

person Jonathan Grynspan    schedule 17.10.2012

Лучший способ выяснить, что делает ваша реализация, это, конечно, прочитать исходный код ее библиотеки.

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

Вы также можете посмотреть на «серьезные» реализации библиотеки с открытым исходным кодом и, возможно, сделать из этого какие-то выводы.

Одна из идей может заключаться в том, что библиотека копирует данные большими порциями, чем по одному символу за раз, что прерывается, когда вы нарушаете допущения проекта.

person unwind    schedule 17.10.2012