2
int PileInts[1024];
char *Pile = (char *)PileInts;

What do these two lines of code do? I am thinking that the char *Pile = (char *)PileInts; line creates a character named *Pile that gives the value of the address at PileInts. Am I right? Could I get a deeper explanation?

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • _creates a character named *Pile that gives the value of the address at PileInts_ not a character, pointer to character. This is a cast of (not really) the pointer on `int` to the pointer on `char`. – fas Sep 25 '19 at 05:47
  • @user3365922 -- hmm, what do you mean by (not really)? That is exactly what it does. On access `PileInts` is converted to a pointer to the first element, it is the cast to `char *` and the address assigned to `Pile` (no violation of C Standard 6.5(p6) because it is allowed in 6.5(p7). See [C11 Standard - 6.5 Expressions](http://port70.net/~nsz/c/c11/n1570.html#6.5p6)) – David C. Rankin Sep 25 '19 at 05:59
  • @DavidC.Rankin I meant that `int*` and `int[N]` are not exactly the same, isn't it? – fas Sep 25 '19 at 06:03
  • Okay, gotcha, I was a bit confused at first. So `int*` being a *pointer to* `int` and the `[...]` acting as a dereference making the type `int`. You are correct, so if `p` is a pointer, then `p[N]` is the same as `*(p + N)` which is type `int`. – David C. Rankin Sep 25 '19 at 06:07

2 Answers2

3

The line

int PileInts[1024];

creates an array object that consists of 1024 integers. The object can be accessed using the variable name PileInts

The line

char *Pile = (char *)PileInts;

creates a char pointer object and makes it point to the first char of the array object. The char pointer object is accessed using the variable name Pile.

The char pointer Pile can be used for accessing the individual bytes of PileInts. Example:

#include <stdio.h>

int main(void) {
    int PileInts[1024];
    char *Pile = (char *)PileInts;

    PileInts[0] = 1;
    PileInts[1] = 2;

    // Print the bytes/chars of the first two ints of PileInts
    for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
    {
        printf("0x%02x\n", *Pile); // Print what Pile points to
        ++Pile;                    // Increment Pile so it points to the next byte/char
    }
    return 0;
}

Possible output:

0x01
0x00
0x00
0x00
0x02
0x00
0x00
0x00

Note: The output may differ from system to system due to different endianess and/or integer size.

If you want to see the value of Pile, i.e. the address it points to, you can change the code like:

#include <stdio.h>

int main(void) {
    int PileInts[1024];
    char *Pile = (char *)PileInts;

    PileInts[0] = 1;
    PileInts[1] = 2;

    for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
    {
        printf("Pile points to addresss %p where the value 0x%02x is stored\n", 
               (void*)Pile, *Pile);
        ++Pile;
    }
    return 0;
}

Possible output:

Pile points to addresss 0x7ffe5860b8e0 where the value 0x01 is stored
Pile points to addresss 0x7ffe5860b8e1 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e2 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e3 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e4 where the value 0x02 is stored
Pile points to addresss 0x7ffe5860b8e5 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e6 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e7 where the value 0x00 is stored
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • what am I able to do with Pile? I didn't really understand the purpose of pointers very well. –  Sep 25 '19 at 05:53
  • @4386427: Also it would be good if you can show the example code, with the address printed so that OP can know 1 byte address increments – Inian Sep 25 '19 at 06:00
  • @kevinmarks (**type is King!**) It controls pointer arithmetic. So say you use `int *p = PileInts;` and then advance the pointer `p` by one (`p = p + 1;`) since the pointer it type `int`, `p + 1` advances 4-bytes in memory to the next `int`. By casting to `char *`, the type of `Pile` is pointer to `char`. So if you advance `Pile`, e.g (`Pile = Pile + 1;`), now `Pile` points to the next **byte** in `PileInt`, not the next `int`. – David C. Rankin Sep 25 '19 at 06:04
  • 1
    @Inian Added example that prints the address as well – Support Ukraine Sep 25 '19 at 06:06
  • thanks a lot. I am getting it a little more now. I do have another question. I have this array organized as a 64x64 box, with a value in every location. How would I check to see if values are the same in some rows vertically? –  Sep 25 '19 at 06:09
  • @DavidC.Rankin thanks a lot. this information is very helpful. in my code, would there be any reason to cast Pile to a char since I already made it a char on the left side? –  Sep 25 '19 at 06:12
  • @kevinmarks I'm not sure what you mean by 64x64. The result of 64x64 is 4096 but your array is only 1024 ints. Anyway, if you need 64x64 why not do: `int box[64][64];` – Support Ukraine Sep 25 '19 at 06:12
  • @4386427 that's what I am wondering. I've got no clue why I would have a 1x1024 int array if I will be searching through a 64x64 box. –  Sep 25 '19 at 06:13
  • @kevinmarks As you iterate across your rows `x` and columns `y` (beginning at the second row and ending at last), you can check if value at `[x][y-1]` equals value at `[x][y]`. If it does, the value at that index is the same as the one above vertically. As to you second question, always compile with **warnings enabled** `-Wall -Wextra -pedantic` for gcc/clang or `/W3` for VS (and don't accept code until it compiles without warning) -- without the cast -- you will receive a warning (you probably will by default). – David C. Rankin Sep 25 '19 at 06:15
  • why `(void*)Pile`, not just `Pile` at `printf`? – fas Sep 25 '19 at 06:17
  • Because the C standard requires it and specifically states a mismatch between the conversion specifier `"%p"` (which requires type `void *`) and the argument (type `char *`) invokes *Undefined Behavior* -- Bad Juju... [C11 Standard - 7.21.6.1 The fprintf function(p9)](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p9) – David C. Rankin Sep 25 '19 at 06:19
  • @DavidC.Rankin That's the thing. If I had a 64x64 array I could do just that, but I only have a 1x1024 array and have no idea how to check that. In the writeup it says that every 8 bits is a color. –  Sep 25 '19 at 06:20
  • @user3365922 Because `%p` requires a `void*` so `Pile` must be cast'ed into `void*` before printing. If you don't, it will be undefined behavior according to the C standard. In the real world, it will however work fine even without the cast on most systems. But to be sure: do the cast – Support Ukraine Sep 25 '19 at 06:21
  • @kevinmarks I guess you have some assignment... It seems to be some "dirty" trick thing. On many systems `int` is 4 chars. So 1024 ints is 4096 chars which again is 64x64 chars. So it seems the idea is to use the 1024 ints as if it is 64x64 chars. Very ugly IMO. – Support Ukraine Sep 25 '19 at 06:23
  • @kevinmarks - I'll comment back under the original question – David C. Rankin Sep 25 '19 at 06:24
  • @4386427 use 1024 ints as if it were 64x64 chars, could you expand a little more on that? I think that is exactly what I am supposed to be doing but I cannot figure out how to think of a 1x1024 int array as a 64x64 array of chars –  Sep 25 '19 at 06:34
  • @kevinmarks Well, `*(Pile + 64*row + column)` will do that, i.e. read/write the char in position `(row, column)` – Support Ukraine Sep 25 '19 at 06:37
  • @4386427 thanks, that was exactly what I was looking for! –  Sep 25 '19 at 06:53
1

line creates a character named *Pile that gives the value of the address at PileInts

No. It creates a pointer to character char* named Pile, which points to the lowest byte in the first int in an array of int-

Could I get a deeper explanation?

The code gives a pointer which can be used to access individual bytes of the first int in the array, and from there, individual bytes of the next adjacent int, until the end of the array.

This is possible because when an array name is used in an expression, it "decays" into a pointer to the first element of that array. Making PileInts, when used in an array, equivalent to type int*.

Converting from int* to char* is valid but fishy C. Some things to be aware of:

  • First of all, uint8_t* should be used whenever one seeks to access raw data values. The char type is highly problematic since it has implementation-defined signedness - it should not be used for anything else but for characters and strings. Is char signed or unsigned by default?

  • This pointer will point at the lowest address of the first int. Which bytes this corresponds to is CPU-specific, namely it depends on endianess of the CPU. Using bitwise shift operators instead of pointers would have remove this CPU dependency, so depending on what you want to do with this pointer, it may or may not be the right tool for the task.

As for the pointer conversion itself, it is fine as per C17 6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

Meaning that something like (int*) ((char*)PileInts + 1) would be an undefined behavior bug, because the int* will be misaligned.

The same paragraph in the standard continues:

When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

This is the rule allowing us to iterate over any data type by using a character pointer (or preferably the equivalent uint8_t*). The same is not true for any other pointer type, we could for example not use a short* to do the same, thinking we iterate over 16 bit words.

There's also another rule preventing us from using any other pointer type, namely the rule regulating the compiler's internal type system and how different pointers may be an alias of each other. Informally known as the strict aliasing rule. This rule too has an exception for character types only. Meaning we can't do this:

int PileInts[1024];
char *Pile = (char *)PileInts;
short* sptr = (short*)Pile; // very questionable cast but not a bug yet
printf("%h", *sptr); // bug, data is accessed as wrong type, strict aliasing violation
Lundin
  • 195,001
  • 40
  • 254
  • 396