Let's say I'm creating a struct with mostly numeric fields. Then, I'd like to initialize these different fields with values that are ASCII chars, so that in the end, I could cast the struct as a char* pointer, and print it in one go, and obtain a sort of a per-byte "map" printout of the struct.
I came to the following example that compiles:
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
struct mystruct_s {
uint8_t id_tag[6];
uint8_t fld_one[2];
uint64_t fld_two;
uint16_t fld_three;
uint16_t fld_four;
uint16_t fld_five;
};
struct mystruct_s myrh = {
.id_tag = "123456",
.fld_one = "78",
.fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),
};
int main() {
printf("size %zu (%zu) bytes\n", sizeof(myrh), sizeof(struct mystruct_s));
printf("as string:\n");
printf("%s\n", (char*)&myrh);
printf("char 10: '%c'\n", ((char*)&myrh)[9]);
return 0;
}
... however, when it runs, it produces:
$ gcc -g main.c -o main.exe
$ ./main.exe
size 24 (24) bytes
as string:
12345678`�V
char 10: ''
... whereas I would have expected something like 12345678TTTTTTT (and char 10 being also 'T'). So, clearly my "cast" did not quite work.
So, is there some sort of syntax, where I could initialize a numeric field of a struct, with the values equivalent to a char array? (Note, the above example shows the intent to initialize all bytes in .fld_two to 'T', that is, 0x54 as per ASCII - but I'm ultimately interested in specifying arbitrary strings like this, with varying characters)
Second thing that I found surprising, is that if I add another field initialization:
...
struct mystruct_s myrh = {
.id_tag = "123456",
.fld_one = "78",
.fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),
.fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),
};
...
... then building fails with:
gcc -g main.c -o main.exe
main.c:18:16: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
.fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),
^
main.c:18:16: error: initializer element is not constant
main.c:18:16: note: (near initialization for ‘myrh.fld_three’)
So, how come it did NOT detect an error on this line:
.fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),
... and yet it did find an error on this line:
.fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),
... even if both lines are apparently doing the same thing, just with different sizes and values?
(uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' })
This creates a compound literal at the same scope as where it is used. Like any array, the uint8_t [8] decays into a pointer to the first element when used in an expression. So you end up casting an address to uint64_t, not the data.
You can solve this with union type punning. Something like:
typedef union
{
uint8_t bytes [8];
uint64_t u64;
} pun_intended;
...
.fld_two = (pun_intended){ .bytes = {'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T'} }.u64,
However that brings another problem, namely that it is no longer a compile-time constant expression. Which should of course ideally be solved by not using sloppy globals in the first place. But in case that is not an option (maybe you need const), you could alternatively change the type of the struct member:
pun_intended fld_two;
...
.fld_two = (pun_intended){ .bytes = {'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T'} },
Well, I sort of managed to do the effect I desired via unions - but there is so much typing and boilerplate, it defeats the purpose (I wanted the syntax in the OP because it appears easy :) )
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
struct mystruct_s {
uint8_t id_tag[6];
uint8_t fld_one[2];
uint64_t fld_two;
uint16_t fld_three;
uint16_t fld_four;
uint16_t fld_five;
} __attribute__((packed));
struct mystruct_alt_s {
unsigned char id_tag[6];
unsigned char fld_one[2];
unsigned char fld_two[8];
unsigned char fld_three[2];
unsigned char fld_four[2];
unsigned char fld_five[2];
} __attribute__((packed));
union undata {
struct mystruct_alt_s myo_alt;
struct mystruct_s myo;
} ;
union undata data = {
.myo_alt = {
.id_tag = "123456",
.fld_one = "78",
.fld_two = "TTTTTTTT",
.fld_three = "HH",
.fld_four = "RR",
.fld_five = "VV",
}
};
int main() {
printf("size %zu (%zu) bytes\n", sizeof(data), sizeof(struct mystruct_s));
printf("as string:\n");
printf("%s\n", (char*)&data.myo);
return 0;
}
This prints:
$ gcc -g main.c -o main.exe
$ ./main.exe
size 22 (22) bytes
as string:
12345678TTTTTTTTHHRRVV
As an alternative to your own answer, you may use the newly learned X Macros to gather the variable definitions and initialization strings in one place:
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
// X macro taking Type, Name, Count, ValueString
#define FIELDS \
X(uint8_t, id_tag, [6], "123456") \
X(uint8_t, fld_one, [2], "78") \
X(uint64_t, fld_two, , "TTTTTTTT") \
X(uint16_t, fld_three, , "HH") \
X(uint16_t, fld_four, , "RR") \
X(uint16_t, fld_five, , "VV")
#define X(T,N,C,V) T N C;
struct mystruct_s {
FIELDS
} __attribute__((packed));
#undef X
#define X(T,N,C,V) unsigned char N[sizeof(T C)];
struct mystruct_alt_s {
FIELDS
} __attribute__((packed));
#undef X
union undata {
struct mystruct_alt_s myo_alt;
struct mystruct_s myo;
} ;
#define X(T,N,C,V) .N = V,
union undata data = {
.myo_alt = {
FIELDS
}
};
#undef X
int main() {
printf("size %zu (%zu) bytes\n", sizeof(data), sizeof(struct mystruct_s));
printf("as string:\n");
printf("%s\n", (char*)&data.myo);
return 0;
}
Related
I have a struct variable of struct:
typedef struct message_t
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message;
message msg;
msg.time = 0x1234;
msg.lat = 0x2122;
msg.ns = 'n';
msg.lon = 0x2234;
msg.ew = 'e';
uint8_t msg_arr[8];
How to convert msg to uint8_t array msg_arr[8]?
By using memcpy to convert the data,
memcpy(msg_arr, &msg, sizeof(msg_arr));
for(int i=0; i < 8; i++){
printf("msg[%d]: %d \n", i, msg_arr[i]);
}
I get the output:
msg[0]: 52
msg[1]: 18
msg[2]: 34
msg[3]: 33
msg[4]: 110
msg[5]: 0
msg[6]: 52
msg[7]: 34
It seems something wrong with using memcpy. for example, msg[5], msg[6] and msg[7] are not correct.
Answer 1/3: use a union and a packed struct
See also my much longer answer here: Portability of using union for conversion
You can do the conversion to a byte array using a union. Be sure to pack the struct to remove padding bytes.
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
typedef union message_converter_u
{
message_t message;
uint8_t bytes[sizeof(message_t)];
} message_converter_t;
Now do the conversion through the message_converter_t union:
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
message_converter_t converter;
converter.message = message;
That's it!
converter.bytes is now magically a uint8_t array with 8 elements containing all the bytes of the struct.
It has endianness considerations, however!
Note: copying message into converter.message above is unnecessarily inefficient, since it's an unnecessary byte-for-byte copy of the whole message struct. A more-efficient way is to simply construct the union type alone and populate the struct data inside the union directly. See "struct_to_array_via_type_punning_union_more_efficient.c" below for that demo.
Here is some sample print code to print all the bytes:
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(converter.bytes); i++)
{
printf("0x%02X", converter.bytes[i]);
if (i < sizeof(converter.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
and the output on a 64-bit little-endian x86-architecture Linux machine:
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
Notice that due to my machine being little-endian, the least-significant-byte 0x34 comes first in the time variable of 0x1234. So, you get 0x34 and then 0x12. This happens with all of the multi-byte variables. To remove endianness considerations across hardware architectures, you'd have to move to a bit-shifting approach instead of using a union--see my link above for examples and more details, and also see my Answer 2/3 here.
Full, runnable example
struct_to_array_via_type_punning_union.c: <-- download it as part of my eRCaGuy_hello_world repo
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_unpacked_t;
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
typedef union message_converter_u
{
message_t message;
uint8_t bytes[sizeof(message_t)];
} message_converter_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("This is the start of `main()`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_unpacked_t) = %zu bytes\n", sizeof(message_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_t) = %zu bytes\n", sizeof(message_t)); // 8 bytes
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
message_converter_t converter;
// Note: copying `message` into `converter.message` here is unnecessarily inefficient. A
// more-efficient way is to simply construct the union type alone and populate the struct
// data inside the union directly. See "struct_to_array_via_type_punning_union_more_efficient.c"
// for that demo.
converter.message = message;
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(converter.bytes); i++)
{
printf("0x%02X", converter.bytes[i]);
if (i < sizeof(converter.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Build and run command:
mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union.c \
-o bin/struct_to_array_via_type_punning_union && bin/struct_to_array_via_type_punning_union
Sample output:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union.c \
> -o bin/struct_to_array_via_type_punning_union && bin/struct_to_array_via_type_punning_union
This is the start of `main()`.
sizeof(message_unpacked_t) = 10 bytes
sizeof(message_t) = 8 bytes
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
More-efficient technique: do NOT copy from a struct to a union! Just use the union alone as your message_t!
struct_to_array_via_type_punning_union_more_efficient.c
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_data_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_data_unpacked_t;
typedef struct __attribute__ ((__packed__)) message_data_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_data_t;
typedef union message_u
{
message_data_t data;
uint8_t bytes[sizeof(message_data_t)];
} message_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("This is the start of `main()`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_data_unpacked_t) = %zu bytes\n", sizeof(message_data_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_data_t) = %zu bytes\n", sizeof(message_data_t)); // 8 bytes
message_t message =
{
.data =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
},
};
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(message.bytes); i++)
{
printf("0x%02X", message.bytes[i]);
if (i < sizeof(message.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Sample output is the same as before:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union_more_efficient.c \
> -o bin/struct_to_array_via_type_punning_union_more_efficient && bin/struct_to_array_via_type_punning_union_more_efficient
This is the start of `main()`.
sizeof(message_data_unpacked_t) = 10 bytes
sizeof(message_data_t) = 8 bytes
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
Keywords: c type punning; c struct to byte array conversion; c array to bytes; c serialization; c struct serialization; c object serialization
Answer 2/3: convert a struct to an array of bytes via manual bit-shifting
Convert a struct to an array in C via bit shifting, which unlike the union technique, does NOT have
architecture-based endianness concerns or differences which can vary from hardware architecture to
architecture! Rather, the manual bit-shifting removes endianness concerns at the expense of
manually copying the bytes from the struct into an array (whereas the union and raw ptr techniques do NOT require copying any bytes)!
This technique being endianness-agnostic makes it better than the other two approaches (union and raw ptr) if you ever need to send serialized data between different systems of different endianness.
struct_to_array_via_bit_shifting.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
// Read a single byte at index `byte_num` from any multi-byte variable.
#define BYTE(value, byte_num) ((uint8_t)(((value) >> (8*(byte_num))) & 0xff))
// Notice with this technique the struct does NOT need to be packed! This is contrary to the
// union technique.
typedef struct message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
void message_struct_to_array(const message_t* message, uint8_t* bytes)
{
bytes[0] = BYTE(message->time, 0);
bytes[1] = BYTE(message->time, 1);
bytes[2] = BYTE(message->lat, 0);
bytes[3] = BYTE(message->lat, 1);
bytes[4] = BYTE(message->ns, 0);
bytes[5] = BYTE(message->lon, 0);
bytes[6] = BYTE(message->lon, 1);
bytes[7] = BYTE(message->ew, 0);
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Answer 2/3: convert a struct to an array of bytes via manual bit-shifting.\n");
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
// NB: this is NOT the same thing as `sizeof(message_t)` due to padding bytes which are in the
// `message_t` struct!
const size_t MESSAGE_NUM_BYTES = sizeof(message.time) + sizeof(message.lat) +
sizeof(message.ns) + sizeof(message.lon) + sizeof(message.ew);
uint8_t bytes[MESSAGE_NUM_BYTES];
message_struct_to_array(&message, bytes);
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(bytes); i++)
{
printf("0x%02X", bytes[i]);
if (i < sizeof(bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Sample output:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_bit_shifting.c \
> -o bin/struct_to_array_via_bit_shifting && bin/struct_to_array_via_bit_shifting
Answer 2/3: convert a struct to an array of bytes via manual bit-shifting.
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
Answer 3/3: use a packed struct and a raw uint8_t pointer to it
Convert a struct to an array in C via a raw pointer without doing any copies of the data, such as into an array via memcpy or via the bitshifting approach. This also has architecture endianness concerns, just like using a union.
The concept is super simple though. Just pack the struct and then look at it as though it was an array of bytes!--like this:
// Use a raw pointer
uint8_t * bytes = (uint8_t*)(&message);
It's essentially an explicit, manual union, since you're just looking at the data "through another lens", as I like to call it, rather than actually copying, manipulating, or changing the data.
Full example:
struct_to_array_via_type_punning_raw_pointer.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_unpacked_t;
// This MUST be packed to work properly without having padding bytes in the byte array!
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Answer 3/3: use a packed struct and a raw `uint8_t` pointer to it`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_unpacked_t) = %zu bytes\n", sizeof(message_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_t) = %zu bytes\n", sizeof(message_t)); // 8 bytes
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
// Use a raw pointer
uint8_t * bytes = (uint8_t*)(&message);
// Print the bytes of `message`
printf("bytes = [");
for (size_t i = 0; i < sizeof(message); i++)
{
printf("0x%02X", bytes[i]);
if (i < sizeof(message) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Build and run command:
mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_raw_pointer.c \
-o bin/struct_to_array_via_type_punning_raw_pointer && bin/struct_to_array_via_type_punning_raw_pointer
Sample output:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_raw_pointer.c -o bin/struct_to_array_via_type_punning_raw_pointer && bin/struct_to_array_via_type_punning_raw_pointer
Answer 3/3: use a packed struct and a raw `uint8_t` pointer to it`.
sizeof(message_unpacked_t) = 10 bytes
sizeof(message_t) = 8 bytes
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
i am a beginner to coding and learning c language as my first step.
when i use string function the program returns with a value without showing output. using minGW as compiler
i tried to add string.h header folder
string
from below follows code
''''
#include <stdio.h>
#include <conio.h>
int main()
{
/*
int strlen(string);
*/
char name = { 's','i','l','a','m','\0' };
int length;
length = strlen(name);
printf("the length of %s is %d\n", name, length);
getch();
return 0;
}
'''
code ends here
expected to to print length of the char name but it crashes
as in build log
"Process terminated with status -1073741819 "
in build messages
warning: passing argument 1 of 'strlen' makes pointer from integer without a cast [-Wint-conversion]|
note: expected 'const char *' but argument is of type 'char'|
thanking you for looking into
You declare name as a char yet you treat it like an array. To declare name as a char array, use:
char name[] = { 's','i','l','a','m','\0' };
Also since you reference the function strlen(), you must add the header file:
#include <string.h>
Enjoy:
#include <stdio.h>
#include <string.h>
int main()
{
// single char can't store that string
// so we have to declare an array of chars
char name[] = {'s', 'i', 'l', 'a', 'm', '\0'};
// also we can use string literal syntax:
// char name[] = "silam";
int length = strlen(name);
printf("the length of %s is %d\n", name, length);
getc(stdin);
// i don't have <conio.h> so i call 'getc' instead of 'getch'
return 0;
}
I have this code but it gives me some errors when compiling.
[Error] expected identifier or '(' before '[' token
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
//functions
void createdeck();
//structures
typedef struct card{
int val;
char face;
char suit;
}deck[52];
//global variables
const char suits[4] = { 'D', 'C', 'H', 'S' };
const char face[13] = { '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'J', 'Q', 'K', 'A' };
int main()
{
createdeck();
}
void createdeck()
{
int ctr1,ctr2,count = 0;
for (ctr1 = 0; ctr1 < 4;ctr1++)
{
for (ctr2 = 0;ctr2 < 13; ctr2++)
{
deck[count].val = count + 1;
deck[count].face = face[ctr2];
deck[count].suit = suit[ctr];
count = count + 1;
}
}
}
[Error] expected identifier or '(' before '[' token
That was what the compiler says
deck[count].val = count + 1;
deck[count].face = face[ctr2];
deck[count].suit = suit[ctr];
These are the highlighted errors.
Please enlighten me. I am still a beginner.
It is because deck is type, not a variable (has no memory initialised for it).
I recommend not using a typedef, as they obfuscate the type and tend to make things more confusing (they're misused quite often), and to just declare a variable named deck within your createdeck() function, like so:
//structures
struct card{
int val;
char face;
char suit;
};
...
void createdeck()
{
struct card deck[52];
Note that you have also mispelled suits as suit on line
deck[count].suit = suit[ctr];
And ctr on that line does not exist as a variable, as well.
I have 2 defines, one with a string and one with a number.How can i make a const array from the define with the string and the number. There are also some additional constant which should be in this array.
How can i write this Code to have 0x22, 0x41, 0x42, 0x42, 0x21 in the array foobar, from the defines FOO and BAR?
#define FOO "AB"
#define BAR 33
extern int rs232_write(const unsigned char *data, unsigned char count);
const unsigned char foobar[] =
{
0x22,
FOO[0], /*what must i put here, this do not work*/
FOO[1],
0x42,
BAR,
};
int main(void)
{
rs232_write(foobar,sizeof(foobar));
return 1;
}
In gcc, for example, i get the error:
./001.c:9:5: error: initializer element is not constant
FOO[0], /*what must i put here*/
^
The String have always the same length.
I did also a try the other way around:
#define FOO "AB"
#define BAR 33
extern int rs232_write(const unsigned char *data, unsigned char count);
const char foobar[] = \
"\x22" \
FOO \
"\x42" \
BAR /*what must i put here, this also not work*/
int main(void)
{
rs232_write(foobar,sizeof(foobar));
return 1;
}
Here i get also a error, for example gcc prints:
./002.c:2:13: error: expected ‘,’ or ‘;’ before numeric constant
#define BAR 33
^
I working on a Microcontroller with not much space, so i would like to avoid creating the array at runtime and my compiler do only support C89.
The simplest, using memcpy:
#include <stdio.h>
#include <string.h>
#define FOO "AB"
#define BAR 33
extern int rs232_write(const unsigned char *data, unsigned char count);
unsigned char _foobar[] =
{
0x22,
0, 0,
0x42,
BAR,
};
const unsigned char *foobar;
int main(void)
{
foobar = (const unsigned char *)memcpy(_foobar + 1, FOO, 2) - 1;
rs232_write(foobar,sizeof(foobar));
return 0;
}
The ugly, using an X Macro and a compound literal:
In this way you can use the first two digits:
const unsigned char foobar[] =
{
0x22,
'A', 'B',
0x42,
33,
};
or the full string "AB"
#include <stdio.h>
#define FOO X('A', 'B', '\0')
#define BAR 33
extern int rs232_write(const unsigned char *data, unsigned char count);
const unsigned char foobar[] =
{
0x22,
#define X(a, b, c) a, b
FOO,
#undef X
#define X(a, b, c) ((char []){a, b, c})
0x42,
BAR,
};
int main(void)
{
// rs232_write(foobar,sizeof(foobar));
printf("%s\n", FOO);
return 0;
}
Output:
AB
This should work:
#include<stdio.h>
#define FOO 'A','B'
#define BAR 33
const char foobar[] = {
0x22,
FOO,
0x42,
BAR,
'\0'
};
int main(void)
{
printf("%s\n", foobar);
return 0;
}
BTW it is very bad to init the array that way, maybe you can explain your aim better.
#include <stdio.h>
#include <stdlib.h>
#define FOO "ab"
#define BAR 33
#define STRINGIFY(x) STRINGIFY2(x)
#define STRINGIFY2(x) #x
const char foobar[] = "\x22" FOO "\x42" STRINGIFY(BAR);
int main(void)
{
printf("foobar = |%s| (%ld+1 characters)\n",
foobar, (long) sizeof(foobar) - 1);
return EXIT_SUCCESS;
}
Running this program ouputs:
foobar = |"abB33| (6+1 characters)
The problem is that the compiler doesn't know, at the time of compilation, where the string literal "AB" will be placed in memory. Its location will be decided when the program is linked or possibly when it is loaded into memory.
Therefore using it will not result in a compile-time constant that is required for the initialization of the foobar array.
In this case you really have no option but to use setting foobar[1] and foobar[2] once at run-time. However, even on an extremely small embedded system this will not incur much overhead either in memory or in time. If the program runs more than a few seconds it will most likely not even be measurable.
In C, I am trying to do the following:
typedef struct {
int length;
int items[]; /* 1 */
} wchararray_t;
typedef struct {
long hash;
wchararray_t chars; /* 2 */
} string_t;
static string_t s1 = {
617862378,
{ 5, { 'H', 'e', 'l', 'l', 'o' } } /* 3 */
};
In full words, I would like a type string_t that ends in another type wchararray_t that is itself dynamically sized -- its size being stored in length. Moreover, I would also like to write a prebuilt particular string, as static data, here s1 of length 5.
The code above assumes C99 support for /* 1 */. The inclusion of the substructure into the bigger structure at /* 2 */ is, as far as I understand, not supported even by the C99 standard -- but GCC accepts it. However, at /* 3 */ GCC gives up:
error: initialization of flexible array member in a nested context
As a workaround, the ideal code above is so far written as the following hack, which "kind of works":
typedef struct { int length; int items[1]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;
typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;
static union { string_len5_t a; string_t b; } s1 = {
617862378,
{ 5, { 'H', 'e', 'l', 'l', 'o' } }
};
...and we'd use "s1.b" as the prebuilt string_t (and never refer to "s1.a", which is here only for the static declaration of s1). However, it breaks in the newest GCC 4.8, which optimizes away parts of our code because -- obviously -- any loop over the items of a wchararray_t can iterate only once, given that it is an array of length 1.
This particular issue is fixed by giving gcc the option -fno-aggressive-loop-optimizations. It can probably also be fixed by not declaring the length in wchararray_t's items[] array, making it a dynamic array "just because". However, this way to write code is such a hack that I'd prefer a fully different way to approach the problem...
(Note that it is all generated C code produced by PyPy, as opposed to hand-written code; any change is fine, including if it requires changing the way we access the data everywhere, as long as the "valid" C optimizations are not prevented.)
EDIT: replaced "char[]" with "int[]", which doesn't accept the double-quote syntax "hello". This is because I'm looking for a solution for any array type.
NOT RESOLVED: thanks everybody for your suggestions. It seems there is no clean way, so I have implemented the hackish solution: declaring the types k+1 times, once with a flexible array "int items[];" and the k other times with "int items[N];" for the various values of N that are needed. This requires some additional hacks: e.g. not using flexible arrays for MSVC (they work differently there; I didn't investigate to know if exactly the same syntax would work); and GCC follows what C99 says and is not happy with structs that would contain int items[]; as only field. It is however happy if we add a dummy field char _dummy[0];... which is not strictly C99 as far as I know...
It's hackish, but could this work?
#include <stdio.h>
typedef struct {
int length;
int items[]; /* 1 */
} wchararray_t;
typedef struct {
long hash;
wchararray_t chars; /* 2 */
int dummy[]; /* hack here */
} string_t;
static string_t s1 = {
617862378, { 5 },
{ 'H', 'e', 'l', 'l', 'o' } /* 3: changed assignment */
};
int main(void)
{
int i;
for (i=0; i < 5; ++i) {
putchar(s1.chars.items[i]);
}
putchar('\n');
return 0;
}
GCC gives me warnings:
xx.c:10:22: warning: invalid use of structure with flexible array member [-pedantic]
xx.c:16:9: warning: initialization of a flexible array member [-pedantic]
xx.c:16:9: warning: (near initialization for ‘s1.dummy’) [-pedantic]
But it seems to work.
Reference
Edit: How about adding a "padding member" that makes sure items[] is always properly aligned?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
/* change to the strictest alignment type */
typedef long aligner;
typedef struct {
long stuff; /* to show misalignment on 64-bit */
int length;
aligner padding;
int items[];
} chararray_t;
typedef struct {
long hash;
chararray_t chars;
int dummy[];
} string_t;
static string_t b1 = {
617862378,
{ 42, 5 },
{-1, -2, -3, -4, -5}
};
int main(void)
{
int i;
printf("sizeof chararray_t: %zu\n", sizeof(chararray_t));
printf("offsetof items: %zu\n", offsetof(chararray_t, items));
printf("sizeof string_t: %zu\n", sizeof(string_t));
printf("offsetof dummy: %zu\n", offsetof(string_t, dummy));
for (i=0; i < 5; ++i) {
printf("%d ", b1.chars.items[i]);
}
putchar('\n');
for (i=0; i < 5; ++i) {
printf("%d ", b1.dummy[i]);
}
putchar('\n');
return 0;
}
When I run the above, I seem to get the correct answer:
sizeof chararray_t: 24
offsetof items: 24
sizeof string_t: 32
offsetof dummy: 32
-1 -2 -3 -4 -5
-1 -2 -3 -4 -5
Answering my own question to write it down. Yet another hack would be to build on top of Alok's suggestion, which may give an occasionally bogus alignment --- and then fix the alignment by init-time code. This assumes that the big majority of such types used in a program happen to be correctly aligned. Code:
typedef struct {
long stuff; /* to show misalignment on 64-bit */
int length;
int items[];
} chararray_t;
typedef struct {
long hash;
chararray_t chars;
int dummy[];
} string_t;
static string_t b1 = {
617862378,
{ 42, 5 },
{-1, -2, -3, -4, -5}
};
/* same with b2 .. b6 */
void fixme(void) {
/* often compares as equal, and the whole function is removed */
if (offsetof(string_t, dummy) !=
offsetof(string_t, chars) + offsetof(chararray_t, items)) {
static string_t *p_array[] = { &b1, &b2, &b3, &b4, &b5, &b6 };
string_t *p;
int i;
for (i=0; i<6; i++) {
p = p_array[i];
memmove(p->chars.items, p->dummy, p->chars.length * sizeof(int));
}
}
}
#include <stdio.h>
typedef struct {
int length;
char items[]; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
/*static string_t s1 = {
617862378,
{ 5, { 'H', 'e', 'l', 'l', 'o' } } // 3
};*/
static string_t s1 =
{
617862378,
{6,"Hello"} /* 3 */
};
int main()
{
printf("%d %d %s\n",s1.hash,s1.chars.length,s1.chars.items);
return 0;
}
Add 1 for the null character, et voila! :)
Edit, Also works for 2 levels of nesting (GCC 4.8.0)
#include <stdio.h>
typedef struct {
int length;
char items[]; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
typedef struct {
long number;
string_t arr;
}experiment_t;
static experiment_t s1 =
{
617862378,
{786,{6,"Hello"}} /* 3 */
};
int main()
{
printf("%d %d %d %s\n",s1.number,s1.arr.hash,s1.arr.chars.length,s1.arr.chars.items);
return 0;
}
----------EDIT 2------------------
Found a way around the limitation C initialize array within structure
Final code::
#include <stdio.h>
typedef struct {
int length;
int *items; /* 1 */
} intarray_t;
typedef struct {
long hash;
intarray_t chars; /* 2 */
int dummy[2];
} string_t;
/*string_t s1 =
{
617862378,
{
6,
{1,2,3,4,5,6}
},
{
0,0
}
};*/
string_t s1 = {617862378,{},{0,0}};
int main()
{
int i=0;
intarray_t t1 = {.length = 6, .items = (int[6]){1,2,3,4,5,6}};
s1.chars = t1;
printf("%d %d\n",s1.hash,s1.chars.length);
while(i<s1.chars.length)
{
printf("%d",s1.chars.items[i]);
i++;
}
putchar('\n');
return 0;
}
I assume there is some reason for keeping the string "inside" the struct and that you want to save a char, by not initializing with a C-string.
But, if not, you could do:
typedef struct {
int length;
char *items; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
static string_t s1 = {
617862378,
{ 5, "Hell" } /* 3 */
};
s1.chars.items[4] = 'o' ;
Looks like you can do the union trick, but with a typecast instead ?
#include <stdio.h>
typedef struct { int length; int items[]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;
typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;
static union { string_len5_t a; string_t b; } s5 = {
617862378,
{ 5, { 'H', 'e', 'l', 'l', 'o' } }
};
string_t *s1 = (string_t*) &s5 ;
int main( int argc, char *argv[])
{
for( int i = 0 ; i < s1->chars.length ; i++ )
{
printf ( "%c", s1->chars.items[i] );
}
printf( "\n" );
}