Доступ к готовым решениям

Переход в группу "Пользователь"

300.00
Одноразовый платёж
Быстрый переход в группу "Пользователи", без надобности написания постов и ожидания.

Покупка дает возможность:
Быть полноправным участником форума
Нормальное копирование кода
Создавать темы
Скачивать файлы
Доступ к архиву Pawno-Info

Урок const-корректность (warning 239)

Valera_Kovshikov

Изучающий
Пользователь
Регистрация
28 Сен 2013
Сообщения
752
Лучшие ответы
4
Репутация
166
Автор перевода: Daniel_Cortez
Оригинальный текст: github.com/pawn-lang/compiler/wiki/Const-Correctness​


В компиляторе, начиная с версии 3.10.9, появилось новое предупреждение, относящееся к правильному использованию const в объявлениях и реализациях функций.
PHP:
warning 239: literal array/string passed to a non-const parameter
Поскольку данное предупреждение раскрывает ранее скрытую семантику, пользователи могут быть сбиты с толку, увидев появление этого предупреждения в своём коде в первый раз.

Цель данной статьи - объяснить, что такое const-корректность, зачем она нужна и как можно писать код лучше.



Передача аргументов

Перед тем как разобраться, что означает const в объявлениях/реализациях функций, следует хорошо понимать, какими способами аргументы могут передаваться в функцию


Передача по значению

Когда вы передаёте в функцию обычные переменные, они передаются по значению:
PHP:
main() {
    new a; // Переменная 'a' создаётся здесь.
    f(a); // 'a' передаётся в функцию 'f' по значению.
}

f(b) {
    // 'b' - это копия 'a'.
    // При изменении значения в 'b' значение 'a' не изменится.
}

Передача по ссылке

Также можно добавить амперсанд (знак "&") перед названием аргумента, чтобы указать компилятору, что аргумент передаётся по ссылке:
PHP:
main() {
    new a; // Переменная 'a' создаётся здесь.
    f(a); // 'a' передаётся в функцию 'f' по ссылке.
    // Поскольку 'f' модифицирует аргумент,
    // переменная 'a' теперь равна трём.
}

f(&b) {
    // 'b' - это ссылка на 'a'.
    // Изменив значение 'b' мы изменим его и в 'a'.
    b = 3;
}


Массивы и передача по ссылке

Массивы всегда передаются по ссылке. Причина этого не охватывается данным уроком, но вы можете почитать, почему так сделано в языке C - то же самое применимо и к Pawn.

Именно поэтому в SA-MP работают такие функции, как например GetPlayerName - они принимают ссылку на массив, который достаточно большой, чтобы вместить никнейм игрока, и записывает никнейм в массив. Ей не нужно возвращать никнейм, потому что она просто модифицирует указанный ей массив.

PHP:
main() {
    new a[4];
    GetHi(a);
    // В 'a' теперь записано "hi!" с символом конца строки.
}

GetHi(input[]) {
    input[0] = 'h';
    input[1] = 'i';
    input[2] = '!';
    input[3] = '\0';
}
В указанном выше примере мы знаем, что GetHi модифицирует input, записывая символы в каждую ячейку. Но что будет, если передать в эту функцию строковый литерал?

PHP:
main() {
    GetHi("123");
}

GetHi(input[]) {
    input[0] = 'h';
    input[1] = 'i';
    input[2] = '!';
    input[3] = '\0';
}
Это неопределённое поведение, которое в предыдущих версиях компилятора остаётся абсолютно незамеченным, поскольку раньше он не выдавал предупреждений о том, что скриптер допустил ошибку, передав строковый литерал в функцию, которая может попытаться изменить его.



Семантика const

И здесь на помощь приходит const. Стоит отметить, что это далеко не новый функционал компилятора; единственное, что здесь новое - это предупреждение, указывающее на функции, которые должны либо использовать const, либо модифицировать передаваемый им массив и этим массивом не должен быть строковый/массивный литерал.

Вот пример функции, которая не модифицирует передаваемый ей массив, а просто выводит его как строку:
PHP:
main() {
    Say("Привет!");
}

Say(input[]) {
    if(input[0] == '\0') {
        return;
    }
    printf("Я говорю: %s", input);
    return;
}
Функция может выглядеть логически корректно, и так оно и есть: она проверяет, является ли строка пустой, и если да, то завершает свою работу, иначе - выводит строку и завершает работу. В логике функции нет ничего неправильного.

Тем не менее, семантика сигнатуры* функции некорректна. Некорректность можно заметить, просто прочитав сигнатуру функции:
  • Функция называется Say.
  • Она принимает массив в качестве аргумента.
  • Она может модифицировать массив.
Последний пункт и является причиной некорректности. В сигнатуре функции нет ничего, что могло бы показать, что функция не изменяет массив.

Как это исправить? С помощью const!

PHP:
Say(const input[]) {
    if(input[0] == '\0') {
        return;
    }
    printf("I say: %s", input);
    return;
}
Теперь точно понятно, что не опасно передавать в функцию константные массивы или строковые литералы, поскольку известно, что она ни при каких (правильных) обстоятельствах не может модифицировать массив.

* Сигнатура - комбинация квалификатора функции, типов аргументов, квалификаторов аргументов и типа возвращаемого значения.



Заключение

Надеюсь, этот урок был полезным и вы уже можете найти новые ошибки в своём коде. Может показаться, что компилятор чересчур придирчив, но эти ошибки могут вызвать серьёзные проблемы, причины которых трудно найти (например, ошибки времени выполнения "Invalid instruction").

Вы можете использовать код ниже, дабы "игнорировать" данные предупреждения, добавив его в начало Вашего мода.

PHP:
#pragma warning disable 219
#pragma warning disable 239
#pragma warning disable 214
 
Последнее редактирование модератором:
Сверху Снизу