I have a buffer where each entry in the buffer is 8 bits in size:
uint8_t Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
What I need to do is to create pointers to that array, for example 16 bit and 32 bit pointers. For example:
uint32_t *x;
x = Buffer;
uint32_t *y;
y = Buffer+4;
uint16_t *z;
z = Buffer+8;
Where each variable would then read from the array, for example:
x = 0x78563412
y = 0xf4f3f2f1
z = 0xf6f5
This works completely fine, the problem is that I'm getting warnings about incompatible pointer types. So I was wondering if there is an alternative way of doing this or if I'll just have to live with the warnings? Or am I simply doing this completely the wrong way?
Please note that this code will be executed on a single type of platform where the endianness is always the same and the size of data types is always the same.
You should heed the warnings; what you're doing is undefined behavior. Type aliasing like that is undefined behavior, particularly since there is no guarantee that Buffer has the correct alignment such that it can be accessed as an int/short. If Buffer has the correct alignment, then you can just explicitly cast and it's okay (and the warning will go away).
You have two options:
One, you align the buffer as the larger of the two types. Be careful that your pointer arithmetic doesn't throw off the alignment:
#include <stdalign.h>
alignas(int) unsigned char Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
unsigned int *x;
x = (unsigned int*)(Buffer);
unsigned int *y;
y = (unsigned int*)(Buffer+4);
unsigned short *z;
z = (unsigned short*)(Buffer+8);
Two, you create an unsigned int/unsigned short variable and then memcpy the bytes you're interested in into the variable:
unsigned int x;
memcpy(&x, Buffer, 4);
unsigned int y;
memcpy(&y, Buffer + 4, 4);
unsigned short z;
memcpy(&z, Buffer + 8, 2);
The problem with your approach is that it assumes a particular endianness of underlying hardware. Different computers will interpret a sequence of hex bytes
01 23 45 67
as eiter
01234567 or 67452301
Your program may compile and run on both systems, but since the result is hardware-specific, the compiler must warn you of the possibility.
The proper way of forcing a particular endianness is by using an array of integers, convert them using hton and ntoh functions, and set individual bytes either directly by casting a pointer to unsigned char*, or with memcpy.
You might want to use a union
#include <stdint.h>
#include <stdio.h>
typedef union{
uint8_t Buffer[10];
struct{
uint32_t x;
uint32_t y;
uint16_t z;
};
}MYSTRUCT;
int main(){
MYSTRUCT b = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
printf("x=%#x y=%#x z=%#x\n",b.x,b.y,b.z);
return 0;
}
Related
For some reason that i cant quite figure out my union of just structs containing bit fields is setting up twice as many bytes as is are necessary for any single struct.
#include <stdio.h>
#include <stdlib.h>
union instructionSet {
struct Brane{
unsigned int opcode: 4;
unsigned int address: 12;
} brane;
struct Cmp{
unsigned int opcode: 4;
unsigned int blank: 1;
unsigned int rsvd: 3;
unsigned char letter: 8;
} cmp;
struct {
unsigned int rsvd: 16;
} reserved;
};
int main() {
union instructionSet IR;// = (union instructionSet*)calloc(1, 2);
printf("size of union %ld\n", sizeof(union instructionSet));
printf("size of reserved %ld\n", sizeof(IR.reserved));
printf("size of brane %ld\n", sizeof(IR.brane));
printf("size of brane %ld\n", sizeof(IR.cmp));
return 0;
}
All of the calls to sizeof return 4 however to my knowledge they should be returning 2.
There are a couple of problems here, first of all, your bitfield Brane is using unsigned int which is 4 byte.
Even if you just use half of the bits, you still use a full 32-bit width unsigned int.
Second, your Cmp bitfields uses two different field types, so you use 8-bit of the 32-bit unsigned int for your first 3 fields, and then you use a unsigned char for it's full 8-bit.
Because of data alignement rules, this structure would be at least 6 bytes, but potentially more.
If you wanted to optimize the size of your union to only take 16-bit. Your first need to use unsigned short and then you need to always use the same field type to keep everything in the same space.
Something like this would fully optimize your union:
union instructionSet {
struct Brane{
unsigned short opcode: 4;
unsigned short address: 12;
} brane;
struct Cmp{
unsigned short opcode: 4;
unsigned short blank: 1;
unsigned short rsvd: 3;
unsigned short letter: 8;
} cmp;
struct {
unsigned short rsvd: 16;
} reserved;
};
This would give you a size of 2 all around.
C 2018 6.7.2.1 11 allows the C implementation to choose the size of the container is uses for bit-fields:
An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.…
The implementation you are using apparently chooses to use four-byte units. Likely that is also the size of an int in the implementation, suggesting that it is a convenient size for the implementation.
It isn't specified what this code will do and it isn't meaningful to reason about it without a specific system and compiler in mind. Bit-fields are simply too poorly specified in the standard to be reliably used for things like memory layouts.
union instructionSet {
/* any number of padding bits may be inserted here */
/* we don't know if what will follow is MSB or LSB */
struct Brane{
unsigned int opcode: 4;
unsigned int address: 12;
} brane;
struct Cmp{
unsigned int opcode: 4;
unsigned int blank: 1;
unsigned int rsvd: 3;
/* anything can happen here, "letter" can merge with the previous
storage unit or get placed in a new storage unit */
unsigned char letter: 8; // unsigned char does not need to be supported
} cmp;
struct {
unsigned int rsvd: 16;
} reserved;
/* any number of padding bits may be inserted here */
};
The standard lets the compiler pick a "storage unit" for any bit-field type, which can be of any size. The standard simply states:
An implementation may allocate any addressable storage unit large enough to hold a bitfield.
Things we can't know:
How large the bitfields of type unsigned int are. 32 bits might make sense but no guarantee.
If unsigned char is allowed for bit-fields.
How large the bitfields of type unsigned char are. Could be any size from 8 to 32.
What will happen if the compiler picked a smaller storage unit than the expected 32 bits, and the bits doesn't fit inside it.
What happens if an unsigned int bit-field meets an unsigned char bit-field.
If there will be padding in the end of the union or in the beginning (alignment).
How individual storage units within the structs are aligned.
The location of the MSB.
Things we can know:
We have created some sort of binary blob in memory.
The first byte of the blob resides on the least significant address in memory. It may contain data or padding.
Further knowledge can be obtained by having a very specific system and compiler in mind.
Instead of the bit-fields we can use 100% portable and deterministic bitwise operations, that yield the same machine code anyway.
Read about memory structure padding / memory alignment. By default 32bit processor read from memory by 32bit (4bytes) because is faster. So in memory char + uint32 will be write on 4 + 4 = 8 bytes (1byte - char, 3bytes space, 4bytes uint32).
Add those lines on begin and end of your program and will be result 2.
#pragma pack(1)
#pragma unpack
This is way to say to the compiler: align memory to 1 byte (by default 4 on 32bit processor).
PS: try this example with different #pragma pack set:
struct s1
{
char a;
char b;
int c;
};
struct s2
{
char b;
int c;
char a;
};
int main() {
printf("size of s1 %ld\n", sizeof(struct s1));
printf("size of s2 %ld\n", sizeof(struct s2));
return 0;
}
Having a little difficulty with pointers. I have to store a float in an array of unsigned ints and be able to pull it out.
I know there is a special way to cast this so I don't reorder the bits, I think this is the correct way to store it when I want to put it into the array:
float f = 5.0;
int newF = (int *) f;
arrayOfInts[0] = *newF
Which seems to successfully store the value in the array.
However, at some point I have to pull the value back out of the array of ints, this is where my confusion comes in (assuming I inputed into the array correctly)
float * f = (float *) arrayOfInts[0]
int result = *f;
however, that gives me the warning: 'cast to pointer from integer of different size'
I can't really think of how to solve that without some sort of long cast.. which doesn't seem right..
I don't want to lose the value or damage the bits.. obviously It will lose decimal point precision.. but I know theirs some way to safety convert back and forth
I have to store a float in an array of unsigned ints and be able to pull it out.
Use a union and unsigned char[]. unsigned char is specified to not have any padding and all bit combinations are valid. This is not always true of many other number types. By overlaying the float with unsigned char[], code can examine each "byte" of the float, one at a time.
union {
float f;
unsigned char uc[sizeof (float)];
} x;
// example usage
x.f = 1.234f;
for (unsigned i = 0; i<sizeof x.uc; i++) {
printf("%u:%u\n", i, 1u*x.uc[i]);
}
Sample output: Yours may vary
0:182
1:243
2:157
3:63
float --> unsigned char[] --> float is always safe.
unsigned char[] --> float --> unsigned char[] is not always safe as a combination of unsigned char[] may not have a valid float value.
Avoid pointer tricks and casting. There are alignment and size issues.
// Poor code
float f = 5.0f;
int newF = *((int *) &f); // Conversion of `float*` to `int*` is not well specified.
Code can also overlay with fixed-width no-padding types like (u)int32_t if they exist (they usually do) and match in size.
#include <stdint.h>
union {
float f;
uint32_t u32;
} x32;
#include <assert.h>
#include <inttypes.h>
// example usage
assert(sizeof x32.f == sizeof x32.u32);
x32.f = 1.234f;
printf("%" PRNu32 "\n", x32.u32);
}
Example output: yours may vary
1067316150
To convert a float to an int
float fval = 123.4f;
int ival = *(int*)&fval;
To convert back
int ival = /* from float */
float fval = *(float*) &ival;
it won't work if float and int are different sizes, but presumably you know that already. The unsigned char union method outlined in other answer for chux is more robust, but over-complicated for what you probably want to do.
I am relative new to programming so I apologize if my question is basic.
Situation:
I have several float values and an array of pointers to each value. Ex:
float nr1=1.15;
float nr2=2.30;
float nr3=23.34;
....
float * my_address_array[3];
my_address_array[0] = &nr1;
my_address_array[1] = &nr2;
my_address_array[2] = &nr3;
To access one element, I can use:
float temp_value;
float ** ptr_value;
...
ptr_value = &my_address_array[0];
temp_value = **( ptr_value+0); // copy nr1 to temp
temp_value = **( ptr_value+1); // copy nr2 to temp
temp_value = **( ptr_value+2); // copy nr3 to temp
So far so good. On my system float occupies 32 bits (8051 microcontroller). I need to take one float number and separate it into four 8 bit variables. Example for nr2:
My attempt was:
unsigned char storage1;
unsigned char storage2;
unsigned char storage3;
unsigned char storage4;
...
storage1 =(unsigned char) ((**( ptr_value+1)) >> 24) ;
storage2 =(unsigned char) ((**( ptr_value+1)) >> 16) ;
storage3 =(unsigned char) ((**( ptr_value+1)) >> 8) ;
storage4 =(unsigned char) ((**( ptr_value+1)) & 0xff) ;
I get bad operand type. It seems that I cannot use bit shift operations with float numbers (at least google sais that). I can add a new pointer, as in:
char ** ptr_char_value;
...
ptr_char_value = &my_address_array[0]; // generates warning
storage1 = (*(*( ptr_char_value+1)+0));
storage2 = (*(*( ptr_char_value+1)+1));
storage3 = (*(*( ptr_char_value+1)+2));
storage4 = (*(*( ptr_char_value+1)+3));
I do get a warning (which is fair) that I am using a char type pointer for a float value. I am also not sure how reliable this is. Can anyone advise a better solution?
Thank you!
Edit: The code is for a 8051 microcontroller. I would like to make it as fast/optimal as possible.
with this, you are getting pointer to pointer to value
ptr_char_value = &my_address_array[0];
I don't understand how this should work.
It would be better to use pointer to float directly. Cast it to char array and get separate bytes.
char * ptr_char_value = (char *)(my_address_array[0]);
storage1 = ptr_char_value[0];
storage2 = ptr_char_value[1];
storage3 = ptr_char_value[2];
storage4 = ptr_char_value[3];
EDIT:
It is also question if you realy need array of pointers to floats. It is possible to have struct, cast it to array of bytes and use these bytes directly for something.
struct my_data_type {
float nr1;
float nr2;
float nr3;
}
struct my_data_type my_data;
my_data.nr1 = 1.15;
my_data.nr2 = 4.75;
my_data.nr3 = 8.95;
char * ptr_char_value = (char *)&my_data;
// nr1
storage1 = ptr_char_value[0];
...
// nr2
storage5 = ptr_char_value[4];
...
// nr3
storage9 = ptr_char_value[8];
...
try this. You should type cast before you can shift the data. Include stdint.h
storage1 = (uint8_t) ((((uint32_t)**( ptr_value+1))) >> 24) & 0xFF );
Let's begin with the title:
Different pointer types for the same address
That's code smell. In C, aliasing objects through an incompatible pointer type results in undefined behavior, unless the incompatible aliasing type is (qualified or unqualified, signed or unsigned) character type.
More precisely, an object of type cv-qualified or unqualified T may only be accessed through a pointer to qualified or unqualified T. Additionally, if T is an integral type, then accessing via its (qualified or unqualified) signed or unsigned counterpart is permitted as well.
This is colloquially called the "strict aliasing rule".
Hence, you can't do
float f;
*(int *)&f
even if sizeof(float) == sizeof(int).
The possible solutions:
Use memcpy(), like this (preferred):
float f = 3.14;
int i;
memcpy(&i, &f, sizeof f);
Use a union (C99 only – this is UB too in C89…):
union {
float f;
int i;
} pun = { .f = 3.14 };
printf("%d\n", pun.i);
Why do you want to separate float in 4 variables?
That don't make any sense because of how float is represented in computer.
First bit S is sing, 0 for positive 1 for negative numbers. Then 8 bits of exponent and remaining 23 for mantissa.
So, there is nothing useful you can do with 8bit parts, because they are on their own meaningless.
Here is the program in question:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct x{
int i1;
int i2;
char ch1[8];
char ch2[8];
};
struct y{
long long int f1;
char f2[18];
};
int main(void)
{
struct x * myX;
struct y * myY;
myX = malloc(sizeof(struct x));
myX->i1 = 4096;
myX->i2 = 4096;
strcpy(myX->ch1,"Stephen ");
strcpy(myX->ch2,"Goddard");
myY = (struct y *) myX;
printf("myY->f1 = %llx\n", myY->f1);
printf("myY->f2 = %s\n", myY->f2);
}
and here is the output:
myY -> f1 = 100000001000
myY -> f2 = Stephen Goddard
My question is how does this typecasting work? I don't understand the output for f1. 4096 is 100000000000 in binary and 1000 in hex, so is it combining i1 and i2 into the one long long int? If so, why does it combine in this way? Does the structure being cast just look for similar variables and try to cram them all into one variable? How does this process work? Any help would be greatly appreciated!
This code causes undefined behaviour. C has a rule called strict aliasing . To paraphrase it, the standard doesn't define what happens if you write via an int expression and read via a long long expression.
Traditionally most compilers would "just do it" , i.e. it would write the two ints into those two memory locations, and then read enough bytes for a long long out of the same location, and hope it all made sense (which it usually did, since modern systems are such that every possible bit-pattern does represent a valid number).
However some compilers optimize the step out entirely because they know about the strict aliasing rule.
A further consideration is that there could be padding in between i1 and i2 etc., however you could verify that there is none by using the offsetof macro or a sizeof check before proceeding.
To do what you want in a well-defined manner you can either use memcpy or use a union, e.g.:
union xy
{
struct x x;
struct y y;
};
union xy *ptr = malloc(sizeof *ptr);
ptr->x.i1 = 4096;
ptr->x.i2 = 4096;
printf("%llx\n", (unsigned long long)ptr->y.f1);
If your system has unpadded 2's complement (all modern systems do), and no padding between members of your structs, and 32-bit int, and 64-bit long long, and big-endian int, then 4096 is 00 00 10 00 in hex, so the 8-byte block is 00 00 10 00 00 00 10 00 which is 0x100000001000 as you see in your output. Actually it comes out the same in little-endian too.
Note, %llx is for printing unsigned long long, it causes undefined behaviour to use it with a signed long long however all compilers that I know of will "work anyway". To be strictly correct I have added in a cast. (Consider changing your struct members to be unsigned int and unsigned long long to avoid issues with sign bits).
I am new to the bit fields concept. I am trying to access the elements in the structure, but it is showing the error at aa=v like this.
error: incompatible types when assigning to type ‘cc’ from type ‘long unsigned int ’
And it is showing error if I typecasted at aa= (cc)v;
error: conversion to non-scalar type requested
I tried accessing the elements by declaring a pointer to a structure. I did well in this case, but in this case I do not declare a pointer to a structure and I have to access the elements. How can I overcome this error.
Thanks for any help in advance
#include<stdio.h>
typedef struct
{
unsigned long a:8;
unsigned long b:8;
unsigned long c:8;
unsigned long d:8;
}cc;
int main()
{
cc aa ;
unsigned long v = 1458;
printf("%d\n",sizeof(aa));
aa=v; // aa= (cc)v;
printf("%d %d %d %d\n", aa.a,aa.b,aa.c,aa.d);
return 0;
}
If you intend to access the same data as multiple data-types, then you need to use an union in C. Take a look at the following snippet that will
Write to a union treating it as a 32bit integer
(and then)
Access the data back as 4 individual 8bit bit-fields
(and also for good measure)
Access the same data back again as a 32bit integer
#include<stdio.h>
typedef struct {
unsigned long a:8;
unsigned long b:8;
unsigned long c:8;
unsigned long d:8;
}bitfields;
union data{
unsigned long i;
bitfields bf;
};
int main()
{
union data x;
unsigned long v = 0xaabbccdd;
printf("sizeof x is %dbytes\n",sizeof(x));
/* write to the union treating it as a single integer */
x.i = v;
/* read from the union treating it as a bitfields structure */
printf("%x %x %x %x\n", x.bf.a, x.bf.b, x.bf.c, x.bf.d);
/* read from the union treating it as an integer */
printf("0x%x\n", x.i);
return 0;
}
Note that when union is accessed as an integer, the endian-ness of the system determines the order of the individual bit-fields. Hence the above program on a 32bit x86 PC (little-endian) will output :
sizeof x is 4bytes
dd cc bb aa
0xaabbccdd
It's because aa is a structure and v is not, the types are incompatible just like the error message says. Even if cc is a structure of bitfields, it still can only be used as a structure, with separate members, and not like an integer.
Correct me if I'm wrong, but you wanted to assign a 4 byte sized long to your 4 byte sized struct. If yes, this might be for you:
aa = *(cc*)&v;
However you should be aware about endianness in this case
Use this :
aa.a = v;
Instead of
aa = v;
you are trying to assign a long to a struct which are incompatible.
you can assign inner value of cc to v:
cc c;
unsigned long v = 1458;
c.b = v;
if you want the first 8 bits to be in a for example you can do
cc c;
unsigned long v = 1458;
c.a = v % 255;
c.b = v / 255 % 65280;