0

New to C here, I am studying pointers. So my question "Are the bytes allocated by variables consecutive in memory?" is because I thought that by creating a char pointer from an integer pointer (char *p_c = p_i - (sizeof(char))), the char pointer would point exactly to the second byte of the integer.

E.g.: An integer variable takes 4 bytes and a char variable takes 1 byte.

#include <stdio.h>

int main(){
    int a = 4276545;
    int *p = &a;
    char *p_c1 = p - (1*sizeof(char));
    printf("Value of p_c1 (second byte of a) is %c\n", *p_c1);
}

The ASCII 'A' char in binary is 01000001. 4276545 decimal to binary is 00000000 01000001 01000001 01000001.

Isn't p_c1 pointing to 00000000 01000001 01000001 01000001?

Printing *p_c1, I was expecting it to be 'A', but it prints 'P'. And it's not as it was a random char which always changes in each execution, it always prints 'P'. Why?

alinsoar
  • 15,386
  • 4
  • 57
  • 74
nicolasbk
  • 21
  • 6
  • Aside: `sizeof (char)` is guaranteed to be `1`. – Oka Apr 22 '22 at 19:15
  • 1
    You seem to be under the impression that `p` points to the last byte of `a` - not sure how you came up with that – UnholySheep Apr 22 '22 at 19:17
  • Does this answer your question? [Why aren't consecutively-defined variables in increasing memory locations?](https://stackoverflow.com/questions/57763351/why-arent-consecutively-defined-variables-in-increasing-memory-locations) – Raymond Chen Apr 22 '22 at 19:18
  • @RaymondChen not exactly my question, but found it interesting; thanks for sharing – nicolasbk Apr 22 '22 at 19:33

3 Answers3

3

sizeof(char) is guaranteed to be 1 (no matter how many bits it is), so you have char *p_c1 = p - 1;

Problem 1:

When you add to or subtract from a pointer, you change the address by sizeof(*p), so you're not changing the address by one byte, but by sizeof(int) bytes. This is necessary because of the a[i] == *(a+i) equivalency.

Problem 2:

You are subtracting when you should be adding. The address of something is the address at which it starts, so subtracting would lead to a position outside of a. You want to move into a, so you'd want to add.

Fixed:

int a = 0x41424344;
char *p = (char *)&a;
for ( size_t i=0; i<sizeof(int); ++i )
   printf( "%zu %d %c\n", i, *(p+i), *(p+i) );

The output varies by system, but you should get the following on a little-endian system with an char type which is 8 bits in size and an int type that's 32 bits in size:

0 68 D
1 67 C
2 66 B
3 65 A
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Thanks, your comments under "Problem 1" explains it. But I didn't quite understand why the code outputs D C B A (it does, I tested it), should I post a new question? or can I ask subsequent question here on comments? – nicolasbk Apr 22 '22 at 19:40
  • 1
    Because you have a [little endian machine](https://en.wikipedia.org/wiki/Endianness). The least significant byte is placed first, then second least significant, etc – ikegami Apr 22 '22 at 19:51
1
An integer variable takes 4 bytes

This is a wrong statement. The correct statement is that an integer takes at least 2 bytes if CHAR_BIT=8 and at least 1 byte if CHAR_BIT>=16. There are systems where CHAR_BIT is 64, and on such systems sizeof(TYPE) = 1 for all the types. The idea is, an int must be at least 16 bits in size. There are systems where one needs to make a few fetches to bring an integer into a register.

char *p_c1 = p - (1*sizeof(char));

You can conceive an integer variable int a like an array of length 1. If you take the address of the integer, it is like taking the address of the first element of an array.

What you try to do is to take the previous integer before the address of a (because (1 times sizeof(char) )= 1) and consider the first byte of that integer. This is not correct, it is undefined behavior accessing an array outside its limits, or accessing an address that was not explicitly allocated for a variable.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • What about CHAR_BIT=9? – ikegami Apr 22 '22 at 19:30
  • 1
    You're the one that brought it up. I agree you shouldn't be mentioning it. You could simply state "an `int` need not be that size; the requirement is that it must be at least 16 bits in size". – ikegami Apr 22 '22 at 19:32
  • @ikegami IIRC, posix mandated that a `char` be [only] 8 bits. See: https://stackoverflow.com/questions/6631491/why-did-posix-mandate-char-bit-8 – Craig Estey Apr 22 '22 at 20:12
  • @Craig Estey, Cool, but noone said anything about POSIX – ikegami Apr 22 '22 at 20:14
  • 1
    @ikegami alinsoar _did_ bring up the non-8 bit thing first. In the general case, there are [probably] some vacuum tube era mainframes that have non-8 bit chars [And, I do recall there is an _old_ TI DSP chip that had a 16 bit bus and granularity of 16 bits]. But, this being 2022, for OP [at his/her skill level], the simpler [but not necessarily expert/all encompassing] answer is the correct one. I think it helps to talk about what is 99.44% likely these days. I mentioned posix because they did a "stop the nonsense" approach (that, IMO, ISO/C should do (e.g. no more 1's complement ints, etc)). – Craig Estey Apr 22 '22 at 20:29
  • @Craig Estey Re "*the simpler [but not necessarily expert/all encompassing] answer is the correct one*", I disagree. There's no need to be wrong to be simple. A simple and correct alternative exists. I have provided it in an earlier comment. – ikegami Apr 22 '22 at 20:51
  • I was able to achieve what I wanted by typecasting and adding instead of decreasing the value of the pointer. Thanks everyone. – nicolasbk Apr 22 '22 at 20:56
  • How do I close this topic/question? – nicolasbk Apr 22 '22 at 20:56
  • 1
    @nicolasbk One way is to upvote all good answers + _accept_ the "best" answer (the green checkmark). Then, people will see that the question has an accepted answer [and move on]. – Craig Estey Apr 22 '22 at 20:59
0

As @ikegami said "When you add to or subtract from a pointer, you change the address by sizeof(*p)", so by typecasting and adding 1, I was able to access the second byte of a, now it's printing 'A'

#include <stdio.h>

int main(){
    int a = 4276545;
    int *p = &a;
    char *p_c1 = (char*) p + 1;
    printf("Value of p_c1 (second byte of a) is %c\n", *p_c1);
}

Thanks everyone for the comments, I learned a lot

nicolasbk
  • 21
  • 6