In my application, I have a char array defined which can take one of three options: "okay", "high", "low" which are then sent down a serial port to a remote device. I currently have the array sized to take the 4 character words plus carriage return and line feed, but when I have to send "low" I get a null character in the strings, which I am concerned would confuse the host terminal.
array definition
char mod1_status_char[6] = {'0','0','0','0','0','0'};
char mod2_status_char[6] = {'0','0','0','0','0','0'};
char mod3_status_char[6] = {'0','0','0','0','0','0'};
sample of switch case statement:
void DCOKStatus(uint8_t *ptr_status)
{
uint8_t status = *ptr_status;
switch (status)
{
case 0x00:
strcpy(mod1_status_char, "okay");
strcpy(mod2_status_char, "okay");
strcpy(mod3_status_char, "okay");
break;
case 0x10:
strcpy(mod1_status_char, "okay");
strcpy(mod2_status_char, "okay");
strcpy(mod3_status_char, "low");
break;
}
This is the struct which makes the message string to send
strcpy(MsgStatus_on.descriptor_msg, "$psu_");
MsgStatus_on.address01 = hex_addr[0];
MsgStatus_on.address02 = hex_addr[1];
MsgStatus_on.space01 = 0x20;
strcpy(MsgStatus_on.cmdmsg01, "op_en op1_");
strcpy(MsgStatus_on.statusmsg01, mod1_status_char);
MsgStatus_on.space02 = 0x20;
strcpy(MsgStatus_on.cmdmsg02, "op2_");
strcpy(MsgStatus_on.statusmsg02, mod2_status_char);
MsgStatus_on.space03 = 0x20;
strcpy(MsgStatus_on.cmdmsg03, "op3_");
strcpy(MsgStatus_on.statusmsg03, mod3_status_char);
MsgStatus_on.CR = 0x0D;
MsgStatus_on.LF = 0x0A;
and this sends the message
void USARTWrite(char *object, uint32_t size)
{
GPIO_SetBits(GPIOB, GPIO_Pin_1);
char *byte;
for (byte = object; size--; ++byte)
{
USART_SendData(USART1,*byte);
}
Would anyone be able to suggest a good approach to dynamically size the array to one character shorter when I need to send "low"?
Thanks
I don't think a dynamically sized array is called for. There are two ways in C literally to dynamically size an array: allocate it with malloc or similar; or use C99 VLAs. But in this case where you have strings of different lengths, surely the important point is to write the correct bytes in the correct order? Personally I'd prefer something like this, maybe:
char * strings[] = {"okay\r\n", "high\r\n", "low\r\n"};
serial_send(strings[msg_number], strlen(strings[msg_number]));
You don't have to call strlen, necessarily, you could store the lengths in another array. But even on the tiniest embedded device, counting to 6 takes very little time compared with sending serial data.
Of course I'm assuming that whatever function you call to actually send the data, takes a pointer and a length. But if it doesn't, I don't see how a dynamically sized array helps either.
I think the general problem here which makes it difficult to answer the question, is that you don't really say what the "size" of your array is, or why it has anything to do with the number of bytes actually written to the serial port.
Edit: with your additional explanation, the key thing seems to be this struct that the three individual strings are "passed into". Not sure what passing a string into a struct means. If it currently looks like this:
struct serialmessage {
char first[6];
char second[6];
char third[6];
};
serialmessage msg;
memcpy(msg.first, mod1_status_char, 6); // etc.
Then maybe it would be better to do this:
char *char mod1_status_char; // etc.
switch(status) {
case 0x00:
mod1_status_char = strings[0]; // or #define STATUS_OK 0
mod2_status_char = strings[0];
mod3_status_char = strings[0];
break;
case 0x10:
mod1_status_char = strings[0];
mod2_status_char = strings[0];
mod3_status_char = strings[2]; // STATUS_LOW
};
serialmessage msg[3*MAX_STRING_LENGTH+1];
strcpy(msg, mod1_status_char); // or use stpcpy if you have it
strcat(msg, mod2_status_char);
strcat(msg, mod3_status_char);
Then send the struct using strlen(msg). msg isn't exactly "dynamic" here, but the length of the string in it varies according to the data, which might be what you want. Or maybe I'm still misunderstanding the role of these three char arrays.
Copying the strings more than necessary just seems to me to introduce complications. Refer to them by pointer until the last possible moment, when your message is assembled, and you minimise the number of places in your code where you have to get buffer sizes right.
"The status_char arrays are then passed to a struct, which is then sent using a send routine."
Be very careful doing this, depending on how you code it you can get all kinds of junk in there. Remember in C the compiler can pad structs however it pleases.
As a side note, your string buffers are too short to hold a string correctly. With 4 character + CR + LF you need a buffer of 7 characters as you need to store the null terminator '\0'. If you do not do this, do not use any 'str' functions as you are not dealing with proper C strings, all your going to do is create an issue further down the road when someone goes to read this/make a change and finds out after copying a str around your hacking off the null termination (strcopy is copying "low\0" into your buffer, your apparently tossing /r/n onto the end somewhere else for some reason) use memcpy.
Onto a solution:
Why are you copying these string around at all? Why not just send an indication to your send function to tell it what it should send and just have the string statically allocated?
You could create an enum with values for (E_LOW,E_OKAY,E_HIGH), just send in 3 enums to a send function and have it store the actual strings to send as static variables locally. If space is an issue you could use bit flags instead of an enum.
Your send function just needs to copy the string value a byte at a time into the send buffer and send strlen() bytes.
Am I missing something here? The send routine should just use strlen() on the string so it only sends the data in the buffer.
serWrite( mod1_status, strlen( mod1_status));
serWrite( "\r\n", 2);
You got few options to choose from:
Why you are sending the text if the options are predefined?
You could send just the ID.
Normally, protocol messages are not fixed length except some rare cases.
Length first, message second,
CRC third.
Fill empty char array space with spaces and trim the string on the other side.
Not an option, I did not wrote this.
By truncating "head" of array.
Suppose you have char words[5] (or 6 -- to hold "\r\n").
So in case of "okay" and "high" you send content of words starting from first element -- words, and in case of low just send content starting from second one: words + 1.
EDIT: of course in this case you should write "low" starting from words[1], not words[0].
I'd like to see your definition of MsgStatus_on.
I'm betting you have something like this:
tyepdef struct {
char descriptor_msg[6];
char address01;
char address02;
char space01;
char cmdmsg01[11];
char statusmsg01[6];
char space02;
char cmdmsg02[5];
char statusmsg02[6];
char space03;
char cmdmsg03[5];
char statusmsg03[6];
char CR;
char LF;
} MsgStatus;
MsgStatus MsgStatus_on;
And then my guess is that you're doing a straight byte pointer when you call USARTWrite, like so:
USARTWrite ((char *)&MsgStatus_on, sizeof(MsgStatus_on));
If this is the case then it's copying the extra byte in your buffer. In fact it should be putting extra \0's on all of your char arrays. Unless you declared all of them one less than I did and you're actually overrunning your arrays when you do the strcpy(). It's not causing you problems because you then set the overrun memory in the next statement.
An alternative option could be to use sprintf:
char *message[100] //or some size big enough to hold the whole message.
sprintf (message, "$psu_%d%d op_en op1_%s op2_%s op3_%s\r\n",
address01, address02, mod1_status_char, mod2_status_char, mod3_status_char);
Then call:
USARTWrite (message, strlen(message));
EDIT: Oops I guess this question is quite old. Oh well, I'll leave the answer in case it's useful to you.
Related
i have two string elements in my structure
struct mystruct{
char mydate[10];
char mytime[5];
};
They will store strings of type "XX:YY" and "XX-YY-ZZZZ" respectively.
But when I am assigning some value into these variables
struct mystruct *mystruct = (struct mystruct*)malloc(sizeof(struct mystruct));
strcpy(mystruct->mydate, "01-01-1970");
strcpy(mystruct->mytime, "00:01");
mydate variables is printing this:
01-01-197000:01
I am missing something? Can you help me? Thanks in andance!
EDITED with more info
don't works even if I increase the size by one
You have undefined behavior since there is not sufficient room in mydate to contain strings of the format "MM-DD-YYYY" - don't forget the implicit null terminator at the end.
What you're specifically observing is that the lack of the null terminator means that the output function (puts, printf, or whatever you're using) continues to read characters after the string ends. It so happens that there isn't any padding between mydate and mytime in your case, so the value in mytime appears to be part of the string as well.
Remember, since arrays decay to pointers when passed to functions, there is no way for a function with an array parameter to know when it is done reading the array; the null terminator acts as a sentinel value for this purpose.
Solution: Increase the size of both mydate and mytime to accommodate the null terminator as well.
since you complained that your code did not work even with increased sizes, here is an example which works correctly:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct mystruct1 {
char mydate[11];
char mytime[6];
};
void main() {
struct mystruct1 *mystruct1 = (struct mystruct1*)malloc(sizeof(struct mystruct1));
strcpy(mystruct1->mydate, "01-01-1970");
strcpy(mystruct1->mytime, "00:01");
printf("date: %s, time: %s\n", mystruct1->mydate, mystruct1->mytime);
}
In this example every array has enough space to keep the string with the null terminator. So, you can compare it to your code.
In general it is also possible to keep sizes as in your example. But you need to always remember that the end of the string cannot be determined automatically and will require you to use specific function, like strncpy instead of strcpy. printf will not work directly as well. So here is another example:
struct mystruct2 {
char mydate[10];
char mytime[5];
};
void main() {
struct mystruct2 *mystruct2 = (struct mystruct2*)malloc(sizeof(struct mystruct2));
strncpy(mystruct2->mydate, "02-02-2970", 10);
strncpy(mystruct2->mytime, "00:02", 5);
// need top prepare standard strings with terminator for printf
char mydate[11]; // still need [11] for printf to work
char mytime[6];
strncpy(mydate, mystruct2->mydate, 10);
mydate[10] = 0; // make sure to put a string terminator here
strncpy(mytime, mystruct2->mytime, 10);
mytime[5] = 0;
printf("date: %s, time: %s\n", mydate, mytime);
}
The above makes sense in some situation where you are really tight on the memory, but should not be used in generic cases.
The are at least two problems with the code.
1) You are writing too much in the mydate and mytime. The fix is either to allocate more memory in the struct, i.e., char mydate[10+1]; & char mytime[5+1];. Or just don't write the NULL terminator. A solution for that, since you know the size in advance, use memcpy(mystruct->mydate, "01-01-1970", sizeof(mystruct->mydate)); or similar memcpy(mystruct->mytime, "00:01", 5);.
2) The second part, print out, you are not showing (hint). So if you don't store the NULL-terminator then print out needs to be a little more delicate as shown in the example below.
// Possible dynamic max size
printf("date: %.*stime: %.*sThe\n",
(int) sizeof(mystruct->mydate), mystruct->mydate,
(int) sizeof(mystruct->mytime), mystruct->mytime);
// Fixed max size
printf("date: %.10stime: %.5sEnd\n",
mystruct->mydate,
mystruct->mytime);
Either way, the print out will be:
date: 01-01-1970time: 00:01The
date: 01-01-1970time: 00:01End
The printf-syntax used maximizes the length of the string printed.
BTW, your printout, 01-01-197000:01, is likely the result of the compiler placing the memory layout of struct mystruct directly after each other in memory. The the resulting memory layout is equivalent to
struct mystruct {
char my[15];
}
where the compiler knows that mydate starts at offset 0, and mytime starts at offset 10. This, if you filled your struct in the order of your example you first get "01-01-1970\0" followed by "01-01-197000:01\0" (where the last write is out of scope for the struct). So printing the date with printf("%s", mystruct->mydate); gives you your sequence output.
On the other hand, had you decided to write you data in the reverse order,
strcpy(mystruct->mytime, "00:01");
strcpy(mystruct->mydate, "01-01-1970");
the output of
printf("date: %s, time: %sEnd\n", mystruct->mydate, mystruct->mytime);
would be
date: 01-01-1970, time: End
since you time was overwritten my the null terminator of the mydate string copy.
I'm making a program that reads a text file composed by strings, each one on a line. Basically I do this:
...
char* name;
char* buffer = malloc(sizeof(char) * SIZE); //size is a defined constant in the header
while(fgets(buffer, SIZE, pf)){ //pf is the opened stream
name = malloc(sizeof(char) * SIZE);
strcpy(name, strtok(buffer, "\n"));
manipulate(name); //call an extern function
}
Function manipulate is declared in this manner:
void manipulate(void* ptr);
The problem is that in this way two equal strings will have different memory addresses so they will recognized as two different elements from manipulate function.
How can I make them recognized as a single element?
Store the strings in a set, a data type which stores no repeated values and is fast to search. Basically it's a hash table where the key is the string and the value doesn't matter.
You can write your own hash table, it's a good exercise, but for production you're better off using an existing one like from GLib. It already has convenience methods for using a hash table as a set. While we're at it, we can use their g_strchomp() and g_strdup().
#include <stdio.h>
#include <glib.h>
int main () {
// Initialize our set of strings.
GHashTable *set = g_hash_table_new(g_str_hash, g_str_equal);
// Allocate a line buffer on the stack.
char line[1024];
// Read lines from stdin.
while(fgets(line, sizeof(line), stdin)) {
// Strip the newline.
g_strchomp(line);
// Look up the string in the set.
char *string = g_hash_table_lookup(set, line);
if( string == NULL ) {
// Haven't seen this string before.
// Copy it, using only the memory we need.
string = g_strdup(line);
// Add it to the set.
g_hash_table_add(set, string);
}
printf("%p - %s\n", string, string);
}
}
And here's a quick demonstration.
$ ./test
foo
0x60200000bd90 - foo
foo
0x60200000bd90 - foo
bar
0x60200000bd70 - bar
baz
0x60200000bd50 - baz
aldskflkajd
0x60200000bd30 - aldskflkajd
aldskflkajd
0x60200000bd30 - aldskflkajd
If you indeed have two strings then they necessarily have different addresses, regardless of whether their contents are the same. It sounds like you want to keep track of the strings you've already read, so as to avoid / merge duplicates. That starts with the "keeping track" part.
Evidently, then, you need some kind of data structure in which to record the strings you've already read. You have many choices for that, and they have different advantages and disadvantages. If the number of distinct strings you'll need to handle is relatively small then a simple array or linked list could suffice, but if it is large enough then a hash table will provide much better performance.
With that in hand, you check each newly-read string against the previously read ones and act accordingly.
Let's consider following piece of code:
int len = 100;
char *buf = (char*)malloc(sizeof(char)*len);
printf("Appended: %s\n",struct_to_string(some_struct,buf,len));
Someone allocated amount of memory in order to get it filled with string data. The problem is that string data taken from some_struct could be ANY length. So what i want to achieve is to make struct_to_string function do the following:
Do not allocate any memory that goes outside (so, buf has to be allocated outside of the function, and passed)
Inside the struct_to_string I want to do something like:
char* struct_to_string(const struct type* some_struct, char* buf, int len) {
//it will be more like pseudo code to show the idea :)
char var1_name[] = "int l1";
buf += var1_name + " = " + some_struct->l1;
//when l1 is a int or some non char, I need to cast it
char var2_name[] = "bool t1";
buf += var2_name + " = " + some_struct->t1;
// buf+= (I mean appending function) should check if there is a place in a buf,
//if there is not it should fill buf with
//as many characters as possible (without writting to memory) and stop
//etc.
return buf;
}
Output should be like:
Appended: int l1 = 10 bool t1 = 20 //if there was good amount of memory allocated or
ex: Appended: int l1 = 10 bo //if there was not enough memory allocated
To sum up:
I need a function (or couple of functions) that adds given strings to the base string without overwritting base string;
do nothing when base string memory is full
I can not use C++ libraries
Another things that I could ask but are not so important right now:
Is there a way (in C) iterate through structure variable list to get their names, or at least to get their values without their names? (for example iterate through structure like through array ;d)
I do not normally use C, but for now I'm obligated to do, so I have very basic knowledge.
(sorry for my English)
Edit:
Good way to solve that problem is shown in post below: stackoverflow.com/a/2674354/2630520
I'd say all you need is the standard strncat function defined in the string.h header.
About the 'iterate through structure variable list' part, I'm not exactly sure what you mean. If your talking about iterating over the structure's members, a short answer would be : you can't introspect C structs for free.
You need to know beforehand what structure type you're using so that the compiler know at what offset in the memory it can find each member of your struct. Otherwise it's just an array of bytes like any other.
Don't mind asking if I wasn't clear enough or if you want more details.
Good luck.
So basically I did it like here: stackoverflow.com/a/2674354/2630520
int struct_to_string(const struct struct_type* struct_var, char* buf, const int len)
{
unsigned int length = 0;
unsigned int i;
length += snprintf(buf+length, len-length, "v0[%d]", struct_var->v0);
length += other_struct_to_string(struct_var->sub, buf+length, len-length);
length += snprintf(buf+length, len-length, "v2[%d]", struct_var->v2);
length += snprintf(buf+length, len-length, "v3[%d]", struct_var->v3);
....
return length;
}
snprintf writes as much as possible and discards everything left, so it was exactly what I was looking for.
For my networking class, we're building a bittorrent client based off the UDP protocol, which is pretty cool but I'm having a ton of trouble with C strings for some reasons.
The first time I receive a packet, I do:
if(server_data == NULL){
server_data = malloc(one_block.total_blocks*sizeof(char*));
int i;
for(i = 0; i < one_block.total_blocks; i++){
server_data[i] = malloc(sizeof(char*));
server_data[i] = "";
}
}
Here, server_data is a char** and one_block is struct that holds packet information and the payload.
Next I do:
server_data[one_block.which_block] = one_block.payload;
blocks_rcv++;
if(blocks_rcv == one_block.total_blocks-1)
done = TRUE; //macro
if(done){
int i;
for(i = 0; i < one_block.total_blocks; i++){
printf("%s", server_data[i];
}
}
All seems well and dandy but for whatever insane reason when I print the contents of server_data before all the packets are received, I see different data from each packet. Afterwards I set done = TRUE and go into that for loop, every spot in the array contains the same string value.
I have no idea why this is happening and I really want to understand how from the beginning of the post to the end, the contents of the array change, even though I verify them through every iteration of the loop that reads in one packet at a time.
This line is the problem:
server_data[i] = "";
It overwrites the allocated pointer, with a pointer to the string literal. And as string literals can't be modified, if you later copy into this pointer, you experience undefined behavior.
If you want to make sure the string is empty, either use calloc, set the first character to '\0', or use strcpy to copy in the new string.
There are a couple of issues going on here:
1) First, server_data, if it's declared as a char**, may or may not be null off the bat, unless you declare it so. I'm not sure if you initialized it to NULL or not. It's a good idea to explicitly initialize it to NULL.
2) I'm not sure from what's going on if you intend for each item of the array server_data to hold a char* (in other words, a reference to a string), or for the array to be a string itself. Is one_block.payload a string, or a set of pointers to strings?
I ran your code with some test values and I'm personally not getting any problems with unexpected values...I think the issue may be in how the struct that holds your payload data is set up. Could you show us your one_block struct? What type of variable/array is one_block.payload?
I'm trying to split a char* to an array of char* in C.
I'm used to program in Java / PHP OO. I know several easy way to do that in these languages but in C... I'm totally lost. I often have segfault for hours x)
I'm using TinyXML and getting info from XML File.
Here's the struct where we find the array.
const int MAX_GATES = 64;
typedef struct {
char *name;
char *firstname;
char *date;
char *id;
char *gates[MAX_GATES];
} UserInfos;
And here's where I fill this struct :
UserInfos * infos = (UserInfos*)malloc(1024);
infos->firstname = (char*)malloc(256);
infos->name = (char*)malloc(128);
infos->id = (char*)malloc(128);
infos->date = (char*)malloc(128);
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
sprintf(infos->name, "%s", card->FirstChild("name")->FirstChild()->Value());
sprintf(infos->date, "%s", card->FirstChild("date")->FirstChild()->Value());
sprintf(infos->id, "%s", card->FirstChild("filename")->FirstChild()->Value());
////////////////////////
// Gates
char * gates = (char*) card->FirstChild("gates")->FirstChild()->Value();
//////////////////////////
The only problem is on 'gates'.
The input form XML looks like "gate1/gate2/gate3" or just blank sometimes.
I want gate1 to be in infos->gates[0] ; etc.
I want to be able to list the gates array afterwards..
I always have a segfault when I try.
Btw, I don't really now how to initialize this array of pointers. I always initialize all gates[i] to NULL but It seems that I've a segfault when I do
for(int i=0;i
Thanks for all.
It's OK when I've only pointers but when String(char*) / Arrays / Pointers are mixed.. I can't manage =P
I saw too that we can use something like
int *myArray = calloc(NbOfRows, NbOfRows*sizeof(int));
Why should we declare an array like that.. ? x)
Thanks!
The problem that people frequently have with XML is that they assume all the elements are available. That's not always safe. Thus this statement:
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
Isn't safe to do because you don't actually know if all of those
functions actually return valid objects. You really need something
like the following (which is not optimized for speed, as I don't
know the tinyXML structure name being returned at each point and thus
am not storing the results once and am rather calling each function
multiple times:
if (card->FirstChild("firstname") &&
card->FirstChild("firstname")->FirstChild()) {
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
}
And then, to protect against buffer overflows from the data you should
really be doing:
if (card->FirstChild("firstname") &&
card->FirstChild("firstname")->FirstChild()) {
infos->firstname[sizeof(infos->firstname)-1] = '\0';
snprintf(infos->firstname, sizeof(infos->firstname)-1, "%s", card->FirstChild("firstname")->FirstChild()->Value());
}
Don't you just love error handling?
As to your other question:
I saw too that we can use something like int *myArray =
calloc(NbOfRows, NbOfRows*sizeof(int)); Why should we declare an array
like that.. ? x)
calloc first initializes the resulting memory to 0, unlike malloc.
If you see above where I set the end of the buffer to '\0' (which is
actually 0), that's because malloc returns a buffer with potentially
random (non-zero) data in it. calloc will first set the entire buffer
to all 0s first, which can be generally safer.