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?
Related
So I have this code:
char inte[10];
while(j<noinput) {
fscanf(circuit,"%s",inte);
vararray[count]=inte;
count++;
j++;
}
However when I print the contents of the array like this:
for (h=0;h<noinput+2;h++){
printf("%dth variable: %s\n",h,vararray[h]);
}
The elements past the first two (which are reserved for special elements) are all equal to the LAST string that I had taken in from fscanf earlier. I have no idea how one of the strings from fscanf could be equal to multiple slots in the array when I am only setting
vararray[count]=inte;
Shouldn't this mean that each element of the array will be different since I am incrementing count every time? I am so confused. I also tried doing:
fscanf(circuit,"%s",vararray[count]);
But this also did not work and gave me null elements for certain indexes.
you are doing something too wrong. By "vararray[count]=inte;" you are doing pointer assignment so all of your vararray is getting filled by same string. I am guessing you are new to C so I will answer due to that. Correct way would look something like below
Fixed size solution:
char vararray[ROWCOUNT][BUFFERSIZE];
for(count=0; j<noinput; ++count, ++j) {
fscanf(circuit,"%s",(char*)vararray[count]);
}
With dynamic memory management
char * vararray[ROWCOUNT];
for(count=0; j<noinput; ++count, ++j) {
vararray[count] = (char*)malloc(BUFSIZE);
fscanf(circuit,"%s", vararray[count]);
}
I want to warn you in the way of becoming an expert on C nowadays is somewhat madness , i mean unless you have another choice. Examples below I put and the thing you wrote are completely unsafe and unsecure...
You're not copying the string. Here's what's happening:
char *vararray[462]; // declare an array of string pointers
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
// do something
vararray[i] = inte;
}
This is causing all of the items of vararray to point to the memory also referred to as inte... but you're overwriting that each time! Instead, do something like this:
#include <string.h> // write me at the top, outside main()!
char vararray[462][10]; // declare an array of strings (max length 9)
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
fscanf(circuit,"%10s",inte); // use buffer size to make xscanf safer
strncpy(vararray[i], inte, 9); // copy inte, making sure not to buffer overflow!
vararray[i][9] = '\0'; // if inte is too big, a null byte won't be added to the end of the string; make sure that it's there
}
This copies the string! Your problem should go away when you do this.
What I want to do is take the string, save it into an array of strings and then modify each copy based on index.
I edited the question and code, it was messy and unclear. As the question is almost the same (I think now it's more precise), I thought I could edit it entirely here without creating a new question, but let me know if I have to do differently.
PROBLEM (EDIT): after reading the answer given, creating an MVCE, and reading this and some tips to debug, I think I am doing a mess with pointers and strcpy... Why does the following code (edited to be MVCE) gives this output?
abc
x
x
y
It compiles and gives no debug errors, but I want the code to change the first char of the string in line_ret to "x" if index==0, and to "y" if index==1.
I read here it's not possible to change a single char in what a pointer points to, but what if I don't know how many times I have to copy line_read into line_ret, thus don't know the maximum index size to declare the array line_ret?
Code (EDIT):
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
size_t len = 10;
int main(void){
char *line_read = malloc(5);
strcpy(line_read, "abc");
char **line_ret = malloc(5 * sizeof(char*));
int index = 0;
while(index < 2){
line_ret[index] = realloc(line_ret, 1*len);
memcpy(&line_ret[index], &line_read, len);
printf("%s\n", line_ret[index]);
if(index == 0){
strcpy(&line_ret[index][0], "x");
} else if(index == 1){
strcpy(&line_ret[index][0], "y");
}
printf("%s\n", line_ret[index]);
index++;
}
free(line_read);
free(line_ret);
return 0;
}
If you copy addresses (stored in pointers) to several entries in an array, but they all point to the same piece of memory (i.e. the addresses are same) then using the different entries in that array will always result in the same piece of memory being overwritten.
Debug by comparing/printing the addresses you store in the different entries in your array. The bug is where two array entries contain the same address.
To fix, make sure that the entries in the array receive different addresses, i.e. refer to separatly malloced pieces of memory.
This implies that each time you change the index used to access your array you also need to malloc a new piece of memory to use along with that different array entry.
i am just doing a program in C, which has to read different files in csv-format. Every file consists of two lines.
The first line describes what topics are saved, while the second lines contains the data of the topics. Every file has
6 columns. The data are information like dates, source and category. I actually wrote a program that get the path and
gives the content in one dynamical char array back, but there is always a Debug Assertion Error that crashes it all the time. My Code is:
char* readfile(char csvpath[]) {
FILE *csv;
int c;
int countcontent = 100; //counter for the length of the content array
int counter = 0; //counter of the amount of the inserted chars
char *temp; //temp = buffer
char *content = (char*)calloc(100,sizeof(char)); //content of the file
csv = fopen(csvpath,"r");
while(c = fgetc(csv) != EOF) { //while file isnt at the end
if(countcontent <= counter) {
realloc(content,100*sizeof(char));
countcontent += 100;
}
temp = (char*)calloc(20,sizeof(char));
fgets(temp,20,csv);
content = concat(content,temp); //concat is my own function and add the 2. string behind the 1.
counter+= 20;
}
fclose(csv);
return content;}
Actually i ignore that there are two different lines, cause i want to delete the first one at the end anyway, because no data is saved there. But can you help me to find the solution for this error?
Your problem is this line
realloc(content,100*sizeof(char));
The realloc function returns a pointer to the reallocated memory. Think about what would happen if the realloc call can't just resize the already allocated chunk, and has to actually allocate a completely new chunk of memory, because it can't automatically update the pointer you pass to it.
Another problem with that statement is that it doesn't matter how much more memory you need, you will always allocate 100 bytes. The size argument you provide to realloc is the new size.
Oh, and remember that realloc can fail, and return a NULL pointer. If you don't want to loose your original pointer, you need to have a temporary variable to store the returned pointer, and check it for NULL.
After the initialisation in main(void):
char *params[MAXPARAMS] = {NULL};
params is passed to different functions.
How can I 'reset' the array just as it was during initialisation (after some other functions stored strings in it)?
Edit: params is used as a parameter list, so it might not be fully populated after certain operations. By 'reset' I meant: I want no string values left inside the array, like how you clear a string array in Java, but keeping the same array size.
Considering the memory allocation is done proerly and it is not freed, I think you need to check the memset() function, if you are targeting the values held the array. Please check the man page here.
Otherwise, if you want to be in the same position as the time of initialization, you can free() the allocated memory and again set the variable as NULL.
Please clarify what do you mean by reset. We'll be able to help out in a batter way then.
I think what you want to do is this,
for(i=0;i<MAXPARAMS;i++)
memset(params[i],'\0',strlen(params[i]));
keeping the length of each string intact.
If params has allocated strings, first free the allocations
for (size_t i=0; i<MAXPARAMS; i++) free(params[i]);
To get everything back to NULL
for (size_t i=0; i<MAXPARAMS; i++) params[i] = NULL;
// or
memset(params, 0, sizeof params);
Suggest combining:
for (size_t i=0; i<MAXPARAMS; i++) {
free(params[i]);
params[i] = NULL;
}
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.