I am attempting to learn more about C and its arcane hidden powers, and I attempted to make a sample struct containing a pointer to a void, intended to use as array.
EDIT: Important note: This is for raw C code.
Let's say I have this struct.
typedef struct mystruct {
unsigned char foo;
unsigned int max;
enum data_t type;
void* data;
} mystruct;
I want data to hold max of either unsigned chars, unsigned short ints, and unsigned long ints, the data_t enum contains
values for those 3 cases.
enum Grid_t {gi8, gi16, gi32}; //For 8, 16 and 32 bit uints.
Then I have this function that initializes and allocates one of this structs, and is supposed to return a pointer to the new struct.
mystruct* new(unsigned char foo, unsigned int bar, long value) {
mystruct* new;
new = malloc(sizeof(mystruct)); //Allocate space for the struct.
assert(new != NULL);
new->foo = foo;
new->max = bar;
int i;
switch(type){
case gi8: default:
new->data = (unsigned char *)calloc(new->max, sizeof(unsigned char));
assert(new->data != NULL);
for(i = 0; i < new->max; i++){
*((unsigned char*)new->data + i) = (unsigned char)value;
//Can I do anything with the format new->data[n]? I can't seem
//to use the [] shortcut to point to members in this case!
}
break;
}
return new;
}
The compiler returns no warnings, but I am not too sure about this method. Is it a legitimate way to use pointers?
Is there a better way©?
I missed calling it. like mystruct* P; P = new(0,50,1024);
Unions are interesting but not what I wanted. Since I will have to approach every specific case individually anyway, casting seems as good as an union. I specifically wanted to have much larger 8-bit arrays than 32-bits arrays, so an union doesn't seem to help. For that I'd make it just an array of longs :P
No, you cannot dereference a void* pointer, it is forbidden by the C language standard. You have to cast it to a concrete pointer type before doing so.
As an alternative, depending on your needs, you can also use a union in your structure instead of a void*:
typedef struct mystruct {
unsigned char foo;
unsigned int max;
enum data_t type;
union {
unsigned char *uc;
unsigned short *us;
unsigned int *ui;
} data;
} mystruct;
At any given time, only one of data.uc, data.us, or data.ui is valid, as they all occupy the same space in memory. Then, you can use the appropriate member to get at your data array without having to cast from void*.
What about
typedef struct mystruct
{
unsigned char foo;
unsigned int max;
enum data_t type;
union
{
unsigned char *chars;
unsigned short *shortints;
unsigned long *longints;
};
} mystruct;
That way, there is no need to cast at all. Just use data_t to determine which of the pointers you want to access.
Is type supposed to be an argument to the function? (Don't name this function or any variable new or any C++ programmer who tries to use it will hunt you down)
If you want to use array indices, you can use a temporary pointer like this:
unsigned char *cdata = (unsigned char *)new->data;
cdata[i] = value;
I don't really see a problem with your approach. If you expect a particular size (which I think you do given the name gi8 etc.) I would suggest including stdint.h and using the typedefs uint8_t, uint16_t, and uint32_t.
A pointer is merely an address in the memory space. You can choose to interpret it however you wish. Review union for more information on how you can interpret the same memory location in multiple ways.
casting between pointer types is common in C and C++, and the use of void* implies that you dont want users to accidentally dereference (dereferencing a void* will cause an error, but dereferencing the same pointer when cast to int* will not)
Related
this is my code
#include <stdio.h>
#include <string.h>
#include <stdint.h>
struct mystruct{
int i;
int j;
long k;
long l;
char str[11];
};
int main()
{
struct mystruct obj;
obj.i=5;obj.j=55;obj.k=6;obj.k=1000001;obj.l=2000007;memcpy(obj.str,"hello",sizeof("hello"));
long addr=(long)((uint8_t *)&obj);
struct mystruct *myobj=(struct mystruct *)(addr);
printf("%d %d %zu %zu %s\n",myobj->i,myobj->j,myobj->k,myobj->l,myobj->str);
printf("%zu=%zu\n",sizeof(long),sizeof(myobj));
return 0;
}
So I like to know can I safe the address of any object (struct,union, etc.) or any type variable (int,long,char) into long variable. I showed the the code sizeof struct address or pointer is same as long. Is my above code is OK.
Also does ebpf varifier allows this?
Also if I have map with value of type long
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, long);
__uint(max_entries, 2);
} hash_map1 SEC(".maps");
can I do extract my struct object like following from long type for map value
struct hash_elem {
int cnt;
struct bpf_spin_lock lock;
};
from long type. is this possible in ebpf/xdp?
long addr=(long)((uint8_t *)&obj);
struct mystruct *myobj=(struct mystruct *)(addr);
I don't see a reason why the BPF verifier should reject this; you are just casting into and from long.
This will be compiled into a 64-bit BPF register anyway and type information will be lost (unless using BTF). The verifier will then infer a type for its own purpose and will correctly recognize this as a pointer to the stack (PTR_TO_STACK).
can I do extract my struct object like following from long type for map value
No, that wouldn't make sense. If you store in the map a pointer to the stack, then when your program exists, the pointer may be pointing to invalid memory.
Why not simple:
uintptr_t addr=(uintptr_t)&obj;
struct mystruct *myobj=(struct mystruct *)(addr);
uintptr_t type is guaranteed to keep any pointer converted to an integer and it is widely used in the BPF code.
It's sometimes necessary to cast a data structure into a pointer so that the data can be sent, for example, over an interface, or written out to some other stream. In these cases, I usually do something like this:
typedef struct {
int field1;
char field2;
} testStruct;
int main()
{
char *buf;
testStruct test;
buf = (char *)&test;
// write(buf, sizeof(test)) or whatever you need to do
return 0;
}
Recently in some microprocessor code, however, I saw something similar to this:
typedef struct {
int field1;
char field2;
} testStruct;
int main()
{
char buf[5];
testStruct test;
*(testStruct *)buf = test;
// write(buf, sizeof(test)) or whatever you need to do
return 0;
}
To me, the former feels a little more safe. You just have one pointer, and you assign the address of the structure to the pointer.
In the latter case, it seems like if you allocate the wrong size to the array buf by accident, you'll end up with undefined behavior, or a segfault.
With optimizations on, I get a -Wstrict-aliasing warning from gcc. However, again, this code runs on a microprocessor, so is there something I might be missing there?
There's no pointers in the structures, or anything, it's very straight forward.
(testStruct *)buf may generate a mis-aligned address for a testStruct leading to a bus fault. Do not use.
A union is better. It helps cope with anti-aliasing issues as well as alignment ones.
Also see #Steve Summit's good comment.
Consider a master type like testStruct_all.
typedef struct { // OP's structure
int field1;
char field2;
} testStruct1;
typedef struct { // Perhaps another structure to send
double field1;
char field2;
} testStruct2;
// A union of all possible structures used in this app
typedef union {
testStruct1 tS1;
testStruct2 tS2;
char buf[1];
} testStruct_all;
int main(void) {
testStruct_all ux;
foo(&ux.tS1); // populate ux.tSn of choice.
write(ux.buf, sizeof ux.tS1);
read(ux.buf, sizeof ux.tS1);
// the union insures alignment and avoids AA issues
bar(&ux.tS1);
return 0;
}
write() usually accepts a void * #user58697, so code could drop the buf member and use:
write(&ux, sizeof ux.tS1); // or whatever you need to do
I'm writing a C program in which I define two types:
typedef struct {
uint8_t array[32];
/* struct A's members */
...
} A;
typedef struct {
uint8_t array[32];
/* struct B's members, different from A's */
...
} B;
Now I would like to build a data structure which is capable of managing both types without having to write one for type A and one for type B, assuming that both have a uint8_t [32] as their first member.
I read how to implement a sort of polymorphism in C here and I also read here that the order of struct members is guaranteed to be kept by the compiler as written by the programmer.
I came up with the following idea, what if I define the following structure:
typedef struct {
uint8_t array[32];
} Element;
and define a data structure which only deals with data that have type Element? Would it be safe to do something like:
void f(Element * e){
int i;
for(i = 0; i < 32; i++) do_something(e->array[i]);
}
...
A a;
B b;
...
f(((Element *)&a));
...
f(((Element *)&b));
At a first glance it looks unclean, but I was wondering whether there are any guarantees that it will not break?
If array is always the first in your struct, you can simply access it by casting pointers. There is no need for a struct Element. You data structure can store void pointers.
typedef struct {
char array[32];
} A;
typedef struct {
void* elements;
size_t elementSize;
size_t num;
} Vector;
char* getArrayPtr(Vector* v, int i) {
return (char*)(v->elements) + v->elementSize*i;
}
int main()
{
A* pa = malloc(10*sizeof(A));
pa[3].array[0] = 's';
Vector v;
v.elements = pa;
v.num = 10;
v.elementSize = sizeof(A);
printf("%s\n", getArrayPtr(&v, 3));
}
but why not have a function that works with the array directly
void f(uint8_t array[32]){
int i;
for(i = 0; i < 32; i++) do_something(array[i]);
}
and call it like this
f(a.array)
f(b.array)
polymorphism makes sense when you want to kepp
a and b in a container of some sorts
and you want to iterate over them but you dont want to care that they are different types.
This should work fine if you, you know, don't make any mistakes. A pointer to the A struct can be cast to a pointer to the element struct, and so long as they have a common prefix, access to the common members will work just fine.
A pointer to the A struct, which is then cast to a pointer to the element struct can also be cast back to a pointer to the A struct without any problems. If element struct was not originally an A struct, then casting the pointer back to A will be undefined behavior. And this you will need to manage manually.
One gotcha (that I've run into) is, gcc will also allow you to cast the struct back and forth (not just pointer to struct) and this is not supported by the C standard. It will appear to work fine until your (my) friend tries to port the code to a different compiler (suncc) at which point it will break. Or rather, it won't even compile.
I have these three structures,
typedef struct serial_header {
int zigbeeMsgType;
int seqNumber;
int commandIdentifier;
int dest;
int src;
}serial_header_t;
typedef struct serial_packet {
serial_header_t header;
int data[];
} serial_packet_t;
and last one is
typedef struct readAttributePacket
{
int u8SourceEndPointId;
int u8DestinationEndPointId;
int u16ClusterId;
int bDirectionIsServerToClient;
int u8NumberOfAttributesInRequest;
int bIsManufacturerSpecific;
int u16ManufacturerCode;
int pu16AttributeRequestList[];
}readAttributePacket_t;
I am troubling with this code, i just want to cast the data[] array which reside in serial_packet_t into readAttributePacket_t structure.
I think the data[] should be
data[]={0x01,0x01,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01};
I need to cast those data to readAttributePacket_t structure. But this below code showing wrong.
void main()
{
int a[]= {0x32,0x00,0x31,0x69,0x69,0x00,0x00,0x01,0x01,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01};
int i;
readAttributePacket_t *p;
serial_packet_t *data;
data = (serial_packet_t*)&a;
for(i=0;i<20;i++){
printf(" %02x \n",a[i]);
}
p = (readAttributePacket_t *)&data->data;
printf("\nu8SourceEndPointId:%x \nu8DestinationEndPointId:%x \nu16ClusterId:%04x \nbDirectionIsServerToClient:%x \nu8NumberOfAttributesInRequest:%x \nbIsManufacturerSpecific:%x \nu16ManufacturerCode:%04x",p->u8SourceEndPointId,
p->u8DestinationEndPointId,
p->u16ClusterId,
p->bDirectionIsServerToClient,
p->u8NumberOfAttributesInRequest,
p->bIsManufacturerSpecific,
p->u16ManufacturerCode);
getch();
}
the output should be like
u8SourceEndPointId=01
u8DestinationEndPointId=01
u16ClusterId=0402
bDirectionIsServerToClient=00
u8NumberOfAttributesInRequest=02
bIsManufacturerSpecific=00
u16ManufacturerCode=0000
How could I get the pu16AttributeRequestList[] array into readAttributePacket_t structure, should like that,
pu16AttributeRequestList[0]=0000
pu16AttributeRequestList[1]=0001
You can't just cast an array to a structure because they're simply incompatible types. Due to memory alignment constraints, the compiler needs to insert padding between the fields of a structure, so the members are not located at the memory addresses you may expect. Solutions:
Portable but slower/harder to do manually (preferred): copy manually the fields of the structure to the array.
Shorter to write but GCC-specific: use the __attribute__((packed)) keyword to make GCC not introduce padding between struct fields.
Construct a union of 3 structs. all on equal memory space. then you dont even need to cast.
I think the only thing that you need to do in to remove the address operator from the casting statement.
data = (serial_packet_t*)a;
instead of
data = (serial_packet_t*)&a;
as far as I know, everything should work fine from here.
I've run across this source in a legacy code base and I don't really know why exactly it behaves the way it does.
In the following code, the pData struct member either contains the data or a pointer to the real data in shared memory. The message is sent using IPC (msgsnd() and msgrcv()). Using the pointer casts (that are currently commented out), it fails using GCC 4.4.1 on an ARM target, the member uLen gets modified. When using memcpy() and everything works as expected. I can't really see what is wrong with the pointer casting. What is wrong here?
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
unsigned char pData[8000];
} message_t;
// changing the pointer in the struct
{
unsigned char *pData = <some_pointer>;
#if 0
*((unsigned int *)pMessage->pData) = (unsigned int)pData;
#else
memcpy(pMessage->pData, &pData, sizeof(unsigned int));
#endif
}
// getting the pointer out
{
#if 0
unsigned char *pData; (unsigned char *)(*((unsigned int *)pMessage->pData));
#else
unsigned char *pData;
memcpy(&pData, pMessage->pData, sizeof(int));
#endif
}
I suspect it's an alignment problem and either GCC or the processor is trying to compensate. The structure is defined as:
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
unsigned char pData[8000];
} message_t;
Assuming normal alignment restrictions and a 32-bit processor, the offsets of each field are:
mtype 0 (alignment 4)
uRespQueue 4 (alignment 2)
uID 6 (alignment 2)
uLen 8 (alignment 2)
pData 10 (alignment 1)
On all but the most recent versions of the ARM processor, memory access must be aligned on the ARM processor and with the casting:
*((unsigned int *)pMessage->pData) = (unsigned int)pData;
you are attempting to write a 32-bit value on a misaligned address. To correct the alignment, the address appears to have truncated the LSB's of the address to have the proper alignment. Doing so happened to overlap with the uLen field causing the problem.
To be able to handle this correctly, you need to make sure that you write the value to a properly aligned address. Either offset the pointer to align it or make sure pData is aligned to be able to handle 32-bit data. I would redefine the structure to align the pData member for 32-bit access.
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
union { /* this will add 2-bytes of padding */
unsigned char *pData;
unsigned char rgData[8000];
};
} message_t;
The structure should still occupy the same amount of bytes since it has a 4-byte alignment due to the mtype field.
Then you should be able to access the pointer:
unsigned char *pData = ...;
/* setting the pointer */
pMessage->pData = pData;
/* getting the pointer */
pData = pMessage->pData;
That is a very nasty thing to do (the thing that's compiled out). You're trying basically to hack the code, and instead of using the data copy in the message (in the provided 8000 bytes for it), you try to put a pointer, and pass it through IPC.
The main issue is sharing memory between processes. Who knows what happens to that pointer after you send it? Who knows what happens to the data it points to? That's a very bad habbit to send out a pointer to data that is not under your control (i.e.: not protected/properly shared).
Another thing that might happen, and is probably what you're actually talking about, is the alignment. The array is of char's, the previous member in the struct is short, the compiler might attempt packing them. Recasting char[] to int * means that you take memory area and represent it as something else, without telling the compiler. You're stomping over the uLen by the cast.
memcopy is the proper way to do it.
The point here is the code "int header = (((int)(txUserPtr) - 4))"
Illustration of UserTypes and struct pointer casting is great of help!
typedef union UserTypes
{
SAUser AUser;
BUser BUser;
SCUser CUser;
SDUser DUser;
} UserTypes;
typedef struct AUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} AUser;
typedef struct AUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} AUser;
typedef struct BUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} BUser;
typedef struct CUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} CUser;
typedef struct DUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} DUser;
//this is the function I want to test
void Fun(UserTypes * txUserPtr)
{
int header = (*((int*)(txUserPtr) - 4));
//the problem is here
//how should i set incoming pointer "txUserPtr" so that
//Fun() would skip following lines.
// I don't want to execute error()
if((header & 0xFF000000) != (int)0xAA000000)
{
error("sth error\n");
}
/*the following is the rest */
}