Найдите и объясните ошибку в этом фрагменте C-кода, которая может привести к неопределённому поведению: char *s = "hello"; s[0] = 'H'; — какие безопасные альтернативы возможны и как это отличается в C++ и Rust
Ошибка: строковый литерал нельзя изменять — выражение char *s = "hello"; s[0] = 'H'; вызывает неопределённое поведение (UB). Литералы строк обычно размещаются в памяти только для чтения, поэтому запись через s[0]s[0]s[0] недопустима; компилятор/система могут поместить литерал в read-only сегмент или оптимизировать код, исходя из предположения, что литерал не меняется. Безопасные альтернативы в C: - Массив на стеке (литерал копируется в модифицируемую область): char s[] = "hello"; // теперь s[0]s[0]s[0] можно менять - Динамическое выделение/копирование: char *s = malloc(strlen("hello") + 1); if (s) { strcpy(s, "hello"); s[0] = 'H'; /* ... */ free(s); } (размер буфера — strlen("hello")+1\text{strlen}("hello")+1strlen("hello")+1, т.е. учтён завершающий '\0') - Если изменение не требуется — явно делать указатель на константу: const char *s = "hello"; // запрещает запись через s Отличие в C++: - В C++ литерал имеет тип, эквивалентный массиву const char, поэтому присваивание в char* уже запрещено (должно быть const char*). Правильный изменяемый вариант — использовать std::string: std::string s = "hello"; s[0] = 'H'; // безопасно Отличие в Rust: - Литерал имеет тип &'static str и неизменяем: let s: &str = "hello"; // нельзя менять - Модифицируемая строка — тип String: let mut s = String::from("hello"); s.replace_range(0..1, "H"); // для ASCII безопасно // или через байты: let mut v = s.into_bytes(); v[0] = b'H'; let s = String::from_utf8(v).unwrap(); Кратко: нельзя менять строковые литералы — используйте модифицируемый массив/динамический буфер в C, std::string в C++, String в Rust.
char *s = "hello";
s[0] = 'H';
вызывает неопределённое поведение (UB). Литералы строк обычно размещаются в памяти только для чтения, поэтому запись через s[0]s[0]s[0] недопустима; компилятор/система могут поместить литерал в read-only сегмент или оптимизировать код, исходя из предположения, что литерал не меняется.
Безопасные альтернативы в C:
- Массив на стеке (литерал копируется в модифицируемую область):
char s[] = "hello"; // теперь s[0]s[0]s[0] можно менять
- Динамическое выделение/копирование:
char *s = malloc(strlen("hello") + 1);
if (s) { strcpy(s, "hello"); s[0] = 'H'; /* ... */ free(s); }
(размер буфера — strlen("hello")+1\text{strlen}("hello")+1strlen("hello")+1, т.е. учтён завершающий '\0')
- Если изменение не требуется — явно делать указатель на константу:
const char *s = "hello"; // запрещает запись через s
Отличие в C++:
- В C++ литерал имеет тип, эквивалентный массиву const char, поэтому присваивание в char* уже запрещено (должно быть const char*). Правильный изменяемый вариант — использовать std::string:
std::string s = "hello";
s[0] = 'H'; // безопасно
Отличие в Rust:
- Литерал имеет тип &'static str и неизменяем:
let s: &str = "hello"; // нельзя менять
- Модифицируемая строка — тип String:
let mut s = String::from("hello");
s.replace_range(0..1, "H"); // для ASCII безопасно
// или через байты: let mut v = s.into_bytes(); v[0] = b'H'; let s = String::from_utf8(v).unwrap();
Кратко: нельзя менять строковые литералы — используйте модифицируемый массив/динамический буфер в C, std::string в C++, String в Rust.