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

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

    Псевдооператор arraysize

    Думаю, те из вас, кто работал с исходниками Pawn (или с SDK для плагинов к SA-MP), могли заметить макрос arraysize в файле amx.h:
    PHP код:
    #define arraysize(array)  (sizeof(array) / sizeof((array)[0])) 
    Работает он следующим образом: "sizeof(array)" возвращает размер всего массива (в байтах), а "sizeof(array[0])" - 0-го элемента, в результате деления получается количество элементов в массиве.
    Пример использования:
    PHP код:
    #include <stdio.h>
    #include <stdint.h>

    #define arraysize(array)  (sizeof(array) / sizeof((array)[0]))

    int main(int argcchar **argv)
    {
        
    uint16_t array[10];
        
    // Значение, возвращаемое макросом arraysize имеет тип size_t, поэтому лучше явно привести его к типу int.
        
    const int size = (int)arraysize(array);
        
    // sizeof(array) = 20, sizeof(array[0]) = 2, 20 / 2 = 10
        
    printf("size = %d\n"size); // Выведет "size = 10".
        
    return 0;

    Конечно же, этим макросом пользоваться куда удобнее, чем 2 раза писать "sizeof" с названием одного и того же массива.
    Однако, здесь есть один подводный камень: макрос никак не может отличить массив от указателя.
    Допустим, у вас есть такой код:
    PHP код:
    #include <stdio.h>
    #include <stdint.h>

    #define arraysize(array)  (sizeof(array) / sizeof((array)[0]))

    void PrintArraySize(uint16_t array[])
    {
        const 
    int x = (int)arraysize(array);
        
    printf("x = %d\n"x);
    }

    int main(int argcchar **argv)
    {
        
    uint16_t array[10];
        
    PrintArraySize(array); // Выведет "size = 2".
        
    return 0;

    В этом примере функции PrintArraySize вместо всего массива передаётся только указатель на него.
    Выражение "sizeof(array[0])" вернёт 2, как и положено, но "sizeof(array)" вернёт размер не массива, а указателя - на 32-разрядных платформах это 4 байта, и в итоге макрос arraysize(array) выдаст всего 4/2 = 2 элемента вместо 10.
    Таким образом в ваш код легко может вкрасться ошибка, если вы случайно передадите в arraysize указатель вместо массива. В лучшем случае компилятор выдаст предупреждение, которое может остаться незамеченным среди других сообщений в логе построения.

    Каким образом решить эту проблему?
    Для чистого C решения я не нашёл (не факт, что для этого языка оно вообще есть), но в C++ можно использовать механизм шаблонов.
    PHP код:
    template <typename Tsize_t size>
    size_t arraysize(const (&)[size])
    {
        return 
    size;

    Эта функция узнаёт размер массива на этапе компиляции, как в случае с макросом (т.е. технически никакого вызова функции не будет), но передать ей указатель не получится.
    Рассмотрим работу функции на примере передачи в неё массива:
    PHP код:
    #include <stdio.h>
    #include <stdint.h>

    template <typename Tsize_t sizesize_t arraysize(const (&)[size]){ return size; }

    int main(int argcchar **argv)
    {
        
    uint16_t array[10];
        const 
    int size = (int)arraysize(array);
        
    printf("size = %d\n"size); // Выведет "size = 10".
        
    return 0;

    Здесь функция работает так, как и положено, возвращая количество элементов в массиве.
    Но что будет если передать в неё указатель?
    PHP код:
    #include <stdio.h>
    #include <stdint.h>

    template <typename Tsize_t sizesize_t arraysize(const (&)[size]){ return size; }

    void PrintArraySize(uint16_t array[])
    {
        const 
    int size = (int)arraysize(array); // error: no matching function for call to 'arraysize(uint16_t*&)'
        
    printf("size = %d\n"x);
    }

    int main(int argcchar **argv)
    {
        
    uint16_t array[10];
        
    PrintArraySize(array);
        return 
    0;

    Компилятор выдаёт ошибку при попытке передать в arraysize указатель, поэтому любые случайности с вычислением размера указателя исключаются.

    Работа функции проверена на следующих компиляторах: Visual C++, GCC (в.т.ч. MinGW), Clang.


    Статью подготовил: Daniel_Cortez

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

  2. 7 пользователя(ей) сказали cпасибо:
    Angell (26.10.2016) BadPawn (27.10.2016) DeimoS (26.10.2016) Desulaid (25.10.2016) Disinterpreter (26.10.2016) oukibt (21.11.2020) VVWVV (25.10.2016)
  3. #2
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Кстати, можно ещё добавить оператор constexpr. Как минимум, он покажет, что функция и значение в ней выполняется на этапе компиляции.
    PHP код:
    template <class _Tysize_t sizeconstexpr size_t arraysize(_Ty(&)[size]) { return size; } 
    Кроме того, в примере допущена ошибка: нет переменной X. Исправленный код:
    PHP код:
    #include <stdio.h> 
    #include <stdint.h> 

    template <typename Tsize_t sizesize_t arraysize((&)[size]){ return size; } 

    void PrintArraySize(uint16_t array[]) 

        const 
    int size = (int)arraysize(array); // error: no matching function for call to 'arraysize(uint16_t*&)' 
        
    printf("size = %d\n"size); 


    int main(int argcchar **argv

        
    uint16_t array[10]; 
        
    PrintArraySize(array); 
        return 
    0


  4. #3
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    в примере допущена ошибка: нет переменной X
    Спасибо, исправил.


    Цитата Сообщение от VVWVV Посмотреть сообщение
    Кстати, можно ещё добавить оператор constexpr. Как минимум, он покажет, что функция и значение в ней выполняется на этапе компиляции.
    Во-первых, оператор constexpr доступен только начиная со стандарта C++11.
    Во-вторых, в функции нет никаких вычислений, возвращается заранее известное значение - любой адекватный компилятор и без constexpr заинлайнит эту функцию.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

 

 

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

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

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

Ваши права

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