When is listing between pointer types performed and not undefined behavior in C?

As a newbie to C, I am confused when the hover is actually normal.

As I understand it, you can use any type of pointer for any other type, and the compiler will let you do this. For example:

int a = 5; int* intPtr = &a; char* charPtr = (char*) intPtr; 

However, as a rule, this causes undefined behavior (although it works on many platforms). However, there are apparently some exceptions:

  • you can freely click on and from void* (?)
  • you can freely click on and from char* (?)

(at least I saw it in code ...).

So what are the between types of pointer non-undefined behavior in C?

Edit:

I tried to learn the C standard (section "6.3.2.3 Pointers", http://c0x.coding-guidelines.com/6.3.2.3.html ), but actually figure it out, except for the bit about void* .

Edit2:

Just for clarification: I am explicitly asking only for โ€œnormalโ€ pointers, i.e. not about function pointers. I understand that the rules for cast function pointers are very limited. As far as I remember, I already asked about this :-): What happens if I draw a pointer to a function by changing the number of parameters

+29
c casting undefined-behavior
Jan 26 2018-11-21T00:
source share
5 answers

Basically:

  • a T * can be freely converted to void * and vice versa (where T * not a function pointer), and you will get the original pointer.
  • a T * can be freely converted to U * and vice versa (where T * and U * are not function pointers), and you will get the original pointer if alignment requirements match. If not, the behavior is undefined.
  • The function pointer can be freely converted to any other type of function pointer and vice versa, and you will get the original pointer.

Note. T * (for non-pointers) always satisfies the alignment requirements for char * .

Important: None of these rules says anything about what happens if you convert, say, T * to U * , and then try to dereference it. This is a completely different area of โ€‹โ€‹the standard.

+26
Jan 26 2018-11-11T00:
source share

Oli Charlesworth's excellent answer lists all cases where casting a pointer to a pointer of a different type gives a well-defined result.

In addition, there are four cases where casting a pointer gives the results defined by the implementation:

  • You can overlay a sufficiently large (!) Integer type. For this purpose, C99 has the optional types intptr_t and uintptr_t . The result is determined by implementation. On platforms that address memory as a continuous stream of bytes (the "linear memory model" used by most modern platforms), it usually returns the numerical value of the memory address pointed to by the pointer, which means just a byte. However, not all platforms use the linear memory model, so it is determined by the implementation :-).
  • Conversely, you can point an integer to a pointer. If the integer is of type large enough for intptr_t or uintptr_t and was created by casting a pointer, dropping it back to the same type of pointer will return that pointer to you (which, however, can no longer be valid). Otherwise, the result is determined by the implementation. Note that actually dereferencing a pointer (as opposed to just reading its value) may still be UB.
  • You can point to any object on char* . Then the result points to the low address byte of the object, and you can read the remaining bytes of the object, increasing the pointer to the size of the object. Of course, what values โ€‹โ€‹you actually get are again determined by the implementation ...
  • You can freely enter null pointers, they will always contain null pointers, regardless of the type of pointer :-).

Source: C99 standard, sections 6.3.2.3 "Pointers" and 7.18.1.4 "Integer types capable of holding object pointers".

As far as I can tell, all the rest of the pointer casts to a pointer of a different type are undefined behavior. In particular, if you are not using a char or a sufficiently large integer type, there can always be UB to point to a pointer to another type of pointer - even without dereferencing it.

This is because types can have different alignment, and there is no general, portable way to make sure that different types have compatible alignment (except for some special cases, such as pairs with the whole next to the signature / unsigned).

+8
Jan 27 '11 at 15:59
source share

As a rule, if, as usual, the pointers themselves have the same alignment properties, the problem is not that it is itself, but regardless of whether you can access the data through the pointer.

Throwing away any type of T* in void* and vice versa is guaranteed for any type of T object: this is guaranteed to give you exactly the same backward pointer. void* is the type of all pointers to the catch object.

For other castings between types of objects, there is no guarantee that access to the object through such a pointer can cause all kinds of problems, such as alignment (bus error), trap representations of integers. Different types of pointers are not even guaranteed to have the same width, so theoretically you could even lose information.

One action that should always fire should be (unsigned char*) . Through such a pointer, you can then examine the individual bytes of your object.

+4
Jan 26 2018-11-21T00:
source share

The authors of the Standard did not attempt to weigh the costs and benefits of conversion support between most combinations of pointer types on platforms where such support would be expensive because:

  1. Most platforms where such conversions would be expensive would probably be obscure, which the authors of the Standard were not aware of.

  2. People using such platforms will be better placed than the authors of the Standard, with the costs and benefits of such support.

If a particular platform uses a different view for int* and double* , I think that the Standard would deliberately allow the possibility that, for example, a conversion with a round drop conversion from double* to int* and back to double* would work sequentially , but conversions from int* double* and returning to int* may fail.

I donโ€™t think that the authors of the Standard suggested that such operations could fail on platforms where such conversions cost nothing. They described Spirit C in the charter and logical documents, including the principle of "Do not interfere with the programmer (or do not need) to impede what needs to be done." Given this principle, it will not be necessary for the Standard to mandate that implementations perform actions in such a way as to help programmers do what they need to do when it will cost nothing, because implementations that make a good attempt defend Spirit C will behave this way with or without a mandate.

0
Dec 03 '18 at 16:36
source share

This behavior is undefined when you apply a type with a different size. For example, casting from char to int. A char is 1 byte long. Integers are 4 bytes (on a 32-bit Linux system). Therefore, if you have a pointer to char and you pass it to a pointer to int, this will cause undefined behavior. Hope this helps.

Something like the following will result in undefined behavior:

 #include <stdio.h> #include <stdlib.h> int main() { char *str = "my str"; int *val; val = calloc(1, sizeof(int)); if (val == NULL) { exit(-1); } *val = 1; str = (char) val; return 0; } 

EDIT: What, by the way, did Oli say about void * pointers. You can point between any void pointer and another pointer.

-2
Jan 26 2018-11-11T00:
source share



All Articles