Приветствую,в С есть два понятия - "integral promotion" и "arithmetic conversion". Никак не могу окончательно разобраться. Вот пример:
char a = 9;
unsigned b = 4;
a = b + a;Как я понимаю, 'a' будет преобразован к int? Но ведь это не означает, что компилятор под 'a' выделит 4 байта вместо одного (если рассматривать систему с int=32бита)? Т.е. компилятор задействует какое-то внутреннее представление для сконвертированного типа?
Спасибо за пояснения.
>[оверквотинг удален]
>char a = 9;
>unsigned b = 4;
>a = b + a;
>
>Как я понимаю, 'a' будет преобразован к int? Но ведь это не
>означает, что компилятор под 'a' выделит 4 байта вместо одного (если
>рассматривать систему с int=32бита)? Т.е. компилятор задействует какое-то внутреннее представление для
>сконвертированного типа?
>
>Спасибо за пояснения.Не претендую на истину...
Компилятор на char все равно выделит int или даже больше, поскольку выравнивает по заданному размеру все переменные. Этот размер регулируется ключами компилятора. Тоесть физически под 1 байтовую переменную будет выделено не менее 4 байт, 3 из которых никто никогда не использует. (На самом деле граница выравнивания может быть разной в разных платформах и при разных умолчаниях компилятора о выравнивании. Но чтоб проще было, я назвал границу в 4 байта)"Integral promotion" это "внутреннее представление". Исходя из названия, что-то может представляться процессору или компилятору не так очевидно, как этого хотел неискушенный кодер. Это тема приведения типов. [1,3]
Приведение типов необходимо для операций над "разнородными" переменными. Чтобы операция выполнилась правильно компилятор должен привести используемые в операции переменные к единому типу. Это "Arithmetic conversion". Существует таблица, в которой описано как компилятор выбирает тот самый тип к которому всё будет приводиться [2]В твоем примере при вычислении "b + a" значение "а" и значение "b" будут загружены в регистр процессора с приведением их к наиболее широкому типу unsigned int в соответствии с условиями преобразования типов "Conditions for Type Conversion" [2]. Разумеется, после вычисления unsigned int не уместится в "char a" и получится варнинг о возможной потере данных при присваивании результата.
Использованная литература
[1] http://msdn.microsoft.com/en-us/library/k630sk6z(VS.80).aspx
[2] http://msdn.microsoft.com/en-us/library/09ka8bxx(VS.80).aspx
[3] http://msmvps.com/blogs/vandooren/archive/2007/10/05/integra...
Приветствую,спасибо за ответ.
>Компилятор на char все равно выделит int или даже больше, поскольку выравнивает
Это определяется стандартом языка или архитектурой платформы?
>по заданному размеру все переменные. Этот размер регулируется ключами компилятора. Тоесть
>физически под 1 байтовую переменную будет выделено не менее 4 байт,
>3 из которых никто никогда не использует. (На самом деле границатогда по идее и отладчик должен показывать, что под переменную типа char выделено 32бита. Я это проверил в gdb на x86, на объявление
char j=6;
(gdb) p j
$1 = 6 '\006'
(gdb) x/t &j
0xbf955af3: 10010101010110110001000000000110
(gdb)видно что крайние правые 8 бит отображают значение 6.
>[оверквотинг удален]
>или компилятору не так очевидно, как этого хотел неискушенный кодер. Это
>тема приведения типов. [1,3]
>Приведение типов необходимо для операций над "разнородными" переменными. Чтобы операция выполнилась правильно
>компилятор должен привести используемые в операции переменные к единому типу. Это
>"Arithmetic conversion". Существует таблица, в которой описано как компилятор выбирает тот
>самый тип к которому всё будет приводиться [2]
>
>В твоем примере при вычислении "b + a" значение "а" и значение
>"b" будут загружены в регистр процессора с приведением их к наиболее
>широкому типу unsigned int в соответствии с условиями преобразования типов "ConditionsИМХО по стандарту он приведет их к int:
"If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int."
В моем примере в тип int укладывается весь диапозон значений ориг. переменной -> значит приведение к int. Я не прав?
То же самое в примере, представленном в http://msmvps.com/blogs/vandooren/archive/2007/10/05/integra...
IMHO оба объекта преобразуются к int? Или это пример для С++ и там правила конверсии типов и преобразований другие?
>for Type Conversion" [2]. Разумеется, после вычисления unsigned int не уместится
>в "char a" и получится варнинг о возможной потере данных при
>присваивании результата.Очевидно, не все компиляторы предупреждают об этом, gcc c "-W -Wall -Wconversion" молчит, а вот splint ругается.
>>Компилятор на char все равно выделит int или даже больше, поскольку выравнивает
>
>Это определяется стандартом языка или архитектурой платформы?
>
>>по заданному размеру все переменные. Этот размер регулируется ключами компилятора. Тоесть
>>физически под 1 байтовую переменную будет выделено не менее 4 байт,
>>3 из которых никто никогда не использует. (На самом деле граница
>
>тогда по идее и отладчик должен показывать, что под переменную типа char
>выделено 32бита.Граница выравнивания определяется только установками компилятора. Выставить нужную границу интересно пользователю, и он для оптимизации выставляет ее равной разрядности платформы.
Смысл выравнивания заключается в том, чтобы ускорить операции с памятью. 32 битному процессору на чтение 32 битной ячейки памяти нужно меньше тактов, чем на чтение 16 или 8 битной ячейки памяти. Существует таблица ассемблерных команд с временами их выполнения.
Для 64 разрядного процессора, соответственно, проще работать с 64 разрядными ячейками.Если при 64 битном выравнивании в памяти лежит объявленная переменная типа char, то скомпилированная программа физически будет читать-писать 64 бита, а использовать только младшие 8. Поэтому памяти физически выделится все-таки 64 бита.
Сам же компилятор будет искренне верить, что работает с 8 битами, поэтому sizeof честно вернет 1.Вопрос выравнивания очень остро встает при межпроцессовом взаимодействии или при заполнении "строгих" таблиц, например таблиц файловой системы/MBR итд или таблиц-параметров к системным вызовам. Наверняка все с этим сталкивались.
>[оверквотинг удален]
>
>char j=6;
>
>(gdb) p j
>$1 = 6 '\006'
>(gdb) x/t &j
>0xbf955af3: 10010101010110110001000000000110
>(gdb)
>
>видно что крайние правые 8 бит отображают значение 6.Я не знаком с gdb, а способов проверки могу представить сразу несколько. Только зачем это всё? В конечном итоге компилятор может от версии к версии различаться, или пользоваться какими-то своими соображениями по размещению данных в памяти. А ведь могут и параметры оптимизации влиять... Я сразу сказал, что эта теория - не есть абсолютная истина. Этих соображений придерживался компилятор от Интела, когда я этим интересовался. Логично и рационально. Я исхожу, что и другие компиляторы к этому стремятся, а там как знать... :)
Для проверки напиши что-ньть вроде
char a = 1; # 1
char b = 1; # 2
char c = 1; # 3
char d = 1; # 4
char e = 1; # 5
char find_me_in_dump = 2; # 6
char f = 1; # 7
char g = 1; # 8А потом посмотри дамп памяти запущенного процесса. Можно еще содержимое бинарника посмотреть, узнать как всё лежит на харде. В обоих случаях надо искать двойку среди единиц. Конечно это простенький способ :) Тут компилятор может оказаться хитрее и соптимизировать что-ньть... Например не обратить внимания на переменные, которые в дальнейшем коде не используются.
Раз с дебагером знаком, то можно запросить на вывод 32 байта памяти, начиная с адреса переменной a. При выравнивании 1 байт двойка будет в шестом байте дампа. При выравнивании 4 байта двойка будет в шестом dword, в младшем байте. Нуитд.
>[оверквотинг удален]
>>
>>В твоем примере при вычислении "b + a" значение "а" и значение
>>"b" будут загружены в регистр процессора с приведением их к наиболее
>>широкому типу unsigned int в соответствии с условиями преобразования типов "Conditions
>
>ИМХО по стандарту он приведет их к int:
>
>"If an int can represent all values of the original type, the
>value is converted to an int; otherwise, it is converted to
>an unsigned int."По второй ссылке в таблице условия в жестком порядке:
# If the preceding three conditions are not met, and if either operand is of type unsigned int, the other operand is converted to type unsigned int.
# If none of the preceding conditions are met, both operands are converted to type int.Но ни в коем случае нельзя их считать руководством к действию, поскольку это компилятор микрософт и под винду. Тот самый ньюанс, когда ссылка на мсдн неуместна в качестве аргумента. Если есть подобное описание алгоритма, которого придерживается gcc, или стандарт си предполагает что-либо иное, то у микрософта последнее место в рейтинге доверия. Я эти ссылки написал просто потому что легче нагуглилось и в общем на вопрос отвечало :)
>В моем примере в тип int укладывается весь диапозон значений ориг. переменной -> значит приведение к int. Я не прав?
>
>То же самое в примере, представленном в http://msmvps.com/blogs/vandooren/archive/2007/10/05/integra...
>IMHO оба объекта преобразуются к int? Или это пример для С++ и
>там правила конверсии типов и преобразований другие?Там нет беззнаковых переменных, не могу однозначно выразить свое имхо :)
К тому же приведение может зависеть от типа переменной, куда ляжет результат. Можно провести эксперименты:1.
char a = -106; unsigned b = 2841417161;
int c = a + b;2.
char a = -106; unsigned b = 2841417161;
unsigned int c = a + b;3.
char a = 106; unsigned b = 2841417161;
int c = a + b;4.
char a = 106; unsigned b = 2841417161;
unsigned int c = a + b;Здесь в четырех примерах меняются два параметра:
тип результирующей переменной, чтобы понять играет ли она роль в приведении типов выражения,
и знак у знаковой переменной, чтобы по результату понять к чему ее привел компилятор при вычислении выражения.У меня нет под рукой никакого компилятора, да и собственный опыт куда интереснее чем лекции слушать ;)
>>for Type Conversion" [2]. Разумеется, после вычисления unsigned int не уместится
>>в "char a" и получится варнинг о возможной потере данных при
>>присваивании результата.
>
>Очевидно, не все компиляторы предупреждают об этом, gcc c "-W -Wall -Wconversion"
>молчит, а вот splint ругается.Ну вот, это как раз к вопросу о разных реализациях. Истина где-то рядом :))
>Как я понимаю, 'a' будет преобразован к int?В Unsigned Int. При операции между разными типами, меньший по размеру операнд преобразуется в тип большего, знаковое в беззнаковое, если другой операнд беззнаковый.
> Но ведь это не
>означает, что компилятор под 'a' выделит 4 байта вместо одногоНет, он сделает ещё одну переменную, внутреннюю типа unsigned. Но никакой памяти не потребуется так как все преобразования будут в регистрах процессора, в тех самых которые будут складываться.
А вот char = unsigned - это опасная операция, на то и недовольство ))
>char a = 9;
>unsigned b = 4;
>a = b + a;Подразумеваемые приведения к типам будут выглядеть вот как:
a = (char) (b + (unsigned)a);(а ссылки на msdn считаю на этом форуме неуместными :)
Есть ли какое-то rule of thumb использования типов в программах, при передаче аргуменов функций, чтобы, как говорится, голова не болела, как и во что будут преобразовываться типы?>[оверквотинг удален]
>>unsigned b = 4;
>>a = b + a;
>
>Подразумеваемые приведения к типам будут выглядеть вот как:
>
>
>a = (char) (b + (unsigned)a);
>
>
>(а ссылки на msdn считаю на этом форуме неуместными :)На тех страницах, ссылки на которые были здесь приведены, фактически пересказывается стандарт С - ИМХО в таких рамках msdn допустим :)