Очень часто даже в сценариях, где разработчик гарантирует, что переменная никогда не будет превышать одного байта, а иногда и двух байтов;
Многие люди решают использовать типы int для каждой возможной переменной, используемой для представления чисел в диапазоне от 0 до 1.
Почему вместо этого использовать char или short так больно?
Думаю, я слышал, как кто-то сказал, что int - это "более стандартный" тип .. типа.
Что это значит. Мой вопрос: имеет ли тип данных int какие-либо определенные преимущества перед short (или другими меньшими типами данных), из-за каких преимуществ люди почти всегда прибегали к int?
@chux Верно, а почему именно int? Я бы выбрал short, потому что он имеет небольшое преимущество в пространстве, чем int, который выбирают люди, потому что он выглядит лучше, как кажется? Требуется набрать на 2 клавиши меньше. (Обновлено: не забывать, что целые числа могут иметь разный размер)
int обычно представляет собой естественный целочисленный тип данных ЦП, и поэтому операции с участием int имеют тенденцию быть более оптимизированными ЦП и компилятором.
Собственный размер int обычно выбирается таким образом, чтобы он наилучшим образом соответствовал регистрам процессора. short не занимает полный регистр, поэтому он менее эффективен. Используйте размер, соответствующий тому, что вы делаете. Если у вас есть конкретное требование использовать что-либо, кроме целого числа, сделайте это. В противном случае доверьте свою работу компилятору.
Итак, int имеет преимущество в производительности по сравнению с short, как я полагаю, на большинстве процессоров и в некоторых режимах компиляции.
Это неплохо, и это не больно, сильно, это просто бессмысленно, если у вас нет большого количества их рядом, например, g. в массиве, или какой-то сумасшедший объект с сотнями смежных членов short. Или, если вы по какой-то причине специально выполняете 16-битную арифметику, например преобразование системы счисления. Конечно, бывают ситуации, когда это единственно правильный выбор.
@Edenia: Что вы имеете в виду под «определенными преимуществами»?
@NicolBolas Преимущества, которые когда-либо описывались или упоминались в каком-то надежном источнике (руководство по компилятору, создатель языка, Джон Скит, я не знаю) ..
обман из Когда использовать short вместо int? и, возможно, больше
Не используйте ни то, ни другое. Используйте int_fast8_t и int_fast16_t, если вам нужно работать только с меньшими числами. Однако они (как char и short) подчиняются тупо опасным правилам неявного целочисленного продвижения C и C++, что на самом деле является основной причиной по возможности избегать небольших целочисленных типов.
См. Вопрос Правила повышения неявного типа для некоторых примеров, когда эти типы стреляют вам в ногу.
@Lundin Чем опасны целочисленные рекламные акции?
@PSkocik Ссылка, которую я разместил выше, дает несколько примеров для иллюстрации.
@Lundin Спасибо.





Как правило, большая часть арифметических операций на языке C выполняется с использованием типа int (то есть простого int, а не short или long). Это потому, что (а) так сказано в определении C, что связано с тем, что (б) именно так предпочитают работать многие процессоры (по крайней мере, те, которые имели в виду разработчики C).
Поэтому, если вы попытаетесь «сэкономить место», используя вместо этого целые числа short, и напишете что-то вроде
short a = 1, b = 2;
short c = a + b;
компилятор должен выдать код, чтобы фактически преобразовать a из short в int, преобразовать b из short в int, выполнить сложение и преобразовать сумму обратно в short. Возможно, вы сэкономили немного места в хранилище для a, b и c, но ваш код, вероятно, будет больше (и медленнее).
Если вы вместо этого напишите
int a = 1, b = 2;
int c = a + b;
вы тратите немного больше места для хранения в a, b и c, но код, вероятно, меньше и быстрее.
Это несколько упрощенный аргумент, но он стоит за вашим наблюдением, что использование типа short редко, а обычный int обычно рекомендуется. По сути, поскольку это «естественный» размер машины, предполагается, что это наиболее простой тип для выполнения арифметических операций без дополнительных преобразований в и из менее естественных типов. Это своего рода аргумент «В Риме, делай, как римляне», но обычно делает делает использование простого int более выгодным.
Если у вас есть лоты не очень больших целых чисел для хранения, с другой стороны (большой их массив или большой массив структур, содержащих не очень большие целые числа), экономия хранилища для данных может быть большой, и стоит того, чтобы пожертвовать (относительно меньшим) увеличением размера кода и потенциальным увеличением скорости.
См. Также этот предыдущий вопрос SO и эта запись в списке часто задаваемых вопросов C.
Дополнение: как и любая проблема оптимизации, если вы действительно заботитесь об использовании пространства данных, использовании пространства кода и скорости кода, вы захотите провести тщательные измерения, используя свой точный компьютер и процессор. В конце концов, вашему процессору могут не потребоваться какие-либо «дополнительные инструкции преобразования» для преобразования в / из меньших типов, поэтому их использование может быть не таким большим недостатком. Но в то же время вы, вероятно, можете подтвердить, что для изолированных переменных их использование также может не дать какого-либо измеримого преимущества.
Приложение 2. Вот данные. Я экспериментировал с кодом
extern short a, b, c;
void f()
{
c = a + b;
}
Я скомпилировал с помощью двух компиляторов, gcc и clang (компиляция для процессора Intel на Mac). Затем я заменил short на int и снова скомпилировал. Код, использующий int, был на 7 байт меньше в gcc и на 10 байт меньше в clang. Изучение вывода на языке ассемблера позволяет предположить, что разница заключалась в усечении результата, чтобы сохранить его в c; выборка short в отличие от int, похоже, не меняет счетчика команд.
Однако затем я попробовал вызов в двух разных версиях и обнаружил, что это практически не влияет на время выполнения даже после 10000000000 вызовов. Таким образом, «использование short может увеличить размер кода», часть ответа подтверждается, но, возможно, нет, «а также делает его медленнее».
В этом есть смысл, я знал взаимосвязь между пространством и производительностью. Кэширование - это наиболее общий способ повышения производительности, а синтаксический анализ - для уменьшения потребления места.
Определенное преимущество @Edenia Type int состоит в том, что он считается наиболее "естественным" и эффективным типом для выполнения арифметических операций. Любой другой, менее "естественный" тип может потребовать дополнительных преобразований или менее эффективной арифметики. Я думаю, вы это знали, поэтому, если вы спрашиваете, есть ли у Другие менее очевидные преимущества, я думаю, ответ будет: «Нет, просто« естественность »».
Да, хорошо, я удивлен, почему не многие люди задаются вопросом, почему все используют целые числа в книгах, учебных пособиях, проектах с открытым исходным кодом, в то время как существуют другие типы данных, предназначенные для более низких значений. Для языков с более высокой абстракцией, таких как Object Pascal, это еще более очевидно.
@Edenia, потому что диапазон значений, которые могут храниться в целочисленных типах, не применяется во время выполнения. Так что в абстракции нет преимущества.
Большинство так называемых дополнительных инструкций, упомянутых здесь, встроены в процессоры и не отличаются по размеру или количеству от кода, который будет выдан для int. Например, загрузка 32-битного регистра из 16-битного места с расширением знака или наоборот с усечением высокого порядка.
@EJP Верно. Вот почему я сказал: «Это в некоторой степени упрощенный аргумент», и почему я вставил «предположительно» в «предполагается, что это самый простой тип для арифметических операций», и почему я вставил «обычно» в «обычно» действительно делает использование обычного int выгодным ». Но я должен добавить еще несколько слов по этим строкам; Спасибо за напоминание.
А? Вы заявили без оговорок, что «комплилер должен выдавать код» и «ваш код больше и медленнее». Я не занимался этим серьезно тридцать лет, но тогда это было неправдой, и это неправда сейчас.
@EJP Ну, я тоже занимаюсь этим уже не менее 30 лет, хотя я и не следовал своему собственному совету, поскольку я никогда не проводил никаких тщательных измерений, чтобы подтвердить эффект. (Я просто повторял партийную линию, и это одна из причин, по которой я сказал: «Находясь в Риме, делайте, как римляне».) Но я только что выполнил несколько измерений, и действительно, используя shortделает, сделал код больше . Скоро опубликую результаты.
Я скептически отнесся к утверждению, что короткий код должен быть медленнее и больше в любом значительном смысле (при условии, что здесь локальные переменные, никаких споров о больших массивах, где short определенно окупаются, если это необходимо), поэтому я попытался заменить его на моем Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz
Я использовал (long.c):
long long_f(long A, long B)
{
//made up func w/ a couple of integer ops
//to offset func-call overhead
long r=0;
for(long i=0;i<10;i++){
A=3*A*A;
B=4*B*B*B;
r=A+B;
}
return r;
}
в версии на основе long, int и short (%s/long/TYPE/g) построил программу с gcc и clang в -O3 и -Os и измерил размеры и время выполнения для 100 мил запусков каждой из этих функций.
f.h:
#pragma once
int int_f(int A, int B);
short short_f(short A, short B);
long long_f(long A, long B);
main.c:
#include "f.h"
#include <stdlib.h>
#include <stdio.h>
#define CNT 100000000
int main(int C, char **V)
{
int choose = atoi(V[1]?:"0");
switch(choose){
case 0:
puts("short");
for(int i=0; i<CNT;i++)
short_f(1,2);
break;
case 1:
puts("int");
for(int i=0; i<CNT;i++)
int_f(1,2);
break;
default:
puts("long");
for(int i=0; i<CNT;i++)
long_f(1,2);
}
}
строить:
#!/bin/sh -eu
time(){ command time -o /dev/stdout "$@"; }
for cc in gcc clang; do
$cc -Os short.c -c
$cc -Os int.c -c
$cc -Os long.c -c
size short.o int.o long.o
$cc main.c short.o int.o long.o
echo $cc -Os
time ./a.out 2
time ./a.out 1
time ./a.out 0
$cc -O3 short.c -c
$cc -O3 int.c -c
$cc -O3 long.c -c
size short.o int.o long.o
$cc main.c short.o int.o long.o
echo $cc -O3
time ./a.out 2
time ./a.out 1
time ./a.out 0
done
Я проделал это дважды, и результаты оказались стабильными.
text data bss dec hex filename
79 0 0 79 4f short.o
80 0 0 80 50 int.o
87 0 0 87 57 long.o
gcc -Os
long
3.85user 0.00system 0:03.85elapsed 99%CPU (0avgtext+0avgdata 1272maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
int
4.78user 0.00system 0:04.78elapsed 99%CPU (0avgtext+0avgdata 1220maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
short
3.36user 0.00system 0:03.36elapsed 99%CPU (0avgtext+0avgdata 1328maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
text data bss dec hex filename
137 0 0 137 89 short.o
109 0 0 109 6d int.o
292 0 0 292 124 long.o
gcc -O3
long
3.90user 0.00system 0:03.90elapsed 99%CPU (0avgtext+0avgdata 1220maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
int
1.22user 0.00system 0:01.22elapsed 99%CPU (0avgtext+0avgdata 1260maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
short
1.62user 0.00system 0:01.62elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
text data bss dec hex filename
83 0 0 83 53 short.o
79 0 0 79 4f int.o
88 0 0 88 58 long.o
clang -Os
long
3.33user 0.00system 0:03.33elapsed 99%CPU (0avgtext+0avgdata 1316maxresident)k
0inputs+0outputs (0major+71minor)pagefaults 0swaps
int
3.02user 0.00system 0:03.03elapsed 99%CPU (0avgtext+0avgdata 1316maxresident)k
0inputs+0outputs (0major+71minor)pagefaults 0swaps
short
5.27user 0.00system 0:05.28elapsed 99%CPU (0avgtext+0avgdata 1236maxresident)k
0inputs+0outputs (0major+69minor)pagefaults 0swaps
text data bss dec hex filename
110 0 0 110 6e short.o
219 0 0 219 db int.o
279 0 0 279 117 long.o
clang -O3
long
3.57user 0.00system 0:03.57elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+69minor)pagefaults 0swaps
int
2.86user 0.00system 0:02.87elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+68minor)pagefaults 0swaps
short
1.38user 0.00system 0:01.38elapsed 99%CPU (0avgtext+0avgdata 1204maxresident)k
0inputs+0outputs (0major+70minor)pagefaults 0swaps
Результаты довольно близки, но они довольно сильно различаются в зависимости от разных компиляторов и настроек компилятора.
Я пришел к выводу, что выбор между int и short в теле функции или сигнатуре (массивы - другая проблема), потому что один должен работать лучше другого или генерировать более плотный код, в основном бесполезен (по крайней мере, в коде, который не привязан к конкретному компилятор с определенными настройками). Либо быстро, поэтому я бы выбрал тот тип, который лучше соответствует семантике моей программы или лучше передает мой API.
(Если я ожидаю короткого положительного значения, можно также использовать в подписи uchar или ushort.)
Программисты на C предрасположены использовать int, потому что C исторически их одобрял (целочисленные литералы имеют тенденцию быть int, рекламные акции имеют тенденцию создавать int, раньше были неявные правила int для объявлений и необъявленных функций и т. хорошо подходит для архитектуры, но, в конце концов, важен плотный, производительный машинный код с читаемым, поддерживаемым исходным кодом, и если ваша теория для выполнения чего-либо в исходном коде явно не способствует хотя бы одному из этих голы, думаю, это плохая теория.
Если вы сравните эти типы на микроконтроллерной системе, разница будет очень очевидна. В среднем 8-битный unsigned char может дать одну инструкцию, тогда как unsigned short дает от 5 до 10 инструкций, а unsigned long - около 100 инструкций.
@Lundin звучит как еще одна причина использовать наиболее подходящий тип вместо int
Также enums неявно запрашиваются у int.
Здесь есть несколько проблем.
Во-первых, тип char совершенно не подходит для хранения целочисленных значений. Его следует использовать только для удержания персонажей. Это связано с тем, что он имеет подпись, определяемую реализацией, char фактически является отдельным типом, отдельным от signed char и unsigned char. См. По умолчанию char подписан или неподписан?.
Однако основная причина, по которой следует избегать использования небольших целочисленных типов, таких как char и short, по возможности, заключается в неявном продвижении типов. Эти типы подлежат целочисленному продвижению, что, в свою очередь, может привести к опасным вещам, таким как тихая смена подписи. Подробнее см. Правила повышения неявного типа.
По этой причине некоторые стандарты кодирования прямо запрещают использование целочисленных типов меньшего размера. Хотя для того, чтобы такое правило было осуществимо, вам понадобится 32-битный процессор или больше. Так что это не совсем хорошее универсальное решение, если нужно учитывать разные микроконтроллеры.
Также обратите внимание, что такое микроуправление памятью в основном актуально только при программировании встроенных систем. Если вы программируете программы для ПК, использование меньших типов для экономии памяти, вероятно, является «преждевременной оптимизацией».
Стандартные "примитивные типы данных" языка C, включая char, short, int, в целом не переносимы. Они могут меняться в размере при переносе кода, что, в свою очередь, придает им недетерминированное поведение. Кроме того, C допускает всевозможные неясные и экзотические форматы подписи для этих типов, такие как дополнение, знак и величина, биты заполнения и т. д.
Надежный, переносимый и качественный код вообще не использует эти типы, а использует типы stdint.h. В качестве бонуса эта библиотека допускает только нормальное отраслевое дополнение до двух.
Использование меньших целочисленных типов для экономии места не является хорошей идеей по всем вышеупомянутым причинам. Опять же, stdint.h предпочтительнее. Если вам нужен универсальный тип, который переносимо экономит память, если экономия памяти не означает снижение скорости выполнения, используйте int_fast8_t и аналогичные. Это будет 8 бит, если использование большего типа не означает более быстрое выполнение.
Использование
shortимеет смысл, когда возможен большой массив объектов, иначе есть небольшое преимущество передint.