C ++: sizeof для длины массива

Скажем, у меня есть макрос с именем 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**.


person Nick Heiner    schedule 07.06.2010    source источник
comment
Возможный дубликат Как найти размер (a указатель, указывающий на массив) (могло быть лучшее совпадение; это было лучшее, что я смог найти с помощью быстрого поиска).   -  person James McNellis    schedule 08.06.2010
comment
Вы, наверное, хотите std::vector<std::wstring> > или что-то в этом роде. Это C ++, а не C.   -  person fredoverflow    schedule 08.06.2010


Ответы (5)


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

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

person Mark Rushakoff    schedule 07.06.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;
}
person Jerry Coffin    schedule 07.06.2010

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

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

Это не совсем «решит» вашу проблему, но даст вам понять, что вы делаете что-то не так.

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

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

person Michael Burr    schedule 07.06.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.

person Nathan Ernst    schedule 08.06.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);
person user347594    schedule 08.06.2010