Почему * (str + i) = * (str + j) здесь не работает? - PullRequest
4 голосов
/ 13 октября 2009

void reverse(char *str){
    int i,j;
    char temp;
    for(i=0,j=strlen(str)-1; i<j; i++, j--){
     temp = *(str + i);
     *(str + i) = *(str + j);
     *(str + j) = temp;
     printf("%c",*(str + j));
    }
}

</code>
<code>
int main (int argc, char const *argv[])
{
    char *str = "Shiv";
    reverse(str);
    printf("%s",str);
    return 0;
}

Когда я использую char * str = "Shiv", строки в замещающей части моей обратной функции, т.е. str [i] = str [j], похоже, не работают, однако, если я объявляю str как char str [] = " Шив ", обменная часть работает? Что является причиной этого. Я был немного озадачен поведением, я продолжал получать сообщение «Ошибка шины», когда пытался запустить программу.

Ответы [ 8 ]

16 голосов
/ 13 октября 2009

Когда вы используете char *str = "Shiv";, вам не принадлежит указанная память, и вам не разрешено писать в нее. Фактические байты для строки могут быть константой в коде программы.

Когда вы используете char str[] = "Shiv";, 4 (+1) байта символов и сам массив находятся в вашем стеке, и вы можете записывать в них столько, сколько пожелаете.

4 голосов
/ 13 октября 2009
char *str = "Shiv";

Это должно быть:

const char *str = "Shiv";

А теперь у вас будет ошибка;)

4 голосов
/ 13 октября 2009

Char * str = "Shiv" получает указатель на строковую константу, которая может быть загружена в защищенную область памяти (например, часть исполняемого кода), которая доступна только для чтения.

1 голос
/ 13 октября 2009

Строковые литералы (ваш "Shiv") не могут быть изменены.
Вы назначаете указателю адрес такого строкового литерала, затем пытаетесь изменить содержимое строкового литерала путем разыменования значения указателя. Это большое НЕТ-НЕТ.

Вместо этого объявите str как массив:

char str[] = "Shiv";

Это создает str как массив из 5 символов и копирует символы 'S', 'h', 'i', 'v' и '\ 0' в str [0], str [ 1], ..., стр. [4]. Значения в каждом элементе str могут быть изменены.

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

const char *str = "Shiv";

Представьте, что вы можете сделать то же самое с целыми числами.

/* Just having fun, this is not C! */
int *ptr = &5;                      /* address of 5   */
*ptr = 42;                          /* change 5 to 42 */
printf("5 + 1 is %d\n", *(&5) + 1); /* 6? or 43?  :)  */

Цитата из стандарта:

6.4.5 Строковые литералы
...
6 ... Если программа пытается изменить такой массив [строковый литерал], поведение не определено.

1 голос
/ 13 октября 2009

Строковые литералы являются неизменяемыми объектами в C и C ++. Попытка изменить строковый литерал всегда приводит к неопределенному поведению. Это именно то, что вы наблюдаете, когда получаете «Ошибка шины» с

char *str = "Shiv";

вариант. В этом случае ваша «обратная» функция попытается изменить строковый литерал. Таким образом, поведение не определено.

The

char str[] = "Shiv";
Вариант

создаст копию строкового литерала в модифицируемом массиве 'str', а затем 'экземпляр' будет работать с этой копией Это будет хорошо работать.

P.S. Не создавайте неконстантные указатели на строковые литералы. Ваш первый вариант должен был быть

const char *str = "Shiv";

(обратите внимание на дополнительное «const»).

1 голос
/ 13 октября 2009

Попробуйте

int main (int argc, char const *argv[])
{
    char *str = malloc(5*sizeof(char)); //4 chars + '\0'
    strcpy(str,"Shiv");
    reverse(str);
    printf("%s",str);
    free(str); //Not needed for such a small example, but to illustrate
    return 0;
}

вместо этого. Это позволит вам читать / записывать память при использовании указателей. Использование нотации [] непосредственно выделяет пространство в стеке, а использование константных указателей - нет.

0 голосов
/ 13 октября 2009

Интересно, что я никогда этого не замечал. Мне удалось воспроизвести это условие в VS2008 C ++.

Как правило, изменение констант на месте - плохая идея.

В любом случае этот пост объясняет эту ситуацию довольно четко.

Первый (char []) - это локальные данные, которые вы можете редактировать. (так как массив это локальные данные).

Второй (char *) является локальным указателем на глобальные, статические (постоянные) данные. Вы не разрешается изменять константу данные.

Если у вас есть GNU C, вы можете скомпилировать с -fwritable-строки, чтобы сохранить глобальная строка из сделанного постоянный, но это не рекомендуется.

0 голосов
/ 13 октября 2009

char * str - указатель / ссылка на блок символов (строку). Но он находится где-то в блоке памяти, поэтому вы не можете просто так его назначить.

...