В конечном счете, было бы чище поменять его на место, например:
#include <stdio.h>
#include <string.h>
void
reverse(char *s)
{
int a, b, c;
for (b = 0, c = strlen(s) - 1; b < c; b++, c--) {
a = s[b];
s[b] = s[c];
s[c] = a;
}
return;
}
int main(void)
{
char string[] = "hello";
printf("%s\n", string);
reverse(string);
printf("%s\n", string);
return 0;
}
Ваше решение по сути является семантически большей версией этого. Понять разницу между указателем и массивом. Стандарт прямо заявляет, что поведение такой операции (модификация содержимого строкового литерала) не определено. Вы также должны увидеть этот отрывок из эскимоса:
Когда вы инициализируете массив символов строковой константой:
char string[] = "Hello, world!";
в итоге вы получите массив, содержащий строку, и вы можете изменить содержимое массива так, как вам хочется:
string[0] = 'J';
Однако можно использовать строковые константы (формальный термин - строковые литералы) в других местах вашего кода. Поскольку они являются массивами, компилятор генерирует указатели на свои первые элементы, когда они используются в выражениях, как обычно. То есть, если вы скажете
char *p1 = "Hello";
int len = strlen("world");
это почти как если бы вы сказали
char internal_string_1[] = "Hello";
char internal_string_2[] = "world";
char *p1 = &internal_string_1[0];
int len = strlen(&internal_string_2[0]);
Здесь предполагается, что массивы с именами internal_string_1 и internal_string_2 предполагают тот факт, что компилятор фактически генерирует небольшие временные массивы каждый раз, когда вы используете строковую константу в своем коде. Однако тонкий факт заключается в том, что массивы, которые находятся "позади" строковых констант, не обязательно модифицируемы. В частности, компилятор может хранить их в постоянной памяти. Поэтому, если вы напишите
char *p3 = "Hello, world!";
p3[0] = 'J';
Ваша программа может аварийно завершить работу, потому что она может попытаться сохранить значение (в данном случае символ «J») в недоступной для записи памяти.
Мораль такова, что всякий раз, когда вы строите или модифицируете строки, вы должны убедиться, что память, в которую вы строите или модифицируете их, доступна для записи. Эта память должна быть либо массивом, который вы выделили, либо памятью, которую вы динамически распределили с помощью техник, которые мы увидим в следующей главе. Убедитесь, что ни одна часть вашей программы никогда не будет пытаться изменить строку, которая на самом деле является одним из неназванных, недоступных для записи массивов, которые компилятор сгенерировал для вас в ответ на одну из ваших строковых констант. (Единственное исключение - инициализация массива, потому что если вы пишете в такой массив, вы пишете в массив, а не в строковый литерал, который вы использовали для инициализации массива.) "