2014-01-14 2 views
5

У меня есть некоторые концептуальные вопросы об обращении нулевой завершенной строки C и разъяснения вопросов о природе указателей.Свертывание строки ' 0' с завершающей строкой C?

вход может быть

char arr[] = "opal"; 

и код:

void reverse(char *str) { /* does *str = opal or does *str = o since the pointer str is type char? */ 

    char* end = str; /* what is the difference between char* end and char *end? and is *end pointing to opal now? */ 

    char tmp; 

    if (str) { /* if str isn't null? */ 
     while (*end) 
      ++end; 
    } 
    --end; /* end pointer points to l now */ 

    while (str < end) {  /* why not *str < *end? is this asking while o < l? */ 
     tmp = *str; /* tmp = o */ 

     *str++ = *end; /* what is the difference between *str++ and ++str? does *str++ = l? */ 
     *end-- = tmp; /* *end points to o */ 
     } 
    } 
} 
+2

Если вы буквально передаете «опал», это будет неопределенное поведение, так как вам не разрешено изменять строковый литерал. –

+0

@ShafikYaghmour, что бы вы тогда прошли? – Opal

+0

Массив символов, 'char arr [] =" opal ";'. –

ответ

8

Много вопросов ... пытаются захватить ответы на каждый:

/* делает * ул = опал или делает * ул = о, так как ул указатель символьного типа? */

*str является 'o', поскольку он указывает на первый символ

/* В чем разница между полукокса * конец и полукокса * конец? и теперь кончик указывает на опал?*/

Там нет никакой разницы между char *end и char* end. Это становится сложнее, когда вы пишете

char* a, b; 

, поскольку это эквивалентно

char *a, b; 

и нет, как вы могли бы подумать

char *a, *b; 

Вот почему чище писать char *end;.

И end указывает на opal сейчас - *end is 'o'.

если (str) {/ * если str не является нулевой? */

Да - тестирование, что вы не получили передается указатель NULL

Чтобы проверить, что вы не получили передается строка длины 0, вы должны проверить *str (после тестирования, что str не NULL, в противном случае вы получите ошибку сегментации для "осмеливаясь смотреть на * NULL")

время (ул < конец) {/ * почему не * ул < * конец? это вопрос, когда o < l? */

Тестирование указателей - один движется к концу, а другой движется назад. Когда вы встречаетесь посередине, вы останавливаетесь; в противном случае вы своп в два раза, и не будет никакого чистого эффекта ...

*str++ = *end; /* what is the difference between *str++ and ++str? does *str++ = l? */ 

Сначала скопируйте значение *end в *str, то увеличиваем str указатель. Если вы положили ++str, вы сначала увеличиваете его, а затем используете. И это будет означать, что вы положили l вместо p вместо вместо o.

редактировать один критический анализ на вашем коде (выходит за рамки вопросов, которые вы просили, и в ответ на комментарий от @chux): когда вы проверяете для if(str){} вам действительно нужно else return; заявление, так как вы на самом деле end--;, а затем использовать *end. Удостоверьтесь, что 0xFFFFFFFFFFFFFFFF почти всегда является недопустимым адресом ...

Если вы на самом деле тестировали if(*str!='\0'), тогда вы все равно должны просто вернуться (пустая строка является «необратимой» - или, скорее, ей ничего не нужно считаются обратными).

Кстати, я предпочитаю сделать условие явным (например, я сделал именно тогда); он не только показывает ваше намерение более четко, но компилятор может на самом деле жаловаться, если вы сделали if(str!='\0') или if(*str != NULL), поскольку типы, которые вы сравниваете, несовместимы. Это означает, что у вас будет более надежный, более читаемый код и, скорее всего, сделайте то, что вы намеревались.

+1

Если бы я мог, я бы добавил +1 к комментариям о явном тестировании в последнем абзаце. –

+0

@JonathanLeffler - очень вероятно, что я узнал о важности этого подхода некоторое время в прошлом году, прочитав один из ваших ответов/комментариев. Да, я в основном здесь, чтобы учиться, хотя я делаю это, пытаясь ответить на вопросы. Спасибо за комплимент! – Floris

2

делает * ул = опал или делает * ул = о, так как ул указатель символьного типа?

str является указателем на первый элемент opal, т.е., это указатель на первую букву o. Итак, *s означает, что вы разыскиваете s (первый адрес), следовательно, это 'o'.

какая разница между char * end и char * конец? и теперь кончик указывает на опал?

Нет никакой разницы. Нет. Это end, что указывает на opal, т. Е. Теперь его первый элемент.

если str не является нулевой?

Да.

конец указатель указывает на л Теперь

Да. Теперь он указывает на последнюю букву слова.

В чем разница между * str ++ и ++ str? does * str ++ = l?

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

+0

Woooooo! Почему голос? – haccks

+0

Я не голосовал, кто-то еще должен иметь. Я только что редактировал вопрос, чтобы передать 'char arr [] =" opal ";' – Opal

+0

@Opal; Отредактировано сейчас. – haccks

2

. * Str = opal или does * str = o, поскольку указатель str является символом типа?

*str разыменовывает указатель, который имеет тип char*, поэтому тип вы получаете char. То, что char будет значением, на которое указывает str, который будет 'o'.

какая разница между char * end и char * конец?

Ничего.

и есть * конец, указывающий на опал сейчас?

Да, хорошо, близко. end указывает на тот же адддресс, что и str, что является началом вашей строки символов. *end - это тип символа, а не указатель. Его значение будет 'o'.

если str не является нулевой?

Правильно, выполнение булевского теста на указателе является стандартным тестом. Он оценивает значение «истина», если указатель не равен null и «false» в противном случае. Обратите внимание, что это не то же самое, что и нулевое значение. Стандарт C позволяет любому значению представлять нулевой адрес.

почему нет * str < * конец? это вопрос, когда o < l?

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

В чем разница между * str ++ и ++ str? does * str ++ = l?

str++ наносят первый, который увеличивает str и возвращает его предыдущее значение, то * унарный оператор derferences, чтобы дать символ в этой старой позиции. Да, в первый раз, 'l' от end будет назначен началу строки (где str используется для указания до того, как он был увеличен). tmp используется для переноса старого символа и назначает его end. Это стандартная операция «своп».

1

делает *str = opal или делает *str = 'o', так как указатель str имеет тип char?

Это как. Я знаю, что это сбивает с толку, но вот что происходит: в C указатель может быть интерпретирован двумя способами - либо в буквальном смысле, то есть как указатель на один элемент (в данном случае), либо как указатель на начало последовательность элементов того же типа. В более позднем случае должен быть определен конец последовательности. Существует три способа узнать, где заканчивается последовательность - зная длину в явном виде, зная конечный указатель или помещая в конце последовательности элемент терминатора. В случае строк C специальный символ '\0', называемый «нулевым терминатором», используется для завершения последовательности.

В чем разница между char* end и char *end? и составляет *end, указывая на опал сейчас?

Нет абсолютно никакой разницы - они оба указывают на одно и то же место. Размещение звездочки не имеет значения.

если str не null?

Правильная

end указатель указывает на 'l' теперь

Абсолютно!

почему нет *str < *end? это вопрос while o < l?

Вы сравниваете местоположения по памяти, а не символы, на которые указывают. Это требует, чтобы указатели не «перекрещивались» друг с другом, потому что они идут к центру с обоих концов строки.

tmp = o

На первой итерации это. На второй итерации, он указывает на 'p'

чем разница между *str++ и ++str? *str++ = 'l'?

Выражение ++str увеличивает значение указателя, и оценивает к значению указателя после приращения; выражение *str++ увеличивает значение указателя и оценивает значение знака , на который указывал указатель до приращение.

2

Предполагая, что reverse называется так:

char str[] = "opal"; 
reverse(str); 

И назначается памяти, в качестве примера, как это:

 
Memory Address: 100 101 102 103 104 
Memory Value: ['o'] ['p'] ['a'] ['l'] ['\0'] 

Ниже, в отношении адресов, истинны:

  1. str + 0 == &str[0] == 100
  2. str + 1 == &str[1] == 101
  3. str + 2 == &str[2] == 102
  4. str + 3 == &str[3] == 103
  5. str + 4 == &str[4] == 104

И в дальнейшем, в отношении ценностей, являются истинными:

  1. *(str + 0) == str[0] == 'o'
  2. *(str + 1) == str[1] == 'p'
  3. *(str + 2) == str[2] == 'a'
  4. *(str + 3) == str[3] == 'l'
  5. *(str + 4) == str[4] == '\0'

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

char *str = NULL; 
reverse(str); 

Когда следующее выражение вычисляется:

if(str) 

Оценка будет FALSE. Внутри reverse он должен (вы должны исправить) сразу return, потому что использование указателя с адресом NULL приведет к неопределенному поведению, например, к ошибке сегментации.

Присвоение адреса str к end, когда его адрес NULL, а затем декремент его:

--end; 

будет приводить к неопределенному поведению.

+1

Это не касается всех вопросов в вопросе. –

+0

@JonathanLeffler Вы правы, но если вы это понимаете, ответы на другие вопросы станут очевидными. –

+0

@Awwww. Нет. Но согласился с Джонатаном Леффлером: D – haccks

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