Думаю, те из вас, кто работал с исходниками Pawn (или с SDK для плагинов к SA-MP), могли заметить макрос arraysize в файле amx.h:
Работает он следующим образом: "sizeof(array)" возвращает размер всего массива (в байтах), а "sizeof(array[0])" - 0-го элемента, в результате деления получается количество элементов в массиве.PHP код:
#define arraysize(array) (sizeof(array) / sizeof((array)[0]))
Пример использования:
Конечно же, этим макросом пользоваться куда удобнее, чем 2 раза писать "sizeof" с названием одного и того же массива.PHP код:
#include <stdio.h>
#include <stdint.h>
#define arraysize(array) (sizeof(array) / sizeof((array)[0]))
int main(int argc, char **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;
}
Однако, здесь есть один подводный камень: макрос никак не может отличить массив от указателя.
Допустим, у вас есть такой код:
В этом примере функции PrintArraySize вместо всего массива передаётся только указатель на него.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 argc, char **argv)
{
uint16_t array[10];
PrintArraySize(array); // Выведет "size = 2".
return 0;
}
Выражение "sizeof(array[0])" вернёт 2, как и положено, но "sizeof(array)" вернёт размер не массива, а указателя - на 32-разрядных платформах это 4 байта, и в итоге макрос arraysize(array) выдаст всего 4/2 = 2 элемента вместо 10.
Таким образом в ваш код легко может вкрасться ошибка, если вы случайно передадите в arraysize указатель вместо массива. В лучшем случае компилятор выдаст предупреждение, которое может остаться незамеченным среди других сообщений в логе построения.
Каким образом решить эту проблему?
Для чистого C решения я не нашёл (не факт, что для этого языка оно вообще есть), но в C++ можно использовать механизм шаблонов.
Эта функция узнаёт размер массива на этапе компиляции, как в случае с макросом (т.е. технически никакого вызова функции не будет), но передать ей указатель не получится.PHP код:
template <typename T, size_t size>
size_t arraysize(const T (&)[size])
{
return size;
}
Рассмотрим работу функции на примере передачи в неё массива:
Здесь функция работает так, как и положено, возвращая количество элементов в массиве.PHP код:
#include <stdio.h>
#include <stdint.h>
template <typename T, size_t size> size_t arraysize(const T (&)[size]){ return size; }
int main(int argc, char **argv)
{
uint16_t array[10];
const int size = (int)arraysize(array);
printf("size = %d\n", size); // Выведет "size = 10".
return 0;
}
Но что будет если передать в неё указатель?
Компилятор выдаёт ошибку при попытке передать в arraysize указатель, поэтому любые случайности с вычислением размера указателя исключаются.PHP код:
#include <stdio.h>
#include <stdint.h>
template <typename T, size_t size> size_t arraysize(const T (&)[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 argc, char **argv)
{
uint16_t array[10];
PrintArraySize(array);
return 0;
}
Работа функции проверена на следующих компиляторах: Visual C++, GCC (в.т.ч. MinGW), Clang.
Статью подготовил: Daniel_Cortez
Специально для Pro-Pawn.ruКопирование данной статьи на других ресурсах без разрешения автора запрещено!