2013-08-20 4 views
0

В первой главе, К & R вводит функцию копирования следующим образом:K & R пример кода путаница: копия строки

void copy(char to[], char from[]) { 
/* copy from from[] to to[], assumes sufficient space */ 
    int i = 0; 
    while ((to[i] = from[i]) != '\0') { 
     i++; 
    } 
} 

Лужение вокруг немного с этой функцией, у меня были некоторые неожиданные результаты. Пример программы:

int main() { 
    char a[3] = {'h', 'a', '\n'}; 
    char b[3]; 
    printf("a: %s", a); // prints ha 
    copy(b, a); 
    printf("a: %s", a); // prints nothing 
    printf("b: %s", b); // prints ha 

    return 0; 
} 

Теперь мои вопросы:

  1. Почему копирование с a для b работы, поэтому делает петлю в то время как в копии когда-либо прекращается, даже если a не содержит a '\ 0'?

  2. Почему a мутировал?

+0

Это неопределенное поведение. Это означает, что все возможно, абсолютно все. – nouney

+3

Почему расхождение между 'int' и' char'? Если ваш компилятор не принимает вас за предупреждения, вы неправильно настроили его. – Medinoc

+5

Я предполагаю, что вы изменили пример K & R вместо 'char' вместо этого? В противном случае все ставки будут отключены, так как ваш код будет копировать куски размером 4 байта, что приведет к seg.faults и неопределенному поведению. – Lundin

ответ

2

Возможно, вы столкнулись с переполнением буфера.

В a не прекращается надлежащим образом (отсутствует \0), copy копии от a до b тех пор, пока он не находит \0. Следовательно, больше байтов написано до b, так как оно может содержать то, что затем переполняется в a (зависит от платформы, неопределенное поведение).

Часть, которая переполняется \0 после a, что делает a строкой нулевой длины.

Ваш стек, вероятно, выглядит следующим образом:

     b  a   
[ arbitrary memory ][ 0 0 0 ][ h a \n ][ 0 ? ? ? ? ] 

? указывает неизвестные данные, мы не знаем, что лежит там, и нигде не указано. Но мы знаем, что там должен быть a 0, иначе иначе printf будет печатать гораздо больше мусора.

copy копии до тех пор, пока не будет найден нуль после начало a. Существует, случайно, 0 после окончания a, который затем копируется в b. Как b уже заполнен содержимого из a, b перетекает в a, что делает ваш стек выглядеть следующим образом:

     b   a 
[ arbitrary memory ][ h a \n ][ 0 a \n ][ 0 ? ? ? ? ] 

Как есть \0 в начале a, printf предполагает a быть пустым.

1

Даже если вы не явно положил '\0' в переменной a, то весьма вероятно, что a[3](что технически вне границ) случается быть равен нулю.

Много места обычно заполнено нулями/\0 значениями, хотя есть абсолютно без гарантии.

Это объясняет, почему вы успешно скопировали строку до b.

Ваша функция копирования неправильно в том смысле, что «думает» его копирование массивов ints:

void copy(int to[], int from[]) { 

Когда следует копировать массивы char актеры:

void copy(char to[], int char[]) { 
+2

Написание 'int' over 'char' также будет иметь эффект ... (т. – trojanfoe

+0

А, я пропустил int-part. Спасибо. – abelenky

1

Это работает, поскольку ваш цикл while не выполняет логическую операцию, а выполняет свою привязку. Итак, это так же долго проходит через цикл, поскольку значение asigned равно true. (И это так долго не равно нулю). Таким образом, он заканчивается при достижении '\ 0' (whats 0). , и это будет возможно, когда возможно прекратится (с неопределенным поведением), даже если нет «\ 0», потому что после выхода из ваших границ вероятность появления любого байт с нулевым значением довольно велика. но после выхода из границ массива, поведение вашей программы может быть чем угодно. (это может даже позволить появлению носовых драконов;)) Причина, по которой b ничего не печатает, может быть заземлена в порядке байтов, так как вы вставляете значения int в массив байтов, поэтому может быть, что первый байт Byteorder помещается в первый байт и равен 0, поэтому извне он будет обрабатываться как «\ 0», даже если ваша цель состояла в том, чтобы обрабатывать весь int как знак, а не как 4 одиночных значения. p.s .: Это даже нарушит правила строгого сглаживания.

2

Эта функция copy полагается на завершающий нулевой байт, чтобы определить, когда он должен прекратить копирование. Когда вы используете строковую константу, она автоматически заканчивается на нуль. Однако нормальные массивы не заканчиваются на нуль, поэтому функция продолжает получать доступ к памяти за концом a, пока один из байтов не окажется \0. Когда функция copy прекращает копирование, это зависит от содержимого этой области памяти.Вы не знаете, что, если что-нибудь случится, так что вы не знаете, как долго copy будет продолжать копировать или что произойдет.

1

Лучше изучить вопрос о к или из находится перед циклом а ниже NULL.

while ((to[i] = from[i]) != '\0'){ i++; }

на вопросы:

1. цикл завершается, потому что что-то хранится в памяти после равна 0 (или «\ 0»), которое не определено.

2. может измениться или нет после вызова копию, которая не определена, а также. Обе переменные, хранящиеся после a, и позиция b сохранены при аффекте. В случае nemo, упомянутом выше, a мутированный после функции копия выполнена.

Всегда вставляйте '\ 0' в конец массива символов для обеспечения безопасности.

Смежные вопросы