Any type of pointer can point to anything?

2020-07-16 12:09发布

Is this statement correct? Can any "TYPE" of pointer can point to any other type? Because I believe so, still have doubts.

Why are pointers declared for definite types? E.g. int or char?

The one explanation I could get was: if an int type pointer was pointing to a char array, then when the pointer is incremented, the pointer will jump from 0 position to the 2 position, skipping 1 position in between (because int size=2).

And maybe because a pointer just holds the address of a value, not the value itself, i.e. the int or double.

Am I wrong? Was that statement correct?

标签: c++ pointers
5条回答
你好瞎i
2楼-- · 2020-07-16 12:41

Some pointers are more equal than others...

First of all, not all pointers are necessarily the same thing. Function pointers can be something very different from data pointers, for instance.

Aside: Function pointers on PPC

On the PPC platform, this was quite obvious: A function pointer was actually two pointers under the hood, so there was simply no way to meaningfully cast a function pointer to a data pointer or back. I.e. the following would hold:

int* dataP;
int (*functionP)(int);

assert(sizeof(dataP) == 4);
assert(sizeof(functionP) == 8);
assert(sizeof(dataP) != sizeof(functionP));

//impossible:
//dataP = (int*)functionP;          //would loose information
//functionP = (int (*)(int))dataP;  //part of the resulting pointer would be garbage

Alignment

Furthermore, there is problems with alignment: Depending on the platform some data types may need to be aligned in memory. This is especially common with vector data types, but could apply to any type larger than a byte. For instance, if an int must be 4 byte aligned, the following code might crash:

char a[4];
int* alias = (int*)a;
//int foo = *alias;    //may crash because alias is not aligned properly

This is not an issue if the pointer comes from a malloc() call, as that is guaranteed to return sufficiently aligned pointers for all types:

char* a = malloc(sizeof(int));
int* alias = (int*)a;
*alias = 0;    //perfectly legal, the pointer is aligned

Strict aliasing and type punning

Finally, there are strict aliasing rules: You must not access an object of one type through a pointer to another type. Type punning is forbidden:

assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
//uint32_t bits = *(uint32_t*)&foo;    //type punning is illegal

If you absolutely must reinterpret a bit pattern as another type, you must use memcpy():

assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
uint32_t bits;
memcpy(&bits, &foo, sizeof(bits));    //bit pattern reinterpretation is legal when copying the data

To allow memcpy() and friends to actually be implementable, the C/C++ language standards provide for an exception for char types: You can cast any pointer to a char*, copy the char data over to another buffer, and then access that other buffer as some other type. The results are implementation defined, but the standards allow it. Use cases are mostly general data manipulation routines like I/O, etc.


TL;DR:

Pointers are much less interchangeable than you think. Don't reinterpret pointers in any other way than to/from char* (check alignment in the "from" case). And even that does not work for function pointers.

查看更多
唯我独甜
3楼-- · 2020-07-16 12:45

Pointers may be interchangeable, but are not required to be.

In particular, on some platforms, certain types need to be aligned to certain byte-boundaries. So while a char may be anywhere in memory, an int may need to be on a 4-byte boundary.

Another important potential difference is with function-pointers.
Pointers to functions may not be interchangeable with pointers to data-types on many platforms.

It bears repeating: This is platform-specific.

I believe Intel x86 architectures treat all pointers the same.
But you may well encounter other platforms where this is not true.

查看更多
祖国的老花朵
4楼-- · 2020-07-16 12:46

Any pointer can refer to any location in memory, so technically the statement is correct. With that said, you need to be careful when reinterpreting pointer types.

A pointer basically has two pieces of information: a memory location, and the type it expects to find there. The memory location could be anything. It could be the location where an object or value is stored; it could be in the middle of a string of text; or it could just be an arbitrary block of uninitialised memory.

The type information in a pointer is important though. The array and pointer arithmetic explanation in your question is correct -- if you try to iterate over data in memory using a pointer, then the type needs to be correct, otherwise you may not iterate correctly. This is because different types have different sizes, and may be aligned differently.

The type is also important in terms of how data is handled in your program. For example, if you have an int stored in memory, but you access it by dereferencing a float* pointer, then you'll probably get useless results (unless you've programmed it that way for a specific reason). This is because an int is stored in memory differently from the way a float is stored.

查看更多
仙女界的扛把子
5楼-- · 2020-07-16 12:46

Can any "TYPE" of pointer can point to any other type?

Generally no. The types have to be related.

It is possible to use reinterpret_cast to cast a pointer from one type to another, but unless those pointers can be converted legally using a static_cast, the reinterpret_cast is invalid. Hence you can't do Foo* foo = ...; Bar* bar = (Bar*)foo; unless Foo and Bar are actually related.

You can also use reinterpret_cast to cast from an object pointer to a void* and vice versa, and in that sense a void* can point to anything -- but that's not what you seem to be asking about.

Further you can reinterpret_cast from object pointer to integral value and vice versa, but again, not what you appear to be asking.

Finally, a special exception is made for char*. You can initialize a char* variable with the address of any other type, and perform pointer math on the resulting pointer. You still can't dereference thru the pointer if the thing being pointed to isn't actually a char, but it can then be casted back to the actual type and used that way.

Also keep in mind that every time you use reinterpret_cast in any context, you are dancing on the precipice of a cliff. Dereferencing a pointer to a Foo when the thing it actually points to is a Bar yields Undefined Behavior when the types are not related. You would do well to avoid these types of casts at all costs.

查看更多
smile是对你的礼貌
6楼-- · 2020-07-16 13:02

Every pointer is of some specific type. There's a special generic pointer type void* that can point to any object type, but you have to convert a void* to some specific pointer type before you can dereference it. (I'm ignoring function pointer types.)

You can convert a pointer value from one pointer type to another. In most cases, converting a pointer from foo* to bar* and back to foo* will yield the original value -- but that's not actually guaranteed in all cases.

You can cause a pointer of type foo* to point to an object of type bar, but (a) it's usually a bad idea, and (b) in some cases, it may not work (say, if the target types foo and bar have different sizes or alignment requirements).

You can get away with things like:

int n = 42;
char *p = (char*)&n;

which causes p to point to n -- but then *p doesn't give you the value of n, it gives you the value of the first byte of n as a char.

The differing behavior of pointer arithmetic is only part of the reason for having different pointer types. It's mostly about type safety. If you have a pointer of type int*, you can be reasonably sure (unless you've done something unsafe) that it actually points to an int object. And if you try to treat it as an object of a different type, the compiler will likely complain about it.

Basically, we have distinct pointer types for the same reasons we have other distinct types: so we can keep track of what kind of value is stored in each object, with help from the compiler.

(There have been languages that only have untyped generic pointers. In such a language, it's more difficult to avoid type errors, such as storing a value of one type and accidentally accessing it as if it were of another type.)

查看更多
登录 后发表回答