28

Is there anything such as an unsigned int* which is different from int*. I know that unsigned has a higher range of values. Still, can't int* even point to any unsigned int?

tomol
  • 755
  • 1
  • 6
  • 12
  • 4
    A pointer points to bits. The type of the pointer informs you of how many bits are referenced and what they "mean". The bits, absent that information, are meaningless. – Hot Licks Dec 23 '14 at 17:41
  • @HotLicks: A pointer points to an object of a specified type. – Keith Thompson Dec 23 '14 at 17:52
  • 2
    @KeithThompson - A pointer specifies the type. Maybe in a correct fashion, maybe not. – Hot Licks Dec 23 '14 at 18:00
  • 2
    The "type" of a pointer tells the compiler what kind of data to expect at that address when you dereference it or do pointer math. But it's really nothing more than an address. After all, there's otherwise no way for it to know what random bits at some random address mean unless you tell it. – Lee Daniel Crocker Dec 23 '14 at 18:07
  • Interesting to see that there are 3 answers higher rated than the accepted one. Would be interesting to know OP's thoughts on the selection. – chux - Reinstate Monica Dec 24 '14 at 15:59

6 Answers6

20

int * and unsigned int * are two different pointer types that are not compatible types. They are also pointers to incompatible types. For the definition of compatible types, please refer to § 6.2.7 in the C Standard (C11).

Being pointers to incompatible types means that for example that this:

unsigned int a = 42;

int *p = &a;  // &a is of type unsigned int *

is not valid (the constraints of the assignment operator are violated).

Another difference between the two types is as for most other pointer types (although unlikely here) there is no guarantee from C they have the same size or the same representation.

Palec
  • 12,743
  • 8
  • 69
  • 138
ouah
  • 142,963
  • 15
  • 272
  • 331
  • 5
    Woo. The question is "Is there anything such as an unsigned int* which is different from int*", downvoters please tell what is wrong in my answer. – ouah Dec 23 '14 at 18:03
  • 6
    I do not see anything wrong in this answer. Also, if you do not dare to comment then do not downvote please. – haccks Dec 23 '14 at 18:05
  • The reason why I posted this "downvoter comment" is this same answer (without any changes) had a score of -5 before going upvoted again. – ouah Dec 23 '14 at 19:10
  • I didn't downvote, but my guess is that the problem is that the code you posted is not strictly invalid and `p` correctly interprets the signed number, since the MSB is 0 anyway. A better example might use a number which is between `INT_MAX` and `UINT_MAX`. – IllusiveBrian Dec 23 '14 at 21:16
  • 3
    @Namfuak it is invalid from C point of view. The constraints of the assignment operator are violated. A diagnostic is required and a compiler is free to refuse to translate the program. – ouah Dec 23 '14 at 21:23
  • @ouah You should put that in your answer. – IllusiveBrian Dec 23 '14 at 21:28
  • 7
    This code is a constraint violation, however `int *p = (int *)&a` is valid. Your answer only addresses whether a cast is required for compilation, whereas it seems to me that OP was asking about what happens at runtime if the cast is included. – M.M Dec 23 '14 at 23:41
  • @JaredBurrows post on meta to ask about that. The rules actually seem to say that you should *NOT* comment on why you voted. (To see this, try adding a comment that starts with "+1" or "-1", e.g. "-1 do not want" and you'll get an error message – M.M Dec 23 '14 at 23:42
  • 3
    @MattMcNabb That directly contradict's SE's own advice. [Downvoting a comment with <2k rep gives the message](http://meta.stackexchange.com/questions/135/encouraging-people-to-explain-downvotes) "Please consider adding a comment if you think this post can be improved." –  Dec 24 '14 at 00:41
  • The response to +1 or -1 is in response to putting that prefix on your comment rather than voting it. Vote, then comment without that indicator. (Yes, I think that was a bad design decision, flying in the face of common practice, but nobody asked me -- if it _really_ bothers folks, take it to Meta.) – keshlam Dec 24 '14 at 01:24
  • Re: the misguided error-message when you start a comment with +1 or -1: see http://meta.stackoverflow.com/q/277314/978917 – ruakh Dec 24 '14 at 07:59
18

Using an unsigned pointer to point to a signed version of the same type is defined by C Standard.

Therefore interpreting an int through an unsigned int pointer and vice-versa is valid.

ISO/IEC 9899:201x 6.5 Expressions, p7:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 88)

— a type that is the signed or unsigned type corresponding to the effective type of the object,

— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

88) The intent of this list is to specify those circumstances in which an object may or may not be aliased.

Effective type is basically the type of the object:

The effective type of an object for an access to its stored value is the declared type of the object, if any.


An issue has been raised about the interpretation of the above rule. The following is my additional rationale about it.

This text below is listed purely for semantic reasoning of the word: corresponding ,and not for the direct rules it specifies.

6.2.5 Types

p6: For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements.

p9: The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same.41)

p12: For each floating type there is a corresponding real type, which is always a real floating type. For real floating types, it is the same type. For complex types, it is the type given by deleting the keyword _Complex from the type name.

p27: Further, there is the _Atomic qualifier. The presence of the _Atomic qualifier designates an atomic type. The size, representation, and alignment of an atomic type need not be the same as those of the corresponding unqualified type

6.2.6.2 Integer types

p2: For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type

p5: The values of any padding bits are unspecified.54)A valid (non-trap) object representation of a signed integer type where the sign bit is zero is a valid object representation of the corresponding unsigned type, and shall represent the same value.

(And many more examples with identical usage of the word corresponding )

As you can see in the above snippets, Standard uses the word corresponding to refer to different types or types with different specifiers and/or qualifiers. Therefore, as seen in the above examples Standard uses the word as would be used in this example: qualified type is corresponding to type.

It would be illogical to suddenly use the word corresponding for a different purpose: referring to completely identically qualified/specified types and even confuse the matters more by including the words signed and unsigned in the same sentence for no good reason.

The intention of the 6.5, p7 is: a type that is the signed or unsigned type either a signed or unsigned type corresponding to the effective type of the object that othervise matches( corresponds ) to the target type. So for example: effective type is: int, int or unsigned int correspond to that type.

2501
  • 25,460
  • 4
  • 47
  • 87
16

unsigned int * and int * are different types. To convert one to the other you must use a cast.

If you read a value through a pointer then it attempts to interpret the bits stored at that memory location as if they were bits for the type being pointed to by the pointer you are reading through.

If the bits at that memory location were not written by a pointer of the same type you are reading through, then this is called aliasing.

The strict aliasing rule specifies which types may or may not be aliased; alasing between a type's signed and unsigned versions is always permitted.

However, if the bits are not a valid representation of a value in the type you are reading , then it causes undefined behaviour.

On modern systems there are no such "trap" representations so you have no issue. But let's say you were on a 1's complement system that trapped on negative zero:

unsigned int x = 0xFFFFFFFF;
int *y = (int *)&x;
printf("%d\n", y);

The attempt to read y could cause a hardware fault or any other behaviour.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • So apparently gcc and clang [allow this assignment without a cast as an extension](https://twitter.com/shafikyaghmour/status/957702383810658304) although I [can't say the reasoning behind the extension is good](https://twitter.com/kwalfridsson/status/957752471258124288) – Shafik Yaghmour Jan 29 '18 at 05:57
  • @ShafikYaghmour They print a diagnostic, so they comply with the standard ; the sticking point is that the diagnostic defaults to "warning" rather than "error" – M.M Jan 29 '18 at 08:27
  • I admit that I do take advantage of compiler extensions to use `char *`, `unsigned char *` interchangeably, it's annoying when working with buffers of mixed binary data and text – M.M Jan 29 '18 at 08:29
6

The value of the pointer is the same, but they are different types. A difference will arise depending on the way you interpret the pointer - for eg: dereferencing.

unsigned int *u;
int *d;
unsigned int v = 2147483648; /* 2^31 */
u = &v;
d = (int*) &v;
printf("%u\n", *u);
printf("%d\n", *d);

will output:

2147483648
-2147483648

The difference in the output arises because in printf("%d\n", *d), d is dereferenced and printed as if it points to a signed int, except it isn't. So you have to keep a distinction between the 2 types of pointers in your code.

Arjun Sreedharan
  • 11,003
  • 2
  • 26
  • 34
  • 4
    `u = d = &v;` is not valid here. You are violating the constraints of the assignment operator. You need explicit conversions (i.e., casts). – ouah Dec 23 '14 at 19:22
  • 6
    In C11, 6.7.6.1p2 *"For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types."* and 6.2.7p1 *"Two types have compatible type if their types are the same. [...]"* – ouah Dec 23 '14 at 19:33
  • @ouah i have made amends in my answer – Arjun Sreedharan Dec 24 '14 at 08:41
  • "The value of the pointer is the same" - is this guaranteed by the standard or is it just a practical observation? – martinkunev Mar 05 '23 at 19:42
2

It can point as both has the same size. The problem is this will introduce a hard to find bug, because you'll interpret a signed value as an unsigned or vice-versa.

Nelson Teixeira
  • 6,297
  • 5
  • 36
  • 73
2

A pointer is a number that is a memory address. So pointers have to have enough precision to be able to address all of memory for the implementation.

Whether you reference a signed or unsigned int makes no difference in the internal structure of the pointer, because, in theory anyway, the int or unsigned int could be almost anywhere in memory. The datatype (unsigned) has to be declared to "help" the compiler decide correctness of the code.

jim mcnamara
  • 16,005
  • 2
  • 34
  • 51
  • 5
    In terms of reality, bits are bits. – Lee Daniel Crocker Dec 23 '14 at 18:08
  • 6
    @LeeDanielCrocker: And a rose is a rose. If you only look at bits, there's no such thing as a "pointer". The term "pointer" has meaning in the context of C; in that context, it is not a number. A pointer may be converted to an integer type, or vice versa, but the conversion may be non-trivial. – Keith Thompson Dec 23 '14 at 19:40