Questions about types

Pages: 12
Hello, I spent 4 days trying to wrap my understanding about types, but It seems I am hitting a big fat wall of frustration.

I used to know that char is not necessarily 1 byte, it changes sizes on other hardware. But lately i discover that int and even bool also changes sizes under some hardware.

Those are all the built-in arithmetic types that I know:
float, double, bool, int, char, char8_t, char16_t, char32_t and wchar_t.

1: Are there any other arithmetic types?

2: Are all those types variable-width types? if some of them are fixed-width please site them.

Many other questions to come

Thanks for reading.
1) short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long Some implementations have 128 bit wide arithmetic types.

2) No. Those that contain a number are fixed width. i.e. That many bits wide.
The others are generally "implementation defined", with regard to size.

Last edited on
Thanks @AbstractionAnon, but I think long, short, signed and unsigned are type modifiers not types am I wrong here?

Those that contain a number are fixed width

I do not think so. int8_t, int16_t and int32_t that are fixed-width types. not the charX_t which are a fundamental types. Am I wrong here too?

Last edited on
1) There's size_t which is either unsigned long (32 bit) or unsigned long long (64 bit) depending upon whether compiling for 32 or 64 bits. This is the type returned by .size(), .length(), std::size() etc.

2) ptrdiff_t is the signed version of size_t and is used for the result of pointer subtraction and also the result of std::ssize().

but these aren't fundamental types but based upon others.

For MS, see
https://docs.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170

there are well over 50 names for the integers, a lot more if you have extensions like microsoft in play.

there are basically 1,2,4, and 8 byte integers, in signed and unsigned configurations, but there are many, many names for those. Beyond that are float, double, and long double for floating point, with some platforms offering a few more (rare). Pointers are technically integers.

2) all c++ integers are fixed width. The width varies from compiler to compiler in some cases (eg int may be 32 bits here an 64 bits there) but it never changes on the same platform (platform is a mix of OS, CPU, and compiler).

the size of integers is usually not a problem, eg no one cares if the for loop from 0 to 100 uses 1,2,4, or 8 bytes in any flavor of signed. When it IS a problem, you can try the known size ones like int64_t. But even for those, you may want to verify the size is what you want it to be.

bool can even be cut to 1 bit, and that is a possible optimization for vectors of them (see online dissertations on the subject).

you can force sizes somewhat using a bitset or bitfield (special define in a struct or class) and similar tools if you need to go there.
Last edited on
Having read all that's been posted so far, I think you're questioning integral types, and not types in general. So why so many integral types?

Before C++ there was C, and before C there was B.

B had one type, the machine word. You know how we describe computers as 32bit or 64bit ... well that's the machine word size; the number of bits processed together.

A change in computer architecture, the use of 8bit memory in 16bit computers, made B untenable, as it could only address even bytes. So the language was redesigned, with new fixed types added. The types were char to represent a byte, int to represent a word, with short and long variations, a floating point number type and a double precision floating point type.

Since then, programmers have wanted specific control of the integral types. For example, how large is a short or long? Somehow, saying sizeof(short) <= sizeof(long) isn't satisfactory. Sometimes you just want a 32 bit signed integral type, so int32_t was introduced to give you that. It's just some typedef defined for that platform, it isn't a new fundamental type. That pretty much applies to the plethora of standard integral types defined in the C and C++ standard libraries (stdint.h/cstdint).
Thanks @kbw. I like everything you wrote in this quote:
Before C++ there was C....standard libraries (stdint.h/cstdint).
. I understand now how those types comes to life to torture me after, and why they are so many of them.

But here:

Having read all that's been posted so far, I think you're questioning integral types, and not types in general.
. No, I am questioning about arithmetic type(integral included).

So why so many integral types?
. I do not ask for that. What I am asking is this:

I know that basic built-in types are:
float, double, bool, int, char, char8_t, char16_t, char32_t and wchar_t.

The list gets bigger when type modifiers preceding some of them. which I just learn that they are also called arithmetic variables, thanks to @AbstractionAnon and @JLBorges

I want to know if there is any basic built-in arithmetic types left that I do not know about. without type modifiers.

Then I want to know all arithmetic types with type modifier preceding them

Then I want to check whether all of them change sizes based on some hardware, or only some of them that change size, and I wanna make a table on that.

Last edited on
2) all c++ integers are fixed width. The width varies from compiler to compiler in some cases (eg int may be 32 bits here an 64 bits there) but it never changes on the same platform (platform is a mix of OS, CPU, and compiler).

I like this information, Thanks @jonnin

I am currently using Windows 10 as OS, two compilers C:B and MS VS. my sizeof(int)=4; in C:B is that means that I do not need to check the sizeof(int) in MS VC, and assume with confidence that It will be 4 too, because It is the same OS. Or another compiler might choose its own int size?

Here
there are basically 1,2,4, and 8 byte integers
. I think you mean 6 not 8 am I right?
Last edited on
> all c++ integers are fixed width.

In a C++ context, the term 'fixed width integer types' means 'integer types having specified widths';
these are the type aliases in <cstdint> https://en.cppreference.com/w/cpp/types/integer


> assume with confidence that It will be 4 too, because It is the same OS.
> Or another compiler might choose its own int size?

Unlikely, but in theory, another compiler can choose its own int size; the standard permits it.

These are the commonly found data models: https://en.cppreference.com/w/cpp/language/types#Data_models
Last edited on
On Windows, at least in the past, it wasn't uncommon to compile and run 32-bit applications on 64-bit system. int would be the same size regardless but the size of things like pointers and std::size_t would differ.
there are basically 1,2,4, and 8 byte integers
. I think you mean 6 not 8 am I right?


No. 8 is correct. 1 byte is 8 bits, 2 bytes 16 bits, 4 is 32 and 8 is 64 bits. 6 bytes would be 48 bits - which isn't a very common bit size (although not impossible for some niche cpu).
It doesn't seem like the standard uses the word "type modifier". I think it's more useful to just think about the actual types (where the type names might be made up of those so called "type modifiers").


type name                minimum size    common size
signed char                8 bits          8 bits
unsigned char              8 bits          8 bits
char                       8 bits          8 bits
(signed) short (int)      16 bits         16 bits 
unsigned short (int)      16 bits         16 bits
(signed) (int)            16 bits         32 bits
unsigned (int)            16 bits         32 bits
(signed) long (int)       32 bits         32 or 64 bits 
unsigned long (int)       32 bits         32 or 64 bits 
(signed) long long (int)  64 bits         64 bits
unsigned long long (int)  64 bits         64 bits


The words in the type names that are written inside parentheses are optional and can be left out. For signed int you can leave out either signed or int but you cannot leave out both for obvious reasons.

All the types listed above are separate types. Types listed on separate lines are always different types even if they happen to have the same size and signedness. Most notable is that char, which is often signed but could be unsigned, is always a separate type from both signed char and unsigned char.


C++ tries to be very cross platform and support old, future and sometimes non-existent hardware by not specifying everything exactly. But that doesn't mean you cannot make more specific assumptions. If you're using a mainstream modern computer, and not some very specialized embedded device or old hardware, you can essentially rely on the "common sizes" listed above to be true.

A few notes:

- sizeof(char) is always 1 because that's how sizeof works. There's no type smaller than char. The size of all other types are an integer multiple of char. If char is 8 bits then you can't have types that are 12 bits. It has to be 8, 16, 24, 32, etc.

- The 8-bit byte is a very universal thing in modern computers. But C++ allow char to be larger, but that would mean there is no 8-bit type, which would make it incompatible with a lot of things. Dealing with byte formats (e.g. UTF-8) would be impossible, or at least very cumbersome. It's probably the main reason why the fixed-width integer types in <cstdint> are "optional". You might have been able to find these computers a long time ago but nowadays we use 8-bit bytes so the size of char will therefore be 8 bits in practice.

- On modern 32 and 64-bit computers the size of int is typically 32 bits. I don't think you will ever find it being smaller than that but I wouldn't rule out that it might be larger somewhere. On old 16-bit computers the size of int was 16 bits but those are not common today. That said, at least up to recently, it was extremely common in at least one big Asian country to use Turbo C++ (an ancient 16-bit compiler) with old DOS interface in their programming education for some reason but I'm not sure how likely you are to use something like that for anything serious nowadays.

- Integer types defined by the standard library (e.g. std::size_t, std::ptrdiff_t and types in <cstdint>) are usually typedefs of the types listed above. For example std::uint8_t is usually unsigned char (but it doesn't have to be).
Last edited on
seeplus wrote:
6 bytes would be 48 bits - which isn't a very common bit size (although not impossible for some niche cpu).

I agree this would be very unusual, but it wouldn't be impossible to implement even on a non-niche CPU. It could be done partially in software similar to how 64-bit integers were supported on old 32-bit computers despite there being no 64-bit registers.

Last edited on
No. 8 is correct. 1 byte is 8 bits, 2 bytes 16 bits, 4 is 32 and 8 is 64 bits. 6 bytes would be 48 bits - which isn't a very common bit size (although not impossible for some niche cpu).


Thanks @seeplus, I do not know why, but for some reason I thought 64bit is 6 bytes((

JLBorges wrote
Unlikely, but in theory, another compiler can choose its own int size; the standard permits it.

Even in the same OS?
JLBorges wrote:
Unlikely, but in theory, another compiler can choose its own int size; the standard permits it.
ninja01 wrote:
Even in the same OS?

Yes.

The standard doesn't talk about "operating systems". It talks about "implementations" which is essentially a combination of compiler, standard library implementation, operating system and hardware that makes up the thing that can validate the program, show "diagnostics" when required, and run the program.

Using a different compiler would essentially mean you have a different "implementation". Sometimes even compiler flags could change the size of types.
Last edited on
> 1 byte is 8 bits, , 2 bytes 16 bits ...

A byte is an octet in practice. But other than specifying that it must be at least 8 bits, C++ leaves gives a choice to the implementation.

The fundamental storage unit in the C++ memory model is the byte. A byte ... is composed of a contiguous sequence of bits, the number of which is implementation-defined.
Note: The number of bits in a byte is reported by the macro CHAR_­BIT in the header <climits>.
https://eel.is/c++draft/intro.memory#1



> Even in the same OS?

Yes. For an example, check the size of long double
It doesn't seem like the standard uses the word "type modifier".
wow I checked and you're right. There is not even a single word of modifier in the whole 1345 pages, of The c++ programming language. BS

I just notice these are some left overs of an un-recommanded book that I've already read "c++ from the ground up" 3th edition

So I apologize about using these type modifier non-sense. I will edit all my posts.

Thanks @peter for this info
ninja01 wrote:
There is not even a single word of modifier in the whole 1345 pages, of The c++ programming language. BS

The book "The C++ Programming Language" is not the standard. The real standard contains the word "modifier" in a few places.

ninja01 wrote:
I apologize about using these type modifier non-sense.

It's not nonsense and you don't need to apologise. We don't need to use the same language as the standard. Even cppreference.com uses the word "modifier" to describe them ( https://en.cppreference.com/w/cpp/language/types ).

What I meant was just that I don't think it's very useful to think of it as int being the base type and these "modifiers" are being used to modify the int type. Because sometimes they don't change anything, sometimes the type change but the size and everything else is still the same, and sometimes you can use a modifier on its own.

Instead I find it easier to just think, "here are the types", "here is how you can spell them", and "here is what they mean".
Last edited on
Interesting conversation.
All is related at this Microsoft main page (and the next one) :
https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170
https://en.cppreference.com/w/cpp/language/types

I remember that I played with this little code so as display data types size and min max values. Just a sharing ++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <limits>

template<typename T>
void printLimits() {
	std::cout << " : Range -> " << std::numeric_limits<T>::min() << " * ";
	std::cout << std::numeric_limits<T>::max() << std::endl;
}

int main() 
{	// char
	std::cout << "CHAR \t\t\t" << (CHAR_BIT * sizeof(char)) << " bits" << std::endl;
	// signed int
	std::cout << "SHORT INT \t\t" << sizeof(short) << " bytes";
	printLimits<short>();
	std::cout << "INT \t\t\t" << sizeof(int) << " bytes";
	printLimits<int>();
	std::cout << "LONG INT \t\t" << sizeof(long) << " bytes"; // the same
	printLimits<long>();
	std::cout << "LONG LONG INT \t\t" << sizeof(long long) << " bytes";
	printLimits<long long>();
	// unsigned int
	std::cout << "UNSIGNED SHORT INT \t" << sizeof(unsigned short) << " bytes";
	printLimits<unsigned short>();
	std::cout << "UNSIGNED INT \t\t" << sizeof(unsigned int) << " bytes";
	printLimits<unsigned int>();
	std::cout << "UNSIGNED LONG INT \t" << sizeof(unsigned long) << " bytes"; // the same
	printLimits<unsigned long>();
	std::cout << "UNSIGNED LONG LONG INT \t" << sizeof(unsigned long long int) << " bytes";
	printLimits<unsigned long long>();
	// float
	std::cout << "FLOAT \t\t\t" << sizeof(float) << " bytes";
	printLimits<float>();
	std::cout << "DOUBLE FLOAT \t\t" << sizeof(double) << " bytes";
	printLimits<double>();
}


CHAR                    1 bytes
SHORT INT               2 bytes : Range -> -32768 * 32767
INT                     4 bytes : Range -> -2147483648 * 2147483647
LONG INT                4 bytes : Range -> -2147483648 * 2147483647
LONG LONG INT           8 bytes : Range -> -9223372036854775808 * 9223372036854775807
UNSIGNED SHORT INT      2 bytes : Range -> 0 * 65535
UNSIGNED INT            4 bytes : Range -> 0 * 4294967295
UNSIGNED LONG INT       4 bytes : Range -> 0 * 4294967295
UNSIGNED LONG LONG INT  8 bytes : Range -> 0 * 18446744073709551615
FLOAT                   4 bytes : Range -> 1.17549e-38 * 3.40282e+38
DOUBLE FLOAT            8 bytes : Range -> 2.22507e-308 * 1.79769e+308


Last edited on
Pages: 12