0

I was just curious if this was correct in assigning the value 888 to c and if it is not then why. I haven't found anything saying it was not and when I looked inside the c language specifications it appeared as if it was correct.

int** ppi;
int c = 6;
ppi = (int**)(&c);
*ppi = 888;

I have used it within several IDE's and with several compilers, but none have given me an error. However, some of my friends have said that this code should throw an error.

I was trying to change the value of c without adding in an intermediate pointer. I know the following will work, but I was not sure if doing it the above way would work as well.

int** ppi;
int* pi;
int c = 6;
pi = &c;
ppi = π
**ppi = 888;
  • 2
    It's clearly incorrect. It will "work" on most machines, in that it will set `c` to 888. Why you'd want to do it this way is somewhat of a mystery, though. – Steve Summit Apr 16 '18 at 22:49
  • 2
    You must not be using very good compilers. Mine warns me about the `*ppi = 999` line, as I would expect it to. – Steve Summit Apr 16 '18 at 22:55
  • 2
    What *are* you trying to accomplish, anyway? Why can't you use `int *pi`? – Steve Summit Apr 16 '18 at 22:55
  • 1
    it throws a warning of incompatible type casting, wheras you should know even if its compilable, it's never usable. – Abr001am Apr 16 '18 at 22:56
  • @SteveSummit More likely it's bad compiler _settings_, not a bad _compiler_ per se. OP probably just doesn't have `-Wint-conversion` enabled. – Charles Srstka Apr 16 '18 at 22:57
  • beware that here `**ppi` is a content of an addressblock located at 888, this throws a segfault generally. – Abr001am Apr 16 '18 at 23:06
  • why can't you just do `*pi = 888;`? – bruceg Apr 16 '18 at 23:30
  • I could just use a single pointer, but the was originally for an assignment in which the goal was to use double pointers. – Kelby Dinkel Apr 16 '18 at 23:32
  • 1
    Casting itself is perfectly acceptable as long as you are not violating any alignment requirements (otherwise, the behavior is undefined). But what you do later (i.e. `*ppi = 888`) makes no sense whatsoever. `*ppi` is an `int *`. You are not allowed to assign an integer value `888` to a pointer object in C. The types are different. This would require an explicit cast as well. But even with a cast it still would not make any sense. Why are you trying to assign `888` to a *pointer*? – AnT stands with Russia Apr 16 '18 at 23:43

4 Answers4

4

The code causes undefined behaviour in 4 different ways; it is certainly not "correct" or "acceptable" as some of the other answers seem to be suggesting.

Firstly, *ppi = 888; attempts to assign an int to an lvalue of type int * . This violates the constraint 6.5.16.1/1 of the assignment operator which lists the types that may be assigned to each other; integer to pointer is not in the list.

Being a constraint violation, the compiler must issue a diagnostic and may refuse to compile the program. If the compiler does generate a binary then that is outside the scope of the C Standard, i.e. completely undefined.


Some compilers, in their default mode of operation, will issue the diagnostic and then proceed as if you had written *ppi = (int *)888;. This brings us to the next set of issues.

The behaviour of casting 888 to int * is implementation-defined. It might not be correctly aligned (causing undefined behaviour), and it might be a trap representation (also causing undefined behaviour). Furthermore, even if those conditions pass, there is no guarantee that (int *)888 has the same size or representation as (int)888 as your code relies on.

The next major issue is that the code violates the strict aliasing rule. The object declared as int c; is written using the lvalue *ppi which is an lvalue of type int *; and int * is not compatible with int.

Yet another issue is that the write may write out of bounds. If int is 4 bytes and int * is 8 bytes, you tried to write 8 bytes into a 4-byte allocation.

Another problem from earlier in the program is that ppi = (int**)(&c); will cause undefined behaviour if c is not correctly aligned for int *, e.g. perhaps the platform has 4-byte alignment for int and 8-byte alignment for pointers.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • In the last part of your answer when would &c not be correctly aligned for int*? Doesn't the & part make it return a int*? I apologize if this came off as rude in any way. The rest of your answer made a lot of sense and helped to clear the issue up. Edit: I just realized what you are meaning. If the 888 is being casted as an int* then the int might not match up within the memory. – Kelby Dinkel Apr 16 '18 at 23:52
  • @KelbyDinkel The *value* of an `int *` is aligned for `int`, not aligned for `int *`. If that pointer value is stored in a variable, then that variable must be aligned for `int *`. Some sources are sloppy with language here but hopefully you can identify the two different alignment values involved here – M.M Apr 17 '18 at 00:01
  • An address (i.e. a pointer value) being "aligned for T" means that it's a multiple of the fundamental alignment value for T; which might be `4` for an int and `8` for a pointer – M.M Apr 17 '18 at 00:02
1

This is not acceptable. Unless you have some really good reason to know that there's an int being stored at the memory address 888, this is invalid code which will lead to either crashes or undefined behavior if you dereference the pointer twice (and if you don't plan to do that, there's little point in using an int **).

Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
  • The code never uses the pointer `6`; it's overwritten with `888` before being used. So up to there, there is no problem. `888` is probably also unmapped but it's correctly aligned on every architecture I inow of. – rici Apr 16 '18 at 22:57
  • @rici Ah, you're right. The point still stands that memory address `888` almost certainly isn't going to contain what OP expects (whatever that even _is_). – Charles Srstka Apr 16 '18 at 22:59
0

ppi contains a pointer that points to a memory location that itself contains a pointer to an int. int c=6; creates storage for an int and puts the value 6 into that storage giving:

ppi : [ some pointer ]
c   : [ 6 ]

The line

ppi = (int**)(&c)

is telling the compiler "never mind that &c is a pointer to int; assume it's a pointer that holds a pointer to int; then store that in ppi. So at this point, ppi will contain the address of c (whatever that may be). So we have

ppi : [ &c ]
c   : [ 6 ]

The next line

*ppi = 888;

is telling the compiler : "Store the value 888 at the location pointed to by *ppi."

So ppi points at c which contains 6 so we'd expect the value of c to be modified to 888. But wait, c is an int so depending on how much space an int takes, it may not be enough to store a pointer. This is the biggest problem here.

agbinfo
  • 793
  • 5
  • 17
0
int** ppi;
int c = 6;
ppi = (int**)(&c); // Cast from int* to int** may be lossy or trap due to alignment issues
*ppi = 888; // 888 is not an int* nor implicitly convertible. Whether casting
            //  is allowed, and what that means, depends on the implementation

Regarding compilation giving you an error:
While the last assignment forces the compiler to give a diagnostic message, any singular one is enough. Whether that is called an error, how much detail it contains, and if that breaks the build is at the discretion of the implementation. There are probably options.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118