C ++: sizeof для длины массива - PullRequest
3 голосов
/ 08 июня 2010

Допустим, у меня есть макрос с именем LengthOf(array):

sizeof array / sizeof array[0]

Когда я создаю новый массив размером 23, не должен ли я вернуть 23 для LengthOf?

WCHAR* str = new WCHAR[23];
str[22] = '\0';
size_t len = LengthOf(str); // len == 4

Почему len == 4?

ОБНОВЛЕНИЕ : Я сделал опечатку, это WCHAR*, а не WCHAR**.

Ответы [ 5 ]

12 голосов
/ 08 июня 2010

Поскольку str здесь - это указатель на указатель, а не массив.

Это одно из тонких различий между указателями и массивами: в этом случае ваш указатель равенв стеке, указывая на массив из 23 символов, который был выделен в другом месте (предположительно, в куче).

11 голосов
/ 08 июня 2010
WCHAR** str = new WCHAR[23];

Прежде всего, это даже не должно компилироваться - оно пытается присвоить pointer to WCHAR pointer to pointer to WCHAR. Компилятор должен отклонить код на основе этого несоответствия.

Во-вторых, одним из известных недостатков макроса sizeof(array)/sizeof(array[0]) является то, что он может и не сможет полностью работать при применении к указателю вместо реального массива. В C ++ вы можете использовать шаблон для получения кода, такого как отклоненный:

#include <iostream>

template <class T, size_t N>
size_t size(T (&x)[N]) { 
    return N;
}

int main() { 
    int a[4];
    int *b;

    b = ::new int[20];

    std::cout << size(a);      // compiles and prints '4'
//    std::cout << size(b);    // uncomment this, and the code won't compile.
    return 0;
}
0 голосов
/ 08 июня 2010

Вы писали:

WCHAR* str = new WCHAR[23];

если 23 подразумевает статическое значение (не переменное на протяжении всей жизни вашей программы), лучше использовать #define или const, чем просто жесткое кодирование 23.

#define STR_LENGTH 23
WCHAR* str = new WCHAR[STR_LENGTH];
size_t len = (size_t) STR_LENGTH;

или C ++ версия

const int STR_LENGTH = 23;
WCHAR* str = new WCHAR[STR_LENGTH];
size_t len = static_cast<size_t>(STR_LENGTH);
0 голосов
/ 08 июня 2010

Проблема в том, что оператор sizeof проверяет размер своего аргумента. Аргумент, передаваемый в вашем примере кода: WCHAR*. Итак, sizeof (WCHAR *) равен 4. Если у вас есть массив, такой как WCHAR foo[23], и вы взяли sizeof(foo), то переданный тип будет WCHAR[23], по сути, и даст sizeof(WCHAR) * 23. Фактически при типе компиляции WCHAR* и WCHAR[23] - это разные типы, и хотя мы с вами видим, что результат new WCHAR[23] функционально эквивалентен WCHAR[23], в действительности тип возвращаемого значения WCHAR*, причем нет информации о размере.

Как примечание, поскольку sizeof(new WCHAR[23]) равно 4 на вашей платформе, вы, очевидно, имеете дело с архитектурой, в которой указатель составляет 4 байта. Если вы построите это на платформе x64, вы обнаружите, что sizeof(new WCHAR[23]) вернет 8.

0 голосов
/ 08 июня 2010

Как уже отмечали другие, макрос не работает должным образом, если на него передан указатель вместо фактического массива.К сожалению, поскольку указатели и массивы оцениваются одинаково в большинстве выражений, компилятор не сможет дать вам понять, что есть проблема, если вы не сделаете ваш макрос несколько более сложным.

Для C ++ версии макроса, которая безопасна для типов (сгенерирует ошибку, если вы передадите указатель, а не тип массива), см .:

Itточно не решит вашу проблему, но даст вам знать, что вы делаете что-то не так.

Для макроса, который работает в C и несколько безопаснее (многие указатели будут диагностировать как ошибку,но некоторые пройдут без ошибок, в том числе и ваша, к сожалению):

Конечно, используя мощность #ifdef __cplusplus, вы можете использовать как заголовок общего назначения, так и компилятор, который выбирает более безопасный для сборок C ++ и C-совместимый скурица C ++ не действует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...