Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 1 из 1
  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    Мифы о Pawn-скриптинге - #4

    Внимание: данная тема закрыта для защиты от копирования.
    Если есть какие-то вопросы, замечания или просто пожелания по поводу данного урока - оставляйте их здесь.


    Миф 4: "В командах на ZCMD/DC_CMD лучше использовать params/params[0]/params[1], чем создавать новую переменную/массив."

    Статус: Опровергнут, Подтверждён.

    Описание:
    Похоже на предыдущий миф (рекомендуется к прочтению для понимания теории в данном мифе), но больше относится к командам.
    Пример кода:
    1. CMD:example(playerid, params[])
    2. {
    3. sscanf(params, "ud", params[0], params[1]);
    4. // ...
    5. }

    Заблудшие обычно аргументируют использование ячеек массива params экономией памяти. На деле же этот единственный плюс незначителен и не оправдывает себя перед меньшей читаемостью и надёжностью кода.

    Доказательство:
      Открыть/закрыть
    В предыдущей статье было сказано, почему не стоит использовать массивы вместо одиночных переменных.
    В данной же статье будут рассмотрены дополнительные причины, почему не следует заменять все переменные на params[X] в командах.
    • Нет гарантии существования params[1], params[2], ...
      При использовании DC_CMD, если строка params пустая, в ней будет существовать только 0-я ячейка, в которой будет символ '\0', означающий конец строки (в случае с ZCMD будет комбинация из двух ячеек {'\1', '\0'}, но это всего лишь костыль, сделанный чтобы избежать краша из-за передачи пустой строки через CallLocalFunction).
      И если попытаться использовать ячейки с индексом больше 0 (params[1], params[2], etc.), возникнет ошибка доступа к неправильному участку памяти.
      Проверим это утверждение на практике:
      1. #include <a_samp>
      2. #include <dc_cmd>
      3.  
      4. CMD:test(playerid, params[])
      5. {
      6. params[1] = 0; // Если строка params пустая, 1-й ячейки не будет существовать.
      7. }
      8.  
      9. main()
      10. {
      11. cmd::test(0, ""); // Вызываем команду с пустой строкой.
      12. }

      При запуске с плагином crashdetect в консоль будет выведено сообщение об ошибке: "Invalid memory access".
      Без crashdetect под Windows никакого эффекта не будет, но под Linux возникнет краш.
      Отсюда и львиная доля крашей серверов на хостинге, которые не возникают на локальном сервере.

    • Запутывание кода.
      Для отдельных переменных можно использовать свои имена, по которым эти переменные можно отличить друг от друга и легко понять их назначение.
      Если же использовать вместо переменных ячейки массива, этим ячейкам нельзя задать отдельные названия - они различаются только по индексам, которые сложнее удерживать в голове, если в команде используется несколько ячеек params. Это может привести к путанице как у людей, которые просто читают такой код, так и у самого автора кода, который может случайно использовать не ту ячейку.


    Справедливости ради, следует заметить, что при использовании 0-й ячейки массива params компилятор оптимизирует обращения к этой ячейке до 1 инструкции вместо 2 (см. предыдущую статью об использовании массивов).
    Благодаря этому обращение к params[0] происходит с такой же скоростью, как и к одиночной переменной, но при этом никаких дополнительных объёмов памяти, как с одиночной переменной, выделять не нужно.
    Тем не менее, это правило действует только для нулевых ячеек. Для ячеек с индексами 1, 2, 3, ... выгоднее всё же использовать локальные переменные.
    К тому же в современных реалиях лучше обеспечить читаемость и надёжность кода, чем рисковать ради экономии каких-то 4 байт оперативы.
    Сервера запускаются не на калькуляторах, а на хостингах, которые предоставляют десятки и даже сотни мегабайт оперативной памяти.

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

    Но остаётся ещё одно применение для params: хранение массивов. Чаще всего этими массивами оказываются строки.
    Рассмотрим следующий пример:
    1. CMD:pm(playerid, params[])
    2. {
    3. new targetid;
    4. sscanf(params, "us[128]", targetid, params);
    5. if (0 == IsPlayerConnected(targetid))
    6. return SendClientMessage(playerid, -1, "Игрок не подключен!");
    7. return SendClientMessage(targetid, -1, params);
    8. }

    В таком случае лучше будет сохранить текст сообщения в массив params.
    Во-первых, нет риска выйти за пределы массива. Если в нём помещается весь текст параметров команды, то часть текста и подавно влезет.
    Во-вторых, не нужно выделять место в стеке. Также не будет никаких потерь во времени из-за инициализации всех элементов массива нулями (эти потери ничтожно малы, но они всё же есть).
    В-третьих, особых проблем с понятностью кода возникнуть не должно. Если использовать params только для хранения текста, то при использовании params где-то ещё кроме sscanf (пример в коде выше: SendClientMessage(playerid, -1, params)), то станет понятно, что это какой-то текст - в данном случае это текст для личного (PM) сообщения.
    Таким образом, в случае с сохранением текста из команды массив params может пригодиться, т.к. у такого подхода практически нет никаких минусов.

    Итог:
    • Для использования отдельных ячеек массива params вместо локальных переменных - миф опровергнут.
    • Для использования массива params целиком для сохранения текста из параметров команды - миф подтверждён.



    Вывод: Использование ячеек params вместо одиночных переменных чревато запутыванием кода (запоминать, что находится в params[0], а что в params[1] и params[2] мазохизму подобно).
    Кроме того, размер массива params может меняться в зависимости от параметров команды, гарантировано только существование params[0]. Если попытаться сохранить что-то в другой ячейке, можно словить ошибку из-за доступа к невалидному участку памяти.
    Тем не менее, есть смысл использовать массив params для сохранения в него строк из самого себя.
    Пример: команда /pm [ID игрока/часть ника] [текст]
    1. CMD:pm(playerid, params[])
    2. {
    3. new targetid;
    4. sscanf(params, "us[128]", targetid, params);
    5. if (0 == IsPlayerConnected(targetid))
    6. return SendClientMessage(playerid, -1, "Игрок не подключен!");
    7. return SendClientMessage(targetid, -1, params);
    8. }



    Специально для Pro-Pawn.ru
    Не разрешается копирование данной статьи на других ресурсах без разрешения автора.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 19 пользователя(ей) сказали cпасибо:
    $continue$ (13.12.2015) ArtZet (13.12.2015) BadPawn (20.02.2016) BENGO (24.12.2017) Desulaid (12.12.2015) DmitriyVasilev (06.07.2019) Engineer (15.05.2016) execution (10.04.2018) Geebrox (25.06.2016) L0ndl3m (12.12.2015) LLIapuk (14.12.2015) Nurick (14.12.2015) pawnoholic (10.04.2018) Profyan (13.12.2015) Quman (13.12.2015) Reim (19.12.2015) Sarah (27.06.2016) Sp1ke (13.12.2015) ^_^ (13.12.2015)
 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •