Почему не следует возвращать строки/массивы напрямую
Всем привет.
Раньше, когда люди спрашивали, чем плохо то, что функция возвращает строку, основным (и самым очевидным) доводом было лишнее копирование данных: т.е. сначала строка/массив копируется в стек при возврате значения из функции, затем ещё раз копируется из стека в другой массив, к которому идёт присваивание значения. Но, внезапно, для некоторых ленивых скриптеров это не аргумент...
Не так давно получилось найти новый изъян, который (может быть) переубедит ещё несколько скриптеров: баг с возвратом массивов, который может привести к неправильной работе сервера.
Допустим, у нас есть следующие две функции:
new global_string[12] = {"Hello world"};
StringOrigin() {
return global_string;
}
ReturnString() {
return StringOrigin();
}
Довольно просто, так ведь? Есть глобальная строка, и есть функция, которая возвращает эту строку. И ещё есть другая функция, которая возвращает результат первой функции.
new local[12];
strcat(local
, StringOrigin
(), sizeof(local
));
Такой код работает вполне нормально. Нативная функция strcat() вызывает Pawn-функцию StringOrigin(), которая просто возвращает глобальную строку. Это первый "уровень" вложенности возврата массивов (strcat() -> StringOrigin()).
new local[12];
strcat(local
, ReturnString
(), sizeof(local
));
А вот такой код уже не работает. strcat() вызывает функцию ReturnString(), которая возвращает то, что ей возвращает StringOrigin(). Это второй "уровень" вложенности (strcat() -> ReturnString() -> StringOrigin()), и на нём уже проявляется ошибка.
При подключенном плагине CrashDetect выводится следующее сообщение:
Код:
[debug] Run time error 5: "Invalid memory access"
Решение проблемы очень простое: всегда возвращайте строки через массив, переданный по ссылке.
new string[12] = {"Hello world"};
StringOrigin(output[], size = sizeof output) {
return 0; // Будем считать, что 0 - код успешного выполнения этой функции.
}
ReturnString(output[], size = sizeof output) {
return StringOrigin(output, size); // Нет ничего плохого в дальнейшем возврате возвращаемого значения
// из StringOrigin(), т.к. это всего лишь одна ячейка (0), а не массив.
}
Оригинал примера с возвратом строк: https://github.com/sampctl/pawn-array-return-bug
Перевод и дополнение: Daniel_Cortez
Копирование данной статьи на других ресурсах без разрешения автора запрещено!