Когда мне нужно типизировать в C? - PullRequest
2 голосов
/ 17 сентября 2010

Я понимаю типизацию ... но только в ретроспективе.Мой процесс определения того, что требует типизации в выражениях , обычно имеет обратную силу, потому что я не могу предсказать, когда это потребуется, потому что я не знаю, как компилятор выполняет их.Несколько банальный пример:

int8_t x = -50;
uint16_t y = 50;
int32_t z = x * y;

На моем 8-разрядном процессоре (Freescale HCS08) для z установлено значение 63036 (2 ^ 16 - 50 ^ 2).Я могу видеть, как это будет один из возможных ответов (из, возможно, 4 других), но я бы не догадался, что это будет один.

Лучший способ задать вопрос: когда типы взаимодействуют с операторами (+-*/), что происходит?

Ответы [ 7 ]

3 голосов
/ 17 сентября 2010

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

В некоторых случаях вы хотите, чтобы информация была потеряна. Допустим, вы работаете с таблицей поиска sin / cos длиной 256 записей. Очень удобно и обычно (по крайней мере, во встроенной среде) использовать значение u8 для доступа к таблице, чтобы индекс естественным образом соответствовал размеру таблицы, сохраняя при этом циклический характер sin / cos. Затем требуется повторная передача обратно в u8, но это именно то, что вам нужно.

2 голосов
/ 17 сентября 2010

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

int = 32бит, uint16_t = unsigned short, int8_t = знаковый символ

Это приводит к значению -2500, поскольку оба операнда преобразуются в int,и операция выполняется со знаком, а подписанный результат записывается в int32_t.

int = 16 бит, uint16_t = беззнаковый int, int8_t = знаковый символ

Это приводит к значению 63036, поскольку операнд int8_t сначала преобразуется в unsinged int, что приводит к 65536-50.Затем он умножается на него, в результате чего получается 3 274 300 % 65536 (без знака по модулю арифметика), равное 63036. Этот результат затем записывается в int32_t.

Обратите внимание, что минимальный размер int бит 16 бит.Так что на вашей 8-битной платформе этот второй сценарий, скорее всего, произойдет.


Я не буду пытаться объяснить правила здесь, потому что мне не имеет смысла повторять то, что написано в стандарте / проекте (которое свободно доступно), в мельчайших деталях ичто обычно легко понятно.

1 голос
/ 17 сентября 2010

То, что с вами происходит здесь, это целочисленное продвижение. Как правило, перед вычислением все типы, имеющие ранг меньше int, повышаются до signed или unsigned, здесь до unsigned, поскольку один из ваших типов является беззнаковым типом. Вычисление выполняется с этой шириной и подписью, и результат в конце концов присваивается.

В вашей архитектуре unsigned, вероятно, имеет ширину 16 бит, что соответствует значению, которое вы видите. Затем для присвоения вычисленное значение вписывается в целевой тип, который является еще более широким, поэтому значение остается тем же.

1 голос
/ 17 сентября 2010

Если вы хотите получить полный ответ, посмотрите на предложения других людей.Прочитайте стандарт C относительно неявного преобразования типов.И напишите контрольные примеры для своего кода ...

Интересно, что вы говорите это, потому что этот код:

#include "stdio.h"
#include "stdint.h"

int main(int argc, char* argv[])
{
  int8_t x = -50;
  uint16_t y = 50;
  int32_t z = x * y;
  printf("%i\n", z);
  return 0;
}

дает мне ответ -2500.

См .: http://codepad.org/JbSR3x4s

Это происходит для меня, как на Codepad.org, так и в Visual Studio 2010

1 голос
/ 17 сентября 2010

Когда компилятор выполняет неявное приведение, он следует стандартному набору арифметических преобразований. Они документированы в стандарте C в разделе 6.3. Если у вас есть книга K & R , в разделе приложения A6.5 есть хорошее резюме.

1 голос
/ 17 сентября 2010

Вам понадобится кастинг типов, когда вы кастуете вниз.

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

0 голосов
/ 17 сентября 2010

Чтобы объяснить, что происходит в вашем примере, вы получили 8-битный тип со знаком, умноженный на 16-битный тип без знака, и поэтому меньший тип со знаком переводится в больший тип без знака.После создания этого значения оно присваивается 32-битному типу.

Если вы просто работаете с целочисленными типами со знаком или без знака, это довольно просто.Система всегда может преобразовать меньший целочисленный тип в больший без потери точности, поэтому она будет преобразовывать меньшее значение в больший тип в операции.В смешанных вычислениях с плавающей запятой и целочисленными значениями он преобразует целое число в тип с плавающей запятой, возможно, теряя некоторую точность.

Похоже, вы путаетесь, смешивая типы со знаком и без знака.Система преобразуется в больший тип.Если этот больший тип подписан и может содержать все значения типа без знака в операции, то операция выполняется как подписанная, в противном случае как без знака.В целом, система предпочитает интерпретировать смешанный режим как беззнаковый.

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

...