суббота, марта 07, 2009

sizeof('a')

Наверняка вы сразу скажете, что будет выведено на экран, при попытке вывести на печать sizeof('a'). Ну да, конечно, единица. (sizeof(char) == 1, 5.3.3/1).
Однако попробуйте скомпилять это каким-нибудь С++ным компилятором, скажем g++. А потом каким-нибудь сишным компилятором, например gcc. И сравните результат.
Во втором случае будет скорее всего 4. Почему? Потому что в чистом С sizeof('a') == sizeof(int). А вот так.

Спасибо Maniac'у за ссылку.

38 коммент.:

Анонимный комментирует...

по стандарту с++ sizeof(char) == 1

Анонимный комментирует...

хм.. стандарт немного гарантирует насчет размеров типов, но что sizeof( char ) == 1 - это точно гарантируется.

кстати sizeof( wchar_t ) - не гарантируется.

Vladimir Dolzhenko комментирует...

2 Анонимный:
всё по честному - и в чистом Си sizeof(char) == 1, и даже
char c = 'a';
sizeof(c) тоже даст 1 ;)

Анонимный комментирует...

стандарт немного гарантирует насчет размеров типов, но что sizeof( char ) == 1 - это точно гарантируется.

Всё нормально. Просто в C литерал 'a' имеет тип int, а не char как в C++.

Анонимный комментирует...

Давно читаю Вас и больше всего поражуюсь тому как Вы пишите об абсолютно очевидных вещах с таким видом как будто каждый раз по меньшей мере открываете Америку. Или и в правду для Вас всё это Америка?

Александр комментирует...

Полезное наблюдение. В реальной жизни крайне редко удается видеть sizeof('a'), поэтому с одной стороны незнание этого факта в целом не опасно, если писать грамотно, а другой стороны - может именно по этому не все знают о такой тонкости.

Surzh комментирует...

Господин Анонимный (хотябы представились, когда критикуете), если Вы это знаете, то не значит, что все знают.

Считаю, что данный топик подобен Хабровскому http://habrahabr.ru/blogs/cpp/53576/ и вижу в них вселенский смысл, который помогает уйти от многих проблем при кодинге или портировании кода.

И да... кстати.. если Вас что-то не устраивает на этом блоге, то можете проследовать по адресу about:blank и выбрать ту информацию, которая Вам больше по вкусу.

Анонимный комментирует...

MigMit:~ MigMit$ cat > test.c << EOF
> #include <stdio.h>
> int main() {
> printf("%d\n", sizeof('a'));
> printf("%d\n", sizeof(char));
> return 0;
> }
> EOF
MigMit:~ MigMit$ gcc test.c -o test
MigMit:~ MigMit$ ./test
4
1

Alena комментирует...

2Анонимный:

по стандарту с++ sizeof(char) == 1


5.3.3/1, точно, поправила. Спасибо.

Surzh комментирует...

Я думаю, что истина в другом.

При конструкции sizeof('a') скорее всего внутрь sizeof попадает немного не то, что бы предпологаем.
Например, это может быть указатель на char.

Alena комментирует...

2Анонимный:

Давно читаю Вас и больше всего поражуюсь тому как Вы пишите об абсолютно очевидных вещах

По данному конкретному посту беглый опрос знакомых программеров показал, что это не так.

с таким видом как будто каждый раз по меньшей мере открываете Америку.

Откуда Вы знаете, какой у меня вид, Вы ж меня не видите :-).

Или и в правду для Вас всё это Америка?

Этот пост, он не для того, чтобы повыпендриваться и доказать самой себе какая я крутая, я про себя все знаю. Цель - рассказать другим то, что, возможно, не знаю они и вызвать заинтересованность. Что мне удается, потому что, как Вы сами говорите, Вы давно меня читаете.

Анонимный комментирует...

Популярна была такая задачка:
Программа должна вывести строку "С++", если скомпилирована на С++ и "С", если на С.
Как раз решается с помощью этого отличия.

Александр комментирует...

Мда, я тут озадачил людей примерчиком:

#include <stdio.h>
int main() {
  char s[4];
  s[0] = 'C'; s[1] = s[2] = '+'; s[3] = 0;
  s[sizeof(' ') ^ 5] = 0;
  printf("%s\n", s);
  return 0;
}

gcc -o x x.c && ./x
g++ -o x x.c && ./x

Народ повеселился ;-)

Анонимный комментирует...

Только лучше
char s[5];
а то при
s[sizeof(' ') ^ 5] = 0;
в C++ вылезаем за границы..

А вообще интересный пример =)

Fla комментирует...

Ну это как бы понятно - по стандарту в C++ литеры имеют размер 1 байт(char), а в C - размер инта(ну это уже специфика платформы)

Анонимный комментирует...

Не понял, зачем узнавать sizeof(char), если это выражение по стандарту должно быть 1? На практике чаше нужно узнавать sizeof("а"), чтобы выяснить размер символа

Анонимный комментирует...

Если нужно узнать размер 1ого символа то sizeof("а") вам тут ничем не поможет. Используйте одинарные кавычки.

Surzh комментирует...

sizeof("а") будет равен размеру указателя (для 32битных платформ - 4 байта)

Анонимный комментирует...

Выходит что я за 16-ти битным компутером сижу?

int main()
{
cout << sizeof('a') << '\n';
cout << sizeof("a") << endl;
return 0;
}
результат (С++):
1
2

У меня компилятор - GCC.
Возможно в компиляторах от М$ или Intel это будет указатель куда-то (я не знаю как они со строками работают), но обычно запись "А" будет означать массив (строку) состоящую из символа 'A' и символа конца строки '\0'. И размер этого безобразия будет 2 char.

Surzh комментирует...

Что есть массив? Масив - это указатель на первый элемент. Или я ошибаюсь?

Анонимный комментирует...

2Surzh

Массив - это указатель на символы char, которые представляются 'a'. Вот почему sizeof('a') в некоторых компиляторах возвращает размер байта и на 16-ти битных системах sizeof('a') может быть равен 2. Но по стандарту должен возвращать 1.

"a" - это не указатель, а строка из одного символа + символ конца строки. поэтому аноним в следующем посте получил 2. Я сам что-то с будуна лохонулся и написал двойные кавычки, но ведь в двойных кавычках смысла действительно нет.

char szError = 'a';
int i = sizeof(szError);

Результат 1, потому что строка

TCHAR szError = 'a';
int i = sizeof(szError);
результат 2, потому что строка Unicode

Анонимный комментирует...

sizeof("а") - имеет смысл - чтобы узнать, какой тип имеет строка по умолчанию в данном компиляторе - Unicode или нет, только нужно не забывать про символ конца строки.

Surzh комментирует...

"Итак, тип строк в С – массив. Однако каков тип элементов этого массива? Вообще говоря, возможны варианты. Исторически символ занимает 1 байт, в этом случае он имеет тип char. При этом существуют и другие кодировки, в которых символ представляется, например, двумя байтами. Для работы с такими строками требуются специальные функции."

http://www.rsdn.ru/article/cpp/cstr.xml

"В C++ имя массива представляет собой не только имя, которое вы используете в своих программах, но и является адресом, по которому в памяти находится первый элемент массива."

http://do.rksi.ru/library/courses/demo/tema1_7.dbk

В чистом С насколько я понимаю (увы, сейчас нет времени искать инфу в инете) та же самая ситуация в отношении массив-указатель.

Stoune комментирует...

Народ я фигею, вроде образованные люди что-то обсуждаете, а стандарт ни один не удосужится открыть.
Встандарте НЕТ упоминаний о байтах что в принципе логично. Люди развращённые и ограниченные платформой Х86 не представляют что может быть другой мир(даже Х86 имеет множество различий, но поколение взращённое на Java, C#, VB слабо представляет что int может быть не 32 бита, а о 8087 им floating point arithmetic имееют очень приблизительное представление, ).

В стандарте пишется:
3.9.1 Fundamental types
Objects declared as characters (char) shall be large enough to store any member of the implementation’s basic
character set. - где здесь байты?
....
Plain ints have the natural size suggested by the
architecture of the execution environment40; the other signed integer types are provided to meet special
needs. - странно и здесь нет никаких байт, зато есть упоминание об оптимизации под платформу.

4 Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number
of bits in the value representation of that particular size of integer.42 - и здесь нет указания на конкретное число бит или байт.

Ну и нужно вернутся на начало документа:
1.9 Program execution
2 Certain aspects and operations of the abstract machine are described in this International Standard as
implementation-defined (for example, sizeof(int)). These constitute the parameters of the abstract machine.
Each implementation shall include documentation describing its characteristics and behavior in these
respects.5 Such documentation shall define the instance of the abstract machine that corresponds to that
implementation (referred to as the “corresponding instance” below). - Ведь надо же, стандарт описывает абстрактную машину, а за подробностями реализации обращатся к разработчикам компилятора.
Проблемы непонимания возникают изза того что нужно привыкнуть в С++ всё измеряется в unsigned char, но размера unsigned char вам никто не прогарантирует:
3.9 Types
4. The object representation of an object of type T is the sequence of N unsigned char objects taken up by
the object of type T, where N equals sizeof(T).

Surzh комментирует...

Stoune, ваше замечание корректно в смысле ограниченности рассмотрения господами (в частности мной) исключительно платформы x86.
Однако, просьба не употреблять фразы типа "поколение взращённое на Java, C#, VB" ибо в большей степени (я так думаю) это не является правдой. К тому же наверняка тут отписываются люди разных поколений.

Дополнительно, к сказанному товарищем Stoune хочу добавить (фактически я это хотел описать в предыдущих своих постах, но не получилось): не возводите строку в ранг некого субстракта, который живет сам по себе. В некоторых языках это понятие действительно абстрагируется до чего-то вселенского (к ним как раз относятся Java, C#, VB, но ими дело не ограничивается). Однако, С и С++ рассматривает строку, как просто набор/массив символов (char`ов) и говорить о том, что это отдельная структурная единица языка нельзя.

Surzh комментирует...

Забыл в прошлый пост написать...

Stoune, если есть возможность, приведите пожалуйста пример платформы, где размер unsigned char не равен 1 байту.

Fla комментирует...

2 C++ Developer

Эмн 'a' - это не строка. Это литера. Будь она хоть трижды юникодовская - это литера. И она равна числу 97(елси a - английская), как в ASCII, так и в юникоде.

sizeof(type*) возвратит кол-во выделенной памяти по этому указателю в байтах.
http://en.wikipedia.org/wiki/Sizeof#Using_sizeof_with_arrays
Dixi.

Fla комментирует...

type* => const type*

selffix

"asdfsdfa" - const char*

Stoune комментирует...

Насчёт размера unsigned char был неправ:
5.3.3 Sizeof [expr.sizeof]
1 The sizeof operator yields the number of bytes in the object representation of its operand. The operand is
either an expression, which is an unevaluated operand (Clause 5), or a parenthesized type-id. The sizeof
operator shall not be applied to an expression that has function or incomplete type, or to an enumeration
type before all its enumerators have been declared, or to the parenthesized name of such types, or to
an lvalue that designates a bit-field. sizeof(char), sizeof(signed char) and sizeof(unsigned char)
are 1. The result of sizeof applied to any other fundamental type (3.9.1) is implementation-defined.
[ Note: in particular, sizeof(bool), sizeof(char16_t), sizeof(char32_t), and sizeof(wchar_t) are
implementation-defined.70 —end note ] [ Note: See 1.7 for the definition of byte and 3.9 for the definition
of object representation. —end note

Но только для char. Допущения о размере для других типов только если вы жостко на зашиваетесь на одну платформу от лукавого. Хотя учитывая переход от 16-битного к 32-х розрядного int , и длящийся переход на x64 в рамках одной платформы x86 хардкодить размер было и есть плохой практикой.

Что интересно из ответивших мне всё таки ни один стандарт открыть не удосужился.

2 Surzh ... не возводите строку в ранг некого субстракта, который живет сам по себе... - А вот здесь я с вами не соглашусь. Дуализм масивов и указателей и строковых данных это один из самых главных факторов почему С снискас успех в встроенных системах, и причина почему тот же Pascal например там не прижился. С для меня это эдакий макроассемблер, который не слишком оторван от машины, и генерирует почти оптимальный код, но позволяет мне мыслить немного висшими абстракциями. Хотя примерно 70% обработчиков прерываний приходится всё же писать на asm-е.

Ну и для поисков что и как должно быть я часто обращаюсь к "The New C Standard: An Economic and Cultural Commentary"
http://www.knosof.co.uk/cbook/cbook.html это фундаментальный труд: стандарт С с коментариями и ссылками на стандарты по С и С++ и замечаниями об отличиях.

Анонимный комментирует...

"Stoune, если есть возможность, приведите пожалуйста пример платформы, где размер unsigned char не равен 1 байту."

К примеру платформа DSP TexasInstruments в частности процы серии C2833x с которыми я работю точно не имеют типа данных размером 1байт. Там вообще байт адресуется уже с извращениями.
Там есть понятие слово и слово это==2байта.
sizeof(char) возвращает 1, но это не 1 байт!
char на самом деле двухбайтовый.
Имеем:
sizeof(char)==1
sizeof(int)==1
sizeof(long)==2
sizeof(long long)==4

sizeof("abc")==4
sizeof('a')==4

sizeof(char*)==2; //т.к. хоть шина адреса 32бита

И действительно Си не только на x86 используется:)

Анонимный комментирует...

Пардон, там я немного опечатался в посте выше
sizeof('a')==1, а не 4

Surzh комментирует...

Спасибо.

Но поясните, в чем принципиальная разница между sizeof(char*) и sizeof("abc")??????

Surzh комментирует...

Сейчас потестировал (под вижуалом). Действительно, почему-то выдает количество символов. Тем не менее, даёт сделать такие операции, как:
sizeof(*"dfgdfgsdfg") == 1
sizeof(*L"dfgdfgsdfg") == 2

Собственно я не удивился, что оно дало мне так сделать. Но непонятно, почему он выдает количество символов при sizeof("dfgdfgsdfg").

Чует моё сердце, что там есть какая-то хитрость, которую надо искать в дебрях документации.

Leirby комментирует...

Stoune
"Народ я фигею, вроде образованные люди что-то обсуждаете, а стандарт ни один не удосужится открыть.
Встандарте НЕТ упоминаний о байтах что в принципе логично."

Удосужился, открыл стандарт.

"byte
addressable unit of data storage large enough to hold any member of the basic character set of the execution environment"

Анонимный комментирует...

sizeof("asdf") по идее считает размер строкового литерала. Строковый литерал это особая сущность. Вас же не напрягает что можно сделать char a[]="asdf"; и все будет хорошо?

Kodt комментирует...

2 Surzh

Строковый литерал - это массив, а не указатель (к которому массив приводится).
Размер его, соответственно, - это размер массива, то есть размер элемента помножить на количество элементов (не забывая о концевом нуле).

Легко убедиться: http://codepad.org/p4995p5i

template<class E, int N>
void foo(E (&arr)[N])
{
// ну скажем,
std::cout << typeid(E).name() << "[" << N << "]" << std::endl;
}

int main()
{
foo("x");
foo("xz");
foo("hello world");
}

Анонимный комментирует...

Кто нить в ISO 15926 разобрался? НА мой взгляд полная муть...

Aser комментирует...

Собственно я не удивился, что оно дало мне так сделать ...