Google Protobuf-c repeated strings - c

My Google protobuf-c proto file looks like this:
message foo{
required uint32 addresscount= 1;
repeated string destination_hops= 2;
}
I want an array of strings and addresscount is supposed to give the count of array elements.
The generated protobuf-h file is
struct _foo
{
ProtobufCMessage base;
uint32_t addresscount;
size_t n_destination_hops;
char **destination_hops;
};
I assume that n_destination_hops is the number of times the string destination_hops appears in the message. I had kept addresscount for that purpose and i guess thats not required. My question is this:
char **destination_hops is a pointer to an array of char * pointers. Each index can be of different length. How will protobuf know the size of each char * pointer when it has packed this into a stream. Does protobuf assume that all destination_hops would be of the same size and is that given by size_t n_destination_hops?
DOUBT 2:
char ** destination_hops is an array of char * pointers.
This means i can look at it as char *destination_hops[0], char *destination_hops[1], etc
If this is correct then shouldn't i be able to set char *destination_hops[0] = "abc".
When i do that i get a segmentation fault.
Any idea why?

I assume that n_destination_hops is the number of times the string destination_hops appears in the message.
Yep.
I had kept addresscount for that purpose and i guess thats not required.
Right.
char **destination_hops is a pointer to an array of char * pointers. Each index can be of different length. How will protobuf know the size of each char * pointer when it has packed this into a stream.
In the wire representation of the string, it is preceded by the string's length, coded as a packed integer. In memory, they are just ordinary zero-terminated C strings, so:
NUL is not a valid character in the string
the length can be computed with strlen
Protobuf requires that the value of a string be a sequence of UTF-8 encoded characters. If you want to include NUL or bytes which are not part of a valid UTF-8 sequence, use bytes instead of string. If you do that, the in-memory datastructure will include an explicit length.
Shouldn't i be able to set char *destination_hops[0] = "abc"
I suppose you mean that you should be able to write:
destination_hops[0] = "abc";
That's valid C, but it requires that destination_hops have at least one element. A newly initialized foo will have n_destination_hops = 0; and destination_hops = NULL;, in which case trying to access destination_hops[0] will try to dereference NULL, which is a segfault. (Or UB, if you prefer.9

Related

How can I convert char pointer to __uint8_t array in C?

I need to convert a string (char*) to an array of __uint8_t. I know the latter is an alias of unsigned char but I can't figure it out how to proper convert it.
Example:
char *ssid = "XXXXX";
This is need cause I have to call an API that accepts only an array of __uint8_t and not char*.
struct WifiConfig {
uint8_t ssid[32];
uint8_t password[64];
};
struct WifiConfig wifi_config;
wifi_config.ssid = ???
If I try to cast:
wifi_config.ssid = (__uint8_t *) ssid;
I get the follow error:
error: assignment to expression with array type
wifi_config.ssid = (__uint8_t *) ssid;
Sorry if that's a silly question, last time I've played with C I was a teen.
Thanks in advance!
Just cast:
const char *name = "My Full Name";
yourAPIFunction((__uint8_t *) name);
Note: this violates const-correctness. You have to make sure yourAPIFunction doesn't mutate name. If it does, then you'll need to memcpy (and free!) it to a local variable, and provide it that, so its mutations don't effect the pooled-string used by any other users of "My Full Name".
Response to your (completely different) updated question:
Those arrays have their storage in-line, they're not pointers to the heap like a typical char *.. You need to copy the contents of your string into them:
// ssid must be at most 31 chars (plus NUL terminator)
// password must be at most 63 chars (plus NUL terminator)
struct WifiConfig new_wifi_config(char *ssid, char *password) {
struct WifiConfig wifi_config;
memcpy(wifi_config.ssid, ssid, strlen(ssid)+1)
memcpy(wifi_config.password, ssid, strlen(ssid)+1)
return wifi_config;
}
I need to convert a string (char*) to an array of __uint8_t. I know
the latter is an alias of unsigned char but I can't figure it out how
to proper convert it.
Being pedantic -- because it matters here -- a C string is not a char *. Rather, it is a null-terminated array of char. These are accessed and handled primarily via pointers of type char *, largely because that's how one works with arrays in C.
Thus, no, you do not want to convert a char * to an array. It appears that what you want to do is copy the contents of the array to which it points. For your particular purpose I would use the strncpy() function:
strncpy((char *) wifi_config.ssid, ssid, sizeof(wifi_config.ssid));
Note well that with that code, if ssid is longer than wifi_config.ssid can accommodate then the latter will end up being unterminated, and that otherwise its tail will be filled out with null bytes. Given the type of that array, such behavior may be precisely what is needed. If you want to ensure null termination, however, then simply overwrite the last byte of the array with a 0 after the strncpy().

Input element into char array using a variable

I need to input an element into a static const char []. I've tried with snprintf, strcat but it not working on my case because char array contains some NULL characters.
char SBP_BASE_LAT[] = "surveyed_position""\x00""surveyed_lat""\x00";
I have variable position_lati of type float and I want to input it into SBP_BASE_LAT, like
float position_lati = 43.456745;
char SBP_BASE_LAT[] = "surveyed_position""\x00""surveyed_lat""\x00"["%f",position_lati];
What is the solution?
Thanks;
This declaration you presented ...
char SBP_BASE_LAT[] = "surveyed_position""\x00""surveyed_lat""\x00";
... declares SBP_BASE_LAT as an array of char, with size derived from its initializer -- 32, including the additional terminator, if I have counted correctly. The array elements are not const.
It is legal to include null bytes in the array's initializer as you do, but the usefulness of doing so seems doubtful. All standard functions that handle strings will interpret the first embedded null byte as a string terminator, and if that's what you intend then it's unclear why you present a longer initializer.
I have variable position_lati of type float and I want to input it into SBP_BASE_LAT [...]
You cannot do this via an initializer, because initializers must be compile-time constants. You could do it at runtime like so:
float position_lati = 43.456745;
/* plenty of extra space to append the formatted value: */
char SBP_BASE_LAT[50] = "surveyed_position""\x00""surveyed_lat""\x00";
int base_lat_end;
/*
* compute the combined length of the first two zero-terminated
* segments of SBP_BASE_LAT
*/
base_lat_len = strlen(SBP_BASE_LAT) + 1;
base_lat_len += strlen(SBP_BASE_LAT + base_lat_len) + 1;
/*
* Format the value of position_lati into the tail of
* SBP_BASE_LAT, following the second embedded zero byte.
*/
sprintf(SBP_BASE_LAT + base_lat_len, "%9.6f", position_lati);
Of course, that all supposes that the value of position_lati is not known until run time. If it is known at compile time then you can just put that value literally into the array initializer.
Also, if your array were actually const, then you could not modify its contents after intialization, so a sprintf()-based approach such as I describe would not work.

Store integer values as a string in C

Following code print integer values:
for ( i=0 ; i<COL ; i++ )
{
fprintf(iOutFile,"%02x ",(int)(iPtr[offset]));
}
I want to store these integer values as a string in a character pointer. To do so, I tried following code but it does not work.
char *hexVal="";
char *temp;
int val;
for ( i=0 ; i<COL ; i++ )
{
fprintf(iOutFile,"%02x ",(int)(iPtr[offset]));
val = (int)(iPtr[offset]);
temp=(char*) val;
hexVal = strcat(hexVal,temp);
}
printf("%s", hexVal);
Thanx.......
When you write
char* hexVal = "";
you are setting hexVal to point to a string literal, later in your code you try to strcat to that address which will cause undefined behavior.
What you need to do is to allocate a large enough area to hold your resulting string and then let hexVal point to that.
E.g.
char* hexVal = malloc(100); // or how much bytes you need
then just do
strcat(hexVal, temp);
alt. allocate on stack
char hexVal[100];
You are approaching this wrong. In general, if you have an int i then you can't just typecast a char *cp to the address (or the value) of i and expect it to magically become a string that you can printf or use in strcat. For one thing, strings are null-terminated and don't have a fixed length, while ints have a fixed size of typically 32 bits long.
You have to create a separate buffer (memory space) where snprintf will happily create a string-representation of your int value for you.
I think that your question is more about understanding how programming, pointers and C work in general, than about ints and strings and their conversion.
You are getting undefined behavior since there is no writable memory at hexVal, which just points at a read-only area containing a character with the value 0. It is a valid string, but it's constant and has length 0, you cannot append to it.
Also, of course you can't cast an integer into a "string", that's just not how C works. This:
temp=(char*) val;
simply re-interprets the value of val as a pointer (i.e. as an address), it doesn't magically compute the proper sequence of digit characters used to represent the address, i.e. it doesn't convert val to a string.
You can use snprintf() to convert an integer to a string.
So, to summarize:
Change hexval's declaration into e.g. char hexval[32] = "";, this declares it as an array of 32 characters, giving you plenty of space into which to build a number as a string. It also initializes the array so the first character is 0, thus making it into an empty string (with space to grow).
Use e.g. snprintf(tmp, sizeof tmp, "%d", 4711); to format a number into a string (which should be e.g. char tmp[16];) in decimal form.
Use strcat(hexval, tmp); to concatenate the newly built numeric string onto hexval.
BEWARE that you can't concatenate forever, you will run out of space if you do it too long. Adjust the sizes, in that case.
Check return values where possible, read the manual pages (Google man FUNCTION where FUNCTION is a standard C library function like strcat).

Convert struct field to unsigned char string

I'm on an embedded system so do not have access to a most of the standard library. I have a struct which contains char values.
I have a simple print function which simply outputs an unsigned char string to an attached screen. It does not support format specifiers like printf does.
This is the struct:
typedef struct my_options{
char test;
} my_options;
And this is where I'm trying to output the value:
struct my_options options;
print(options.test); //Here I get "implicit conversion of int to ptr"
How do I achieve this?
Create a char array to hold your char and then print it:
char wrapper[2];
wrapper[0] = options.test;
wrapper[1] = '\0';
print(wrapper);
Create a temporary 2-character long string that has the character to print, and then the terminator. Then pass that string to your print() function:
void print_options(const struct my_options *opt)
{
char tmp[] = { opt->test, '\0' };
print(tmp);
}
your member test if of the type char, where the print function expects an argument of the type const char * (assuming the const bit here, but that's what I'd expect, this as an asside). Passing the address of test then would seem like the appropriate solution, but is it?
No, of course it isn't. There is no absolute guarantee that the next byte after test will be '\0' (a string terminating char). What you, then, ought to do is create a wrapper string:
char char_wrapper[2] = {};//initializes according to standard
//but as Lundin pointed out, self-documenting code is important:
char_wrapper[0] = options.test;?
char_wrapper[1] = '\0';//explicit, so it's clear what this code does
print(char_wrapper);
That should work just fine.
You can, of course, write this as a one-liner:
char char_wrapper[2] = {options.test, '\0'};//same as before, only in 1 statement
print(char_wrapper);//print "string"
That should do it, really. You don't even have to explicitly write the terminating char, since the standard specifically states:
An array of character type may be initialized by a character string literal, optionally
enclosed in braces. Successive characters of the character string literal (including the
terminating null character if there is room or if the array is of unknown size) initialize the
elements of the array.
6.7.8 Initialization cf p. 138, semantics, point 14
Be that as it may, I'd still prefer to browse the web, or just set about writing your own minor implementation of printf so you can use format specifiers. Heck, it's one of the first exercises in the K&R book, and there's tons of solutions floating about on the net. check those out, and adapt them to your specific needs.
Or, perhaps define print to accept a size_t argument, to specify how many chars you want to pass to the output stream. and call it like so print(options.test, 1);
print is looking for a char*. You are passing an char which can also be represented as an int. Thus, the function is trying to implicitly convert an int to a pointer as it is telling you.
char ptr[2];
ptr[0] = options.test;
ptr[1] = '\0';
Would wrap the char into a char array, which in C will decay into a pointer when you pass it to a function.

How can this char array store four separate strings?

I'm confused by this snippet from the book I'm reading. Text strings are put into a char array. There are four words, with four elements in the array. Wait, but that means a single char element is containing a whole text string. I'm certain chars can only handle a single character. Here's the code.
const char *words[4] = { "aardvark", "abacus",
"allude", "zygote" };
So what gives? How can the author be using chars to store whole strings? I know the solution must be blindingly obvious, but I just can't see it. Also, what's with the const keyword? Why would it need to be read only if all we plan to do with this array is count the length of each word using strlen()?
The code does not use chars to store strings, note that the declaration is an array of char *.
A char can hold a single character, and a char * can be used to point to the first element of an array of characters, which can hold a standard C string. This is an important C fundamental: see a char * and you should immediately think of null-terminated C strings.
Similarly, an int * can be used to refer to an entire array of int, by holding the address of the first int in the array. int* can be subscripted the same as an array declared using [].
A char * can also be used to simply hold the address of a single character - i.e. not the first character in a null terminated string.
How can the author be using chars to store whole strings?
he can't. You notice the asterisk, right?
The point your missing is the *.
char letter = 'a'; //stores a single character
char *word = "bigger"; //stores a string literal
this means word is really:
word ---> [b][i][g][g][e][r][\0]
pointing to a bunch of chars (or a "string" of chars). In your example the author defined:
const char *words[4] = { "aardvark", "abacus", "allude", "zygote" };
So you have 4 strings of chars, which are constant. The const was the author's choice to add that on there... it could have been left off. It's just a safeguard, a note to the compiler (and author) that these values were not ment to be modified.

Resources