Why the size of a pointer is 4bytes in C++ - c++

On the 32-bit machine, why the size of a pointer is 32-bit? Why not 16-bit or 64-bit? What's the cons and pros?

Because it mimics the size of the actual "pointers" in assembler. On a machine with a 64 bit address bus, it will be 64 bits. In the old 6502, it was an 8 bit machine, but it had 16 bit address bus so that it could address 64K of memory. On most 32 bit machines, 32 bits were enough to address all the memory, so that's what the pointer size was in C++. I know that some of the early M68000 series chips only had a 24 bit memory address space, but it was addressed from a 32 bit register so even on those the pointer would be 32 bits.
In the bad old days of the 80286, it was worse - there was a 16 bit address register, and a 16 bit segment register. Some C++ compilers didn't hide that from you, and made you declare your pointers as near or far depending on whether you wanted to change the segment register. Mercifully, I've recycled most of those brain cells, so I forget if near pointers were 16 bits - but at the machine level, they would be.

The size of a pointer in C++ is implementation-defined. C++ might run on anything from your toaster's chip up to huge mainframes. Different architectures require different sizes of the data types.
If on your implementation a pointer is 32bit, then that's very likely an architecture which can address 2^32 bytes. (Note that even the size of bytes might be different depending on the implementation.) 64bit architectures generally can address 2^64 bytes, so implementations on these architectures will likely have a pointer size of 64bit.

16 bit would obviously be insufficient - you could only address 64K then.
Why not emulate 64 bit on 32 bit systems - I guess because the performance of pointer arithmetic would degrade.

As mentioned in many other answers, the size of a pointer need not be 32-bits - the implementation will set the size of a pointer to be whatever the architecture of the platform dictates. On a system with 64-bit addressing, the size of a pointer will generally be 64-bits.
However, you should also note that even on a single implementation, different types of pointers might have different sizes. In particular, pointer-to-member types (which I'll grant are odd-ball pointers) may have different sizes than plain-old pointers to objects.
The same is true about pointers to plain old functions - they might have a different size than pointers to objects (this applies to C as well as C++). However on modern desktop systems you'll usually find that pointers to functions are the same size as pointers to objects.
Here's a short example of fun with pointer-to-member-functions:
#include <stdio.h>
class A {};
class B {};
class VirtD: public virtual A, public virtual B {
public:
virtual int Dfunc() { return 5; };
};
typedef int (VirtD::* Derived_mfp)();
int main()
{
VirtD virtd;
Derived_mfp mfp = &VirtD::Dfunc;
printf( "sizeof( mfp) == %u\n", (unsigned int) sizeof( mfp));
}
Displays: sizeof( mfp) == 12 on MSVC.

The size of the pointer has little to do with the architecture(32bit, 64bit). 32bit usually refers to the fact that the register size is 32bit. As a result, the maximum possible number of address that you can address using one register is 2^32. So, it boils down to efficiency of addressing the memory slots using a register.

With a 32-bit pointer you can point to a wider range of memory than with 16-bit pointers. When 32-bit pointers were standardized, 64-bit CPUs were not very popular (or even existent?). Therefor a pointer would not be able to fit inside the CPU register, which is a very important factor for speed.

Why not 16-bit? Because, presuming a flat 32-bit address space, you cannot address every byte. Far from it: you can only address 216 unique locations with a 16-bit pointer. Even if your pointers only point to dwords and not bytes, this still leaves 1073676288 dwords unaddressable.
Assuming a flat 32-bit address space, you can already address every single byte with a 32-bit pointer. At this point, 64-bit pointers are just wasting space, unless you want to add additional information to each pointer. For example, on 32-bit PowerPC, a function descriptor is actually a 96-bit entity, with one third pointing to the executable code and the rest being data that helps make relocating modules easier.
In a segmented address space, having larger-than-32-bit pointers to data could be useful. Windows NT on the DEC Alpha was a 32-bit operating system, but the Alpha hardware was 64-bit capable. Your ordinary address space was still 32-bit, but there were special APIs to allow 32-bit programs to access 64-bit addresses, as if they were in otherwise-inaccessible segments.

To answer your question: C++ itself says very little about the size of a pointer, and certainly not that it has to be 32 bits or anything. The size of a pointer should be the natural one for the machine architecture.

Related

size of pointers and architecture

By conducting a basic test by running a simple C++ program on a normal desktop PC it seems plausible to suppose that sizes of pointers of any type (including pointers to functions) are equal to the target architecture bits ?
For example: in 32 bits architectures -> 4 bytes and in 64 bits architectures -> 8 bytes.
However I remember reading that, it is not like that in general!
So I was wondering what would be such circumstances?
For equality of size of pointers to data types compared with size of pointers
to other data types
For equality of size of pointers to data types compared with size of pointers
to functions
For equality of size of pointers to target architecture
No, it is not reasonable to assume. Making this assumption can cause bugs.
The sizes of pointers (and of integer types) in C or C++ are ultimately determined by the C or C++ implementation. Normal C or C++ implementations are heavily influenced by the architectures and the operating systems they target, but they may choose the sizes of their types for reasons other than execution speed, such as goals of supporting lower memory use (smaller pointers means less memory used in programs with lots of pointers), supporting code that was not written to be fully portable to any type sizes, or supporting easier use of big integers.
I have seen a compiler targeted for a 64-bit system but providing 32-bit pointers, for the purpose of building programs with smaller memory use. (It had been observed that the sizes of pointers were a considerable factor in memory consumption, due to the use of many structures with many connections and references using pointers.) Source code written with the assumption that the pointer size equalled the 64-bit register size would break.
It is reasonable to assume that in general sizes of pointers of any type (including pointers to functions) are equal to the target architecture bits?
Depends. If you're aiming for a quick estimate of memory consumption it can be good enough. But not if your programs correctness depends on it.
(including pointers to functions)
But here is one important remark. Although most pointers will have the same size, function pointers may differ. It is not guaranteed that a void* will be able to hold a function pointer. At least, this is true for C. I don't know about C++.
So I was wondering what would be such circumstances if any?
It can be tons of reasons why it differs. If your programs correctness depends on this size it is NEVER ok to do such an assumption. Check it up instead. It shouldn't be hard at all.
You can use this macro to check such things at compile time in C:
#include <assert.h>
static_assert(sizeof(void*) == 4, "Pointers are assumed to be exactly 4 bytes");
When compiling, this gives an error message:
$ gcc main.c
In file included from main.c:1:
main.c:2:1: error: static assertion failed: "Pointers are assumed to be exactly 4 bytes"
static_assert(sizeof(void*) == 4, "Pointers are assumed to be exactly 4 bytes");
^~~~~~~~~~~~~
If you're using C++, you can skip #include <assert.h> because static_assert is a keyword in C++. (And you can use the keyword _Static_assert in C, but it looks ugly, so use the include and the macro instead.)
Since these two lines are so extremely easy to include in your code, there's NO excuse not to do so if your program would not work correctly with the wrong pointer size.
It is reasonable to assume that in general sizes of pointers of any type (including pointers to functions) are equal to the target architecture bits?
It might be reasonable, but it isn't reliably correct. So I guess the answer is "no, except when you already know the answer is yes (and aren't worried about portability)".
Potentially:
systems can have different register sizes, and use different underlying widths for data and addressing: it's not apparent what "target architecture bits" even means for such a system, so you have to choose a specific ABI (and once you've done that you know the answer, for that ABI).
systems may support different pointer models, such as the old near, far and huge pointers; in that case you need to know what mode your code is being compiled in (and then you know the answer, for that mode)
systems may support different pointer sizes, such as the X32 ABI already mentioned, or either of the other popular 64-bit data models described here
Finally, there's no obvious benefit to this assumption, since you can just use sizeof(T) directly for whatever T you're interested in.
If you want to convert between integers and pointers, use intptr_t. If you want to store integers and pointers in the same space, just use a union.
Target architecture "bits" says about registers size. Ex. Intel 8051 is 8-bit and operates on 8-bit registers, but (external)RAM and (external)ROM is accessed with 16-bit values.
For correctness, you cannot assume anything. You have to check and be prepared to deal with weird situations.
As a general rule of thumb, it is a reasonable default assumption.
It's not universally true though. See the X32 ABI, for example, which uses 32bit pointers on 64bit architectures to save a bit of memory and cache footprint. Same for the ILP32 ABI on AArch64.
So, for guesstimating memory use, you can use your assumption and it will often be right.
It is reasonable to assume that in general sizes of pointers of any type (including pointers to functions) are equal to the target architecture bits?
If you look at all types of CPUs (including microcontrollers) currently being produced, I would say no.
Extreme counterexamples would be architectures where two different pointer sizes are used in the same program:
x86, 16-bit
In MS-DOS and 16-bit Windows, a "normal" program used both 16- and 32-bit pointers.
x86, 32-bit segmented
There were only a few, less known operating systems using this memory model.
Programs typically used both 32- and 48-bit pointers.
STM8A
This modern automotive 8-bit CPU uses 16- and 24-bit pointers. Both in the same program, of course.
AVR tiny series
RAM is addressed using 8-bit pointers, Flash is addressed using 16-bit pointers.
(However, AVR tiny cannot be programmed with C++, as far as I know.)
It's not correct, for example DOS pointers (16 bit) can be far (seg+ofs).
However, for the usual targets (Windows, OSX, Linux, Android, iOS) then it's correct. Because they all use the flat programming model which relies on paging.
In theory, you can also have systems which uses only the lower 32 bits when in x64. An example is a Windows executable linked without LARGEADDRESSAWARE. However this is to help the programmer avoid bugs when switching to x64. The pointers are truncated to 32 bits, but they are still 64 bit.
In x64 operating systems then this assumption is always true, because the flat mode is the only valid one. Long mode in CPU forces GDT entries to be 64 bit flat.
One also mentions a x32 ABI, I believe it is based on the same paging technology, forcing all pointers to be mapped to the lower 4gb. However this must be based to the same theory as in Windows. In x64 you can only have flat mode.
In 32 bit protected mode you could have pointers up to 48 bits. (Segmented mode). You can also have callgates. But, no operating system uses that mode.
Historically, on microcomputers and microcontrollers, pointers were often wider than general-purpose registers so that the CPU could address enough memory and still fit within the transistor budget. Most 8-bit CPUs (such as the 8080, Z80 or 6502) had 16-bit addresses.
Today, a mismatch is more likely to be because an app doesn’t need multiple gigabytes of data, so saving four bytes of memory on every pointer is a win.
Both C and C++ provide separate size_t, uintptr_t and off_t types, representing the largest possible object size (which might be smaller than the size of a pointer if the memory model is not flat), an integral type wide enough to hold a pointer, and a file offset (often wider than the largest object allowed in memory), respectively. A size_t (unsigned) or ptrdiff_t (signed) is the most portable way to get the native word size. Additionally, POSIX guarantees that the system compiler has some flag that means a long can hold any of these, but you cannot always assume so.
Generally pointers will be size 2 on a 16-bit system, 3 on a 24-bit system, 4 on a 32-bit system, and 8 on a 64-bit system. It depends on the ABI and C implementation. AMD has long and legacy modes, and there are differences between AMD64 and Intel64 for Assembly language programmers but these are hidden for higher level languages.
Any problems with C/C++ code is likely to be due to poor programming practices and ignoring compiler warnings. See: "20 issues of porting C++ code to the 64-bit platform".
See also: "Can pointers be of different sizes?" and LRiO's answer:
... you are asking about C++ and its compliant implementations, not some specific physical machine. I'd have to quote the entire standard in order to prove it, but the simple fact is that it makes no guarantees on the result of sizeof(T*) for any T, and (as a corollary) no guarantees that sizeof(T1*) == sizeof(T2*) for any T1 and T2).
Note: Where is answered by JeremyP, C99 section 6.3.2.3, subsection 8:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
In GCC you can avoid incorrect assumptions by using built-in functions: "Object Size Checking Built-in Functions":
Built-in Function: size_t __builtin_object_size (const void * ptr, int type)
is a built-in construct that returns a constant number of bytes from ptr to the end of the object ptr pointer points to (if known at compile time). To determine the sizes of dynamically allocated objects the function relies on the allocation functions called to obtain the storage to be declared with the alloc_size attribute (see Common Function Attributes). __builtin_object_size never evaluates its arguments for side effects. If there are any side effects in them, it returns (size_t) -1 for type 0 or 1 and (size_t) 0 for type 2 or 3. If there are multiple objects ptr can point to and all of them are known at compile time, the returned number is the maximum of remaining byte counts in those objects if type & 2 is 0 and minimum if nonzero. If it is not possible to determine which objects ptr points to at compile time, __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0 for type 2 or 3.

Range of pointer values on 64 bit systems

Recently I was reading about the small string optimization (SSO): What are the mechanics of short string optimization in libc++?. As we know, a string typically consists of 3 pointers, which is 24 bytes on a 64 bit system. The linked answer says that in libc++'s implementation, the very first bit of the first pointer is used to indicate whether the string is in "long" or "short" mode, i.e. heap allocation and external storage vs internal storage of up to some 22 characters.
This however assumes however that the first bit of the first pointer cannot ever meaningfully be part of the address, because whenever the string is in "long" mode, that bit will always be set (or unset, depending which convention was chosen). This seems reasonable on its face, since with 64 bit pointers that allows 2^64 addresses, larger than 1 followed by 18 zeroes in bytes, or more than 1 billion gigabytes.
So this is reasonable, though not certain. My question is: is this guaranteed somewhere? And if it is guaranteed, where is it guaranteed? By the architecture spec, or by something else? To take it a step further: how many bits is it safe to do this with? I have a vague recollection reading somewhere that only 48 bits are used, but I don't recall.
If there are some number of bits, e.g. 8 or 16 that are guaranteed to be untouched, that is certainly something that could be leveraged in some interesting ways. It would be nice to exploit this, but not at the cost of having code mysteriously failure on some machine.
As we know, a string typically consists of 3 pointers, which is 24 bytes on a 64 bit system.
This is not true with libc++. The __long structure, for "long strings" is defined as:
struct __long
{
size_type __cap_;
size_type __size_;
pointer __data_;
};
The short flag therefore goes into the capacity field, making the whole thing moot.
As for pointer tagging, there is no universal guarantee about the size of a pointer. On x86_64, the data structures that the CPU uses for virtual address translation only use 48 bits (or 52 with physical address extension), so virtual addresses never use the upper 16 (or 12) bits. Additionally, most operating systems map their kernel into every process and reserve some amount of the high end of the address space for it, so in practice, user-mode pointers are even more restricted. On Windows, the most significant hardware-usable bit of a pointer tells whether it belongs to kernel-space or user-space.
These limits can change in the future and will vary across platforms, so it would be bad form to use them in a platform-independent standard library. In general, it's much better practice to use the least-significant bits for pointer tagging, since your application is in control of these.
The "long-bit" isn't part of a pointer, but of the capacity:
struct __long
{
size_type __cap_;
size_type __size_;
pointer __data_;
};
The "trick" is that if you always allocate an even number of characters and reserve one for the nul terminator, the resulting capacity will always be an odd number. And you get the 1-bit for free!

Why does my compiler use an 8-bit char when I'm running on a 64-bit machine?

I am using the Microsoft Visual Studio 2013 IDE. When I compile a program in C++ while using the header <climits>, I output the macro constant CHAR_BIT to the screen. It tells me there are 8-bits in my char data type (which is 1-byte in C++). However, Visual Studio is a 32-bit application and I am running it on a 64-bit machine (i.e. a machine whose processor has a 64-bit instruction set and operating system is 64-bit Windows 7).
I don't understand why my char data type uses only 8-bits. Shouldn't it be using at least 32-bits (since my IDE is a 32-bit application), let alone 64-bits (since I'm compiling on a 64-bit machine)?
I am told that the number of bits used in a memory address (1-byte) depends on the hardware and implementation. If that's the case, why does my memory address still only use 8-bits and not more?
I think you are confusing memory address bit-width with data value bit-width. Memory addresses (pointers) are 32 bits for 32-bit programs and 64 bits for 64-bit programs. But data types have different widths for their values depending on type (as governed by the standard). So a char is 8-bits, but a char* will be 32-bits if you are compiling as a 32-bit application (also note here it depends on how you compile the application and not what type of processor or OS you are running on).
Edit for questions:
However, what is the relationship between these two?
Memory addresses will always have the same bit width regardless of what data value is stored there.
For example, if I have a 32-bit address and I assign an 8-bit value to that address, does that mean there are 24-bits of unused address space?
Some code (assume 32-bit compilation):
char i_am_1_byte = 0x00; // an 8-bit data value that lives in memory
char* i_am_a_ptr = &i_am_1_byte; // pointer is 32-bits and points to an 8-bit data value
*i_am_a_ptr = 0xFF; // writes 0xFF to the location pointed to by the pointer
// that is, to i_am_1_byte
So we have i_am_1_byte which is a char and takes up 8 bits somewhere in memory. We can get this memory location using the address-of operator & and store it in the pointer variable i_am_a_ptr, which is your 32-bit address. We can write 8 bits of data to the location pointed to be i_am_a_ptr by dereferencing it.
If not, what is the bit-width for memory address actually used for
All the data that your program uses must be located somewhere in memory and each location has an address. Most programs probably will not use most of the memory available for them to use, but we need a way to address every possible location.
how can having more memory address bit-width be helpful?
That depends on how much data you need to work with. A 32-bit program, at most, can address 4GB of memory space (and this may be smaller depending on your OS). That used to be a very, very large amount of memory, but these days it is conceivable a program could run out. It is also a lot easier for the CPU to address more the 4GB of RAM if it is 64-bit (this gets into the difference between physical memory and virtual memory). Of course, 64-bit architecture means a lot more than just bigger addresses and brings many benefits that may be more useful to programs than the bigger memory space.
An interesting fact is that on some processors, such as 32-bit ARM, mostly of their instructions are word aligned. That is, compilers tend to allocate 32-bits (4 bytes) to any data type, even though the data type used needs less than 4 bytes unless otherwise stated in the source code. This happens because ARM architectures are optimized to memory access using word alignment.

Adding a 1 bit flag in a 64 bit pointer

If you define a union for traversing a data structure squentially (start and end offset) or via a pointer to a tree structure depending on the number of data elements on a 64 bit system where these unions are aligned with cache lines, is there a possibility of both adding a one bit flag at one of those 64 bits in order to know which traversal must be used and still allowing to reconstruct the right pointer?
union {
uint32_t offsets[2];
Tree<NodeData> * tree;
};
It's system dependent, but I don't think any 64-bit system really uses its full pointer-length yet.
Also, if you know your data is 2n-aligned, chances are those n bits are just sitting idle there (On some old systems they just would not exist. But I don't think any of those were 64-bit systems, and anyway they are no longer of interest).
As an example, x86_64 uses 48bits, the upper 16 must be the same as bit47. (sign-extended)
Another example, ARM64 uses 49bits (2 mappings of 48bit at the same time), so there you only have 15 bits left.
Just remember to corect the pilfered bits. (You might want to use uintptr_t instead of a pointer, and convert after the correction.)
Using a mis-aligned or impossible pointer causes behavior ranging from silent auto-correction, ranging over silent mis-behavior, to loud crashes.

Is the sizeof(some pointer) always equal to four? [duplicate]

This question already has answers here:
Do all pointers have the same size in C++?
(10 answers)
Closed 8 months ago.
For example:
sizeof(char*) returns 4. As does int*, long long*, everything that I've tried. Are there any exceptions to this?
The guarantee you get is that sizeof(char) == 1. There are no other guarantees, including no guarantee that sizeof(int *) == sizeof(double *).
In practice, pointers will be size 2 on a 16-bit system (if you can find one), 4 on a 32-bit system, and 8 on a 64-bit system, but there's nothing to be gained in relying on a given size.
Even on a plain x86 32 bit platform, you can get a variety of pointer sizes, try this out for an example:
struct A {};
struct B : virtual public A {};
struct C {};
struct D : public A, public C {};
int main()
{
cout << "A:" << sizeof(void (A::*)()) << endl;
cout << "B:" << sizeof(void (B::*)()) << endl;
cout << "D:" << sizeof(void (D::*)()) << endl;
}
Under Visual C++ 2008, I get 4, 12 and 8 for the sizes of the pointers-to-member-function.
Raymond Chen talked about this here.
Just another exception to the already posted list. On 32-bit platforms, pointers can take 6, not 4, bytes:
#include <stdio.h>
#include <stdlib.h>
int main() {
char far* ptr; // note that this is a far pointer
printf( "%d\n", sizeof( ptr));
return EXIT_SUCCESS;
}
If you compile this program with Open Watcom and run it, you'll get 6, because far pointers that it supports consist of 32-bit offset and 16-bit segment values
if you are compiling for a 64-bit machine, then it may be 8.
Technically speaking, the C standard only guarantees that sizeof(char) == 1, and the rest is up to the implementation. But on modern x86 architectures (e.g. Intel/AMD chips) it's fairly predictable.
You've probably heard processors described as being 16-bit, 32-bit, 64-bit, etc. This usually means that the processor uses N-bits for integers. Since pointers store memory addresses, and memory addresses are integers, this effectively tells you how many bits are going to be used for pointers. sizeof is usually measured in bytes, so code compiled for 32-bit processors will report the size of pointers to be 4 (32 bits / 8 bits per byte), and code for 64-bit processors will report the size of pointers to be 8 (64 bits / 8 bits per byte). This is where the limitation of 4GB of RAM for 32-bit processors comes from -- if each memory address corresponds to a byte, to address more memory you need integers larger than 32-bits.
The size of the pointer basically depends on the architecture of the system in which it is implemented. For example the size of a pointer in 32 bit is 4 bytes (32 bit ) and 8 bytes(64 bit ) in a 64 bit machines. The bit types in a machine are nothing but memory address, that it can have. 32 bit machines can have 2^32 address space and 64 bit machines can have upto 2^64 address spaces. So a pointer (variable which points to a memory location) should be able to point to any of the memory address (2^32 for 32 bit and 2^64 for 64 bit) that a machines holds.
Because of this reason we see the size of a pointer to be 4 bytes in 32 bit machine and 8 bytes in a 64 bit machine.
In addition to the 16/32/64 bit differences even odder things can occur.
There have been machines where sizeof(int *) will be one value, probably 4 but where sizeof(char *) is larger. Machines that naturally address words instead of bytes have to "augment" character pointers to specify what portion of the word you really want in order to properly implement the C/C++ standard.
This is now very unusual as hardware designers have learned the value of byte addressability.
8 bit and 16 bit pointers are used in most low profile microcontrollers. That means every washing machine, micro, fridge, older TVs, and even cars.
You could say these have nothing to do with real world programming.
But here is one real world example:
Arduino with 1-2-4k ram (depending on chip) with 2 byte pointers.
It's recent, cheap, accessible for everyone and worths coding for.
In addition to what people have said about 64-bit (or whatever) systems, there are other kinds of pointer than pointer-to-object.
A pointer-to-member might be almost any size, depending how they're implemented by your compiler: they aren't necessarily even all the same size. Try a pointer-to-member of a POD class, and then a pointer-to-member inherited from one of the base classes of a class with multiple bases. What fun.
From what I recall, it's based on the size of a memory address. So on a system with a 32-bit address scheme, sizeof will return 4, since that's 4 bytes.
In general, sizeof(pretty much anything) will change when you compile on different platforms. On a 32 bit platform, pointers are always the same size. On other platforms (64 bit being the obvious example) this can change.
No, the size of a pointer may vary depending on the architecture. There are numerous exceptions.
Size of pointer and int is 2 bytes in Turbo C compiler on windows 32 bit machine.
So size of pointer is compiler specific. But generally most of the compilers are implemented to support 4 byte pointer variable in 32 bit and 8 byte pointer variable in 64 bit machine).
So size of pointer is not same in all machines.
In Win64 (Cygwin GCC 5.4), let's see the below example:
First, test the following struct:
struct list_node{
int a;
list_node* prev;
list_node* next;
};
struct test_struc{
char a, b;
};
The test code is below:
std::cout<<"sizeof(int): "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*): "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;
std::cout<<"sizeof(double): "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*): "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;
std::cout<<"sizeof(list_node): "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*): "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;
std::cout<<"sizeof(test_struc): "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*): "<<sizeof(test_struc*)<<std::endl;
The output is below:
sizeof(int): 4
sizeof(int*): 8
sizeof(double): 8
sizeof(double*): 8
sizeof(list_node): 24
sizeof(list_node*): 8
sizeof(test_struc): 2
sizeof(test_struc*): 8
You can see that in 64-bit, sizeof(pointer) is 8.
The reason the size of your pointer is 4 bytes is because you are compiling for a 32-bit architecture. As FryGuy pointed out, on a 64-bit architecture you would see 8.
A pointer is just a container for an address. On a 32 bit machine, your address range is 32 bits, so a pointer will always be 4 bytes. On a 64 bit machine were you have an address range of 64 bits, a pointer will be 8 bytes.
Just for completeness and historic interest, in the 64bit world there were different platform conventions on the sizes of long and long long types, named LLP64 and LP64, mainly between Unix-type systems and Windows. An old standard named ILP64 also made int = 64-bit wide.
Microsoft maintained LLP64 where longlong = 64 bit wide, but long remained at 32, for easier porting.
Type ILP64 LP64 LLP64
char 8 8 8
short 16 16 16
int 64 32 32
long 64 64 32
long long 64 64 64
pointer 64 64 64
Source: https://stackoverflow.com/a/384672/48026