简体   繁体   中英

Accessing an array using a pointer in a struct

I wanted to access the array elements using the struct attributes. I am able to print 1st and the 2nd element using the struct pointer ( nameptr) while the remaining 3 elements has to be accessed using the (uint8_t ptr) which itself is a attribute of the 'struct name'.

#include <iostream>

struct name{

    uint8_t b0;
    uint8_t b1;
    uint8_t* ptr;

}*nameptr;

int main()
{
    
    uint8_t arr[]= {1,2,3,4,5};
    nameptr = (name *)arr;
    nameptr->ptr = arr+2;
    printf("%d ", nameptr->b0);           //prints 1
    printf("%d ", nameptr->b1);           //prints 2
    for (int i=2; i<5; i++){
        printf("%d ",*(nameptr->ptr+i));  //expecting to print 3 4 5
    }

    return 0;
}

When compiled I get the below error, Please help me getting this error solved.

*** stack smashing detected ***: ./a.out terminated

1 2 5 0 56 Aborted (core dumped)

This answer is based on the modified code as in revision 6 of the question.

If you have a structure of type name , not only a pointer, you can assign the array address to the pointer element ptr like this.

#include <cstdio>
#include <cstdint>

struct name{

    uint8_t b0;
    uint8_t b1;
    uint8_t* ptr;

}*nameptr;

int main()
{

    uint8_t arr[]= {1,2,3,4,5};
    name name_struct; // actual memory where you can access the structure fields
    nameptr = &name_struct; // pointer now points to a real structure of the correct type
    nameptr->ptr = arr; // assign array to pointer inside structure

    /* For this loop you must know the size of the array the pointer is pointing to.
     * Your code cannot automatically derive this information from the
     * pointer element `ptr` or from the structure like you could with
     * sizeof(arr)/sizeof(arr[0]). */
    for (int i=2; i<5; i++){
        printf("%d ",*(nameptr->ptr+i));  //expecting to print 3 4 5
        // printf("%d ",nameptr->ptr[i]);  // same, but easier to understand
    }

    return 0;
}

The code in revision 7 of the question is wrong in several ways:

struct name{
    uint8_t b0;
    uint8_t b1;
    uint8_t* ptr;
}*nameptr;

/* Depending on your platform, the structure might be something like this */
struct name_with_padding {
    uint8_t b0; // 1st byte
    uint8_t b1; // 2nd byte
    // uint8_t padding[2]; // 3rd and 4th byte 
    uint8_t* ptr; // 5th to 8th byte
}*nameptr;

uint8_t arr[]= {1,2,3,4,5};

/* This cast is the main error. It will interpret the memory of the array
 * as a structure as shown above which is undefined behavior (and 
 * implementation dependent).
 * It might result in:
 * b0 = 1
 * b1 = 2
 * padding = {3, 4}
 * ptr = value 5 + some (3) bytes after it
 * But it may as well result in other (undefined) behavior.
 */
nameptr = (name *)arr;

/* This may (depending on your implementation) overwrite
 * overwrite the value 5 and the following (3) bytes
 * with the address arr+2
 * so it will change the value of arr[4] and the memory after it
 * This may (or may not) result in a segmentation fauult or stack corruption.
 */ 
nameptr->ptr = arr+2;

This answer is based on the original code in the first revision of the question and on my interpretation about the intention behind the wrong code.

See also my other answer based on the code in revision 6 of the question.

The structure element uint8_t* ptr; is a pointer, not an array. That's why, when doing nameptr = (name *)arr; and dereferencing nameptr->ptr , you interpret the data in memory as an address and try to access the memory at this address. The address is probably constructed from the bytes 3, 4, 5 and whatever follows it in memory. (implementation dependent)

This modified version of the original code might do what you want, but casting the array pointer arr to a structure pointer and accessing the structure elements is undefined behavior and highly depends on your implementation. (There might be padding between the array elements.)

#include <cstdio>
#include <cstdint>

struct name{

    uint8_t b0;
    uint8_t b1;
    uint8_t array[3];

} name1, *nameptr;

int main()
{
    
    uint8_t arr[]= {1,2,3,4,5};
    nameptr = (name *)arr;
    printf("%d\n",(int)nameptr->b0);
    printf("%d\n",(int)nameptr->b1);
    printf("%d\n",(int)nameptr->array[0]); //should print 3
    printf("%d\n",(int)nameptr->array[1]); //should print 4
    printf("%d\n",(int)nameptr->array[2]);

    return 0;
}

On the specific platform where I tested the program, the output is

1
2
3
4
5

see https://onlinegdb.com/HyY8BDUz _

But in fact it is undefined behavior and may produce different results depending on your implementation.

Apart the Undefined Behaviour that is invoked by casting an array to a struct, there is a size problem.

This is a slight variation of your code, still invoking UB, hat at least with no runtime error on gcc:

...

int main()
{

    uint8_t arr[sizeof(name)]= {1,2,3,4,5};
    nameptr = (name *)arr;
    nameptr->ptr = arr+2;
    for (int i=0; i<3; i++){  // SB: starting at 2 so only 0 to 3...
        printf("%d ",*(nameptr->ptr+i));  //expecting to print 3 4 5
    }

    return 0;
}

And when I execute this code under a debugger, I can see that arr has now size 8 (on my 32 bits environment). Because the actual layer of name is:

* b0
* b1
* padding bytes size 2
* ptr (size 4)

So in your original code, when you executed nameptr->ptr = arr+2; you were writing well past of the declared array, which is a nice recipe for SIGSEGV or other weird errors.


But anyway my code does not print 3, 4, 5. Even if it invokes undefined behaviour, my implementation does write the first byte of ptr at the place where was the 5 (see the above layer to understand why...). So I get 3, 4 and a random byte!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM