2012-02-25 3 views
4

Я прохожу через K & R, и у меня возникают трудности с увеличением указателей. Упражнение 5.3 (стр. 107) просит написать функцию strcat с помощью указателей.C указатели: разница между while (* s ++) {;} и while (* s) {s ++;}

В псевдокоде, функция выполняет следующие действия:

  1. принимает 2 строки в качестве входов.
  2. Находит конец строки один.
  3. Копии строки два на конце строки один.

я получил рабочий ответ:

void strcats(char *s, char *t) 
{ 
    while (*s)   /* finds end of s*/ 
     s++; 
    while ((*s++ = *t++)) /* copies t to end of s*/ 
     ; 
} 

Но я не понимаю, почему этот код не работает также:

void strcats(char *s, char *t) 
{ 
    while (*s++) 
     ; 
    while ((*s++ = *t++)) 
     ; 
} 

Очевидно, что я что-то упускаю о том, как увеличивается инкремент указателя. Я думал, что две формы приращения s эквивалентны. Но второй код выводит только строку s.

Я попробовал фиктивную переменную i, чтобы проверить, проходила ли функция через оба цикла. Так оно и было. Я прочитал разделы 5.4 и 5.5 из K & R, но я не мог найти ничего, что проливает свет на это.

Может кто-нибудь помочь мне выяснить, почему вторая версия моей функции не делает то, что я бы хотел? Благодаря!

Редактировать: Спасибо всем. Невероятно, как долго вы можете смотреть на относительно простую ошибку, не замечая ее. Иногда нет лучшего средства, кроме того, что кто-то еще заглядывает в него.

+1

Хотя K & R широко рассматривается как C-Библия, это нехорошее место, чтобы научиться писать хороший код. Такие вещи, как 'while (* s ++ = * t ++),' должны быть отправлены в мусорную корзину истории. –

ответ

3

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

+0

Упс ... конечно. Я увеличил свою исходную строку за терминалом «\ 0». Таким образом, второй код был конкатенацией, но ничего не печаталось, потому что printf() остановился на первом «\ 0». Я проверил, поставив s-- в ошибочной версии после цикла, и все напечатано хорошо. Спасибо. Забавно, как вы можете смотреть на проблему навсегда, не заметив недостатки. – Graeme

2

Там один меньше работы в while (*s) ++s; Когда *s равен нулю, то цикл прерывается, в то время как форма while (*s++) перерывов, но по-прежнему увеличивает s один последний раз.

Строго говоря, последняя форма может быть некорректной (т. Е. UB), если вы попытаетесь сформировать неверный указатель. Это, конечно, надуманно, но вот пример: char x = 0, * p = &x; while (*x++) { }.

Независимо от этого, лучше всего писать чистый, понятный и продуманный код, а не пытаться перехитрить себя. Иногда вы можете написать отличный код на C, который на самом деле изящный, а в других случаях лучше правильно произнести что-то. Соблюдайте свое мнение и попросите кого-нибудь за отзыв (или посмотрите на их лица, когда они смотрят на ваш код).

7

Это:

while(*s++) 
    ; 

за счет постинкремента, находит нулевой байт в конце строки, затем увеличивает его еще раз перед выходом из цикла. t копируется после затем NUL:

scontents␀tcontents␀ 

Печать s остановится на первом NUL.

Это:

while(*s) 
    s++; 

перерывы цикла, когда 0 найден, так что вы оставили указывает на нулевой байт. t копируется над NUL:

scontentstcontents␀ 
2

Это:

while (*s) 
    s++; 

прекращается, как только это *s'\0', после чего он покидает s там (потому что он не выполняет тело цикла).

Это:

while (*s++) 
    ; 

останавливается, как только *s является '\0', но до сих пор выполняет postincrement ++, так s заканчивается указывая прямо после'\0'. Поэтому строка-завершающая строка '\0' никогда не перезаписывается, и она все равно завершает строку.

1

давайте предположим следующие символы в памяти:

Address   0x00 0x01 0x02 0x03 
-------   ---- ---- ---- ---- 
0x8000   'a' 'b' 'c' 0 
0x8004   ... 

При выполнении цикла, это происходит в памяти.

1. *s = 'a' 
2. s = 0x8001 
3. *s = 'b' 
4. s = 0x8002 
5. *s = 'c' 
6. s = 0x8003 
7. *s = 0; 
8. s = 0x8004 
9. end loop 

При оценке, * s ++ продвигает указатель, даже если значение * с 0.

// ход S вперед, пока она не указывает один мимо символа

while (*s++); 

Это не работает, потому что s заканчивается тем, что указывает на другое место.

Как он суммирует, мы получаем значение мусора в качестве последнего символа в нашей целевой строке. Эта строка мусора из-за того, что цикл превышает предел '\ 0' на один шаг вперед.

Вы можете устранить его, используя код, приведенный ниже, я думаю, что это является эффективным

while (*s) 
    s++; 

Это выполнение, как показано ниже в перспективе памяти.

1. *s = 'a' 
2. s = 0x8001 
3. *s = 'b' 
4. s = 0x8002 
5. *s = 'c' 
6. s = 0x8003 
7. *s = 0 
8. end loop 
Смежные вопросы