Thread: I'm refreshing my memory, and am having strange behavior playing around with pointers

  1. #1
    Registered User
    Join Date
    Jan 2009
    Posts
    26

    I'm refreshing my memory, and am having strange behavior playing around with pointers

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void){
    	//10 bit memory bank
    	char *main_pointer = malloc(10);
    
    	// array of 2 integers (8 bits)
    	int **int_pointer_array = malloc(2*sizeof(int *));
    
    	// set first integer pointer in array to point to byte 0 in 10 byte bank
    	int_pointer_array[0] = (int *) main_pointer;
    
    	// set the second integer pointer in array to point to byte 6 in 10 byte bank
    	int_pointer_array[1] = (int *) (main_pointer + 6);
    
    	// assign values
    	*int_pointer_array[0] = 5;
    	*int_pointer_array[1] = 6505;
    
    	// print values
    	printf("The values are %d and %d\n", *int_pointer_array[0], *int_pointer_array[1]);
    
    	return 0;
    
    }
    The output from this code on codepad.org is what I would expect: C code - 26 lines - codepad

    But, when running the code on my server, I get the following output:

    I'm refreshing my memory, and am having strange behavior playing around with pointers-jxrsfiu-png

    What is going on with that second memory address? Is it my server?

    Thank you for any help.
    Attached Images Attached Images I'm refreshing my memory, and am having strange behavior playing around with pointers-mrfeorw-png 

  2. #2
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Code:
    // array of 2 integers (8 bits)
        int **int_pointer_array = malloc(2*sizeof(int *));
    This is an array of two pointers to integers, not to integers.
    I run it and got the correct results.
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  3. #3
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Quote Originally Posted by std10093 View Post
    Code:
    // array of 2 integers (8 bits)
        int **int_pointer_array = malloc(2*sizeof(int *));
    This is an array of two pointers to integers, not to integers.
    I run it and got the correct results.
    You are right, my comment is incorrect, but the idea is still the same. I had co-workers run it as well and get the correct results. I think it's the architecture of my server, looking into it now.
    Last edited by kbfirebreather; 02-06-2013 at 07:25 AM.

  4. #4
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    (As an aside, whenever you refer to "bits" in your comments, this should really be "bytes".)

    This is strange - 426311680 is 0x19690000, and 0x1969 is 6505, so it's as if you're somehow realigning your read/write by two bytes.

    You shouldn't use "main_pointer + 6" - use "main_pointer + sizeof(int *)" as otherwise you'll run into problems if pointers are more than 6 B on your computer.

    What optimizations are you using? What system are you compiling on/for (and are they the same?) and what's the size of int and int* on that system/those systems? (i.e. what do you get if you run printf("int: %d, int*: %d\n", sizeof(int), sizeof(int *))
    Last edited by JohnGraham; 02-06-2013 at 07:34 AM.
    Programming and other random guff: cat /dev/thoughts > blogspot.com (previously prognix.blogspot.com)

    ~~~

    "The largest-scale pattern in the history of Unix is this: when and where Unix has adhered most closely to open-source practices, it has prospered. Attempts to proprietarize it have invariably resulted in stagnation and decline."

    Eric Raymond, The Art of Unix Programming

  5. #5
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Okay, so I figured out what's happening, but not sure why or how to deal with it.

    I modified the code a little bit:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void){
    
    	//10 byte bank
    	char *main_pointer = malloc(10);
    
    	// array of pointers to 2 integers pointers (8 bytes)
    	int **int_pointer_array = malloc(2*sizeof(int *));
    
    	// set first integer pointer in array to point to byte 0 in 10 byte bank
    	int_pointer_array[0] = (int *) main_pointer;
    
    	// set the second integer pointer in array to point to byte 6 in 10 byte bank
    	int_pointer_array[1] = (int *) (main_pointer + 6);
    
    	// assign values
    	*int_pointer_array[0] = 6505;
    	*int_pointer_array[1] = 5;
    
    	// print values
    	printf("The values are %d and %i\n", *int_pointer_array[0], *int_pointer_array[1]);
    	printf("The values are %d and %d\n", *int_pointer_array[0], *(main_pointer+4));
    
    	int i = 0;
    	for (i = 0; i < 10; i++){
    		printf("main_ptr char byte %d, value is : %i\n", i, *(main_pointer + i));
    	}
    
    	printf("Pointer[0] is pointing to: %p\n", int_pointer_array[0]);
    	printf("Pointer[1] is pointing to: %p\n", int_pointer_array[1]);
    
    	return 0;
    
    
    }
    And the output of said code:

    Code:
    The values are 6505 and 327680
    The values are 6505 and 5
    main_ptr char byte 0, value is : 105
    main_ptr char byte 1, value is : 25
    main_ptr char byte 2, value is : 0
    main_ptr char byte 3, value is : 0
    main_ptr char byte 4, value is : 5
    main_ptr char byte 5, value is : 0
    main_ptr char byte 6, value is : 0
    main_ptr char byte 7, value is : 0
    main_ptr char byte 8, value is : 0
    main_ptr char byte 9, value is : 0
    Pointer[0] is pointing to: 0x11008
    Pointer[1] is pointing to: 0x1100e
    So as you can see, instead of the line *int_pointer_array[1] = 5; putting the value 5 at byte 6, it is actually putting it at byte 4, and when it prints it out via the printf, it's actually interpreting it as 0x50000, which is = 327680.

    Why is this happening, and what should I do about it?

    Thank you for any help.
    Last edited by kbfirebreather; 02-06-2013 at 07:43 AM.

  6. #6
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Quote Originally Posted by JohnGraham View Post
    (As an aside, whenever you refer to "bits" in your comments, this should really be "bytes".)

    This is strange - 426311680 is 0x19690000, and 0x1969 is 6505, so it's as if you're somehow realigning your read/write by two bytes.

    You shouldn't use "main_pointer + 6" - use "main_pointer + sizeof(int *)" as otherwise you'll run into problems if pointers are more than 6 B on your computer.

    What optimizations are you using? What system are you compiling on/for (and are they the same?) and what's the size of int and int* on that system/those systems? (i.e. what do you get if you run printf("int: %d, int*: %d\n", sizeof(int), sizeof(int *))

    Size of int, and int* both return the value of 4.

    Yes, i would normally do as you suggested, main_pointer + sizeof(int *), but the point behind this little script is to leave bits 4 and 5 as dead space, so the code is a little unorthodox.

    I'm compiling on a pogoplug, which is using ARMv6l architecture, I believe. Pogoplug Pro/Video/v3 | Arch Linux ARM

    And yes, you're evaluation is correct, as you can see from my post above ( I switched values around to make it a little simpler). Not sure why this is happening though.
    Last edited by kbfirebreather; 02-06-2013 at 07:44 AM.

  7. #7
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    I get the correct output compiling for x86 natively, but the exact same output as yourself when I cross-compile for ARM5TE.

    I think you're running into strict aliasing issues, but I'm not sure why.

    (Edited because I said I could get it to work correctly by changing a type but I was using that type wrong, I can't).
    Last edited by JohnGraham; 02-06-2013 at 08:01 AM.
    Programming and other random guff: cat /dev/thoughts > blogspot.com (previously prognix.blogspot.com)

    ~~~

    "The largest-scale pattern in the history of Unix is this: when and where Unix has adhered most closely to open-source practices, it has prospered. Attempts to proprietarize it have invariably resulted in stagnation and decline."

    Eric Raymond, The Art of Unix Programming

  8. #8
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Quote Originally Posted by JohnGraham View Post
    I get the correct output compiling for x86 natively, but the exact same output as yourself when I cross-compile for ARM5TE.

    I think you're running into strict aliasing issues, but I'm not sure why.

    (Edited because I said I could get it to work correctly by changing a type but I was using that type wrong, I can't).
    Yeah, it works beautifully on x86 machines, but the ARM is definitely doing something strange.

  9. #9
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    Okay, so here's what I've got after some testing - it appears all the writes are being "rectified" to be aligned to a 4-byte boundary, but the reads aren't. I don't know ARM assembler at all, so I don't know why this is.

    However, if you're determined to get two bytes between two int values, using a struct like this works:

    Code:
    struct {
            int  i1;
            char c1;
            char c2;
            int  i2;
    } __attribute((packed)) *block = malloc(10);
    
    // Reads/writes like this work for me.
    block->i2 = VALUE;
    But writing through any int* pointer doesn't work at all. I suspect the use of a properly laid-out struct forces gcc (which is what I assume you're using?) to generate code that's more akin to a memcpy() than a "regular" load/store to get around this because it knows it'll have to.
    Programming and other random guff: cat /dev/thoughts > blogspot.com (previously prognix.blogspot.com)

    ~~~

    "The largest-scale pattern in the history of Unix is this: when and where Unix has adhered most closely to open-source practices, it has prospered. Attempts to proprietarize it have invariably resulted in stagnation and decline."

    Eric Raymond, The Art of Unix Programming

  10. #10
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    Now it seems that on my system the write is like two 2 B values getting swapped - i.e. writing like:

    Code:
    *int_pointer_array[1] = 0xabcd1234;
    results in 0x1234abcd being read in printf(). Also if I memcpy() to the correct address, I can't get printf() to read the correct value through a raw pointer, so I suspect reads aren't happening correctly after all. I don't know why this is, but like I said the struct method works for me. If you need to find out, the gcc-help mailing list is probably the next place to go.
    Programming and other random guff: cat /dev/thoughts > blogspot.com (previously prognix.blogspot.com)

    ~~~

    "The largest-scale pattern in the history of Unix is this: when and where Unix has adhered most closely to open-source practices, it has prospered. Attempts to proprietarize it have invariably resulted in stagnation and decline."

    Eric Raymond, The Art of Unix Programming

  11. #11
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Thanks for taking the time looking into this. Very strange. I'll just leave it as is at the moment as this should hopefully not be a problem for me as long as I don't do weird offsets like the example program. Nothing I'll be writing will be anything ground breaking or overly complicated. Or maybe I'll switch to an x86 architecture and avoid this whole mess. Thanks again.

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    What you're doing is strictly illegal.
    https://en.wikipedia.org/wiki/Data_structure_alignment
    "cast increases required alignment of target type" | Comp.Arch.Embedded | EmbeddedRelated.com

    If you try to access data on an unaligned boundary, then what happens next is a roll of the dice.
    - it works (as on x86)
    - it produces strange results (as on ARM)
    - it is an expensive trap into the OS to fix things up
    - it is a bus error, and your code is just a smoking ruin of what it once was.

    If you're comping with gcc, consider using this flag.
    Code:
           -Wcast-align
               Warn whenever a pointer is cast such that the required alignment of
               the target is increased.  For example, warn if a "char *" is cast
               to an "int *" on machines where integers can only be accessed at
               two- or four-byte boundaries.
    Whilst __attribute((packed)) offer an easy way out, it does so at the expense of more complicated code. Fetching an unaligned int on ARM would result in two memory reads, a shift left, a shift right and a bit-wise or (compared to just a fetch).

    On the X86, the same thing happens in the bloated hardware of the memory access unit (which ARM, being a stripped down RISC doesn't have).

    > Okay, so I figured out what's happening, but not sure why or how to deal with it.
    The hacky approach to pulling unaligned data out of a data stream is something like
    memcpy( &myInt, main_pointer + 6, sizeof(myInt) );

    The sound way to do it is
    Code:
    myInt = 0;  // or your favourite int of a given size
    for ( i = 0 ; i < sizeof(myInt) ; i++ ) {
      myInt = (myInt << 8) | main_pointer[6+i];
    }
    Whilst this seems a rather long winded approach, not only is it immune to alignment issues, it is also immune to Endian issues as well.
    Once you've got the loop the right way round for the endian-ess of the input data, it doesn't matter at all what the endian of the host machine is.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Jan 2009
    Posts
    26
    Today I learned. Thank you for explaining that to me. Would have never found this out if it weren't for the system I was using!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help, please - very strange behavior
    By snork in forum C Programming
    Replies: 16
    Last Post: 09-26-2011, 01:36 AM
  2. strange behavior of pthread_create
    By np2k in forum Linux Programming
    Replies: 13
    Last Post: 05-12-2010, 02:48 AM
  3. Strange behavior
    By onako in forum C++ Programming
    Replies: 1
    Last Post: 05-01-2010, 07:00 AM
  4. strange std::map behavior
    By manav in forum C++ Programming
    Replies: 63
    Last Post: 04-11-2008, 08:00 AM
  5. strange behavior
    By agarwaga in forum C Programming
    Replies: 1
    Last Post: 10-17-2005, 12:03 PM