This question already has answers here:
How can I correctly assign a new string value?
(4 answers)
Closed 5 years ago.
I need to send a value saved in a variable. When trying to pass this in my payload it keeps referencing to the initial value of mydata, which is unsuccessful test.
static uint8_t *mydatapnt;
uint8_t *strdata = "Successfull test";
static uint8_t mydata [] = "Incorrect test";
void send(){
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
Serial.println(F("Packet queued"));
/
}
void setup(){
*mydata = mydatapnt;
}
void loop(){
*mydatapnt = strdata;
Serial.println(*mydatapnt + "");
}
What exactly am I doing wrong? I should receive "successfull test".
You can find the full code project here
About strings...
Strings are just pieces of memory where characters are stored (an array of characters). Strings are terminated by a null character.
To copy a string, you must have sufficient memory in the target string. Then you use strcpy to copy the characters. strcpy appends the terminating null character.
The size of a string is determined with strlen. It does not count the terminating null charcter.
In
char *strptr = "my string";
the pointer points to the literal string. A literal string is read-only. Trying to modify it often leads to a segmentation fault or other terminal error. In:
char myString[] = "my string";
The characters of "my string" are copied to the array myString and the compiler makes its size of the length of the initializing string, plus a null character.
To append to a string, you use strcat. Again, the target must have sufficient memory. You either declare a char array with enough memory, like char mystr[100]; or you can allocate memory with malloc, for example:
char *strptr= malloc(strlen(myString)+1);
strcpy(strptr, myString);
If you no longer need the memory you got from malloc, you must return it to the memory allocator:
free(strptr);
Trying to use strptr after having freed the memory will also result in a segmentation fault or in some other undefined behavior.
*mydatapnt = "success in loop";
This does not store pointer to "success in loop" string into mydatapnt, but rather store address of that string into first character of mydatapnt. (At this time mydatapnt is not initialized, so it clobbered some random memory location.)
What you more likely wanted to do is: mydatapnt = "success in loop";
Serial.println(*mydatapnt + "");
You're not concatenating strings, but adding value of first char from mydatapnt to pointer to empty string, which is more like substring operation. This will likely print something, because of the way strings are stored in executable, but definetely not what you wanted it to print.
You can't concatenate strings in C with simple +. Try just Serial.println(mydatapnt).
Related
I am trying to use the C's strtok function in order to process a char* and print it in a display, and looks like that for some reason I don't know the character '\n' is not substituted by '\0' as I believe strtok does. The code is as follows:
-Declaration of char* and pass to the function where it will be processed:
char *string_to_write = "Some text\nSome other text\nNewtext";
malloc(sizeof string_to_write);
screen_write(string_to_write,ALIGN_LEFT_TOP,I2C0);
-Processing of char* in function:
void screen_write(char *string_to_write,short alignment,short I2C)
{
char *stw;
stw = string_to_write;
char* text_to_send;
text_to_send=strtok(stw,"\n");
while(text_to_send != NULL)
{
write_text(text_to_send,I2C);
text_to_send=strtok(NULL, "\n");
}
}
When applying the code, the result can be seen in imgur (Sorry, I am having problems with format adding the image here in the post), where it can be seen that the \n is not substituted as it is the strange character appearing in the image, and the debugger still showed the character as well. Any hints of where can the problem be?
Thanks for your help,
Javier
strtok expects to be able to mutate the string you pass it: instead of allocating new memory for each token, it puts \0 characters into the string at token boundaries, then returns a series of pointers into that string.
But in this case, your string is immutable: it's a constant stored in your program, and can't be changed. So strtok is doing its best: it's returning indices into the string for each token's starting point, but it can't insert the \0s to mark the ends. Your device can't handle \ns in the way you'd expect, so it displays them with that error character instead. (Which is presumably why you're using this code in the first place.)
The key is to pass in only mutable strings. To define a mutable string with a literal value, you need char my_string[] = "..."; rather than char* my_string = "...". In the latter case, it just gives you a pointer to some constant memory; in the former case, it actually makes an array for you to use. Alternately, you can use strlen to find out how long the string is, malloc some memory for it, then strcpy it over.
P.S. I'm concerned by your malloc: you're not saving the memory it gives you anywhere, and you're not doing anything with it. Be sure you know what you're doing before working with dynamic memory allocation! C is not friendly about that, and it's easy to start leaking without realizing it.
1.
malloc(sizeof string_to_write); - it allocates the sizeof(char *) bytes not as many bytes as your string needs. You also do not assign the allocated block to anything
2.
char *string_to_write = "Some text\nSome other text\nNewtext";
char *ptr;
ptr = malloc(strlen(string_to_write) + 1);
strcpy(ptr, string_to_write);
screen_write(ptr,ALIGN_LEFT_TOP,I2C0);
I have already written a couple of C programs and consider this awkward to ask. But why do I receive a segmentation fault for the following code being supposed to replace "test" by "aaaa"?
#include <stdio.h>
int main(int argc, char* argv[])
{
char* s = "test\0";
printf("old: %s \n", s);
int x = 0;
while(s[x] != 0)
{
s[x++] = 'a'; // segmentation fault here
}
printf("new: %s \n", s); // expecting aaaa
return 0;
}
This assignment is writing to a string literal, which is stored in a read-only section of your executable when it is loaded in memory.
Also, note that the \0 in the literal is redundant.
One way to fix this (as suggested in comments) without copying the string: declare your variable as an array:
char s[] = "test";
This will cause the function to allocate at least 5 bytes of space for the string on the stack, which is normally writeable memory.
Also, you should generally declare a pointer to a string literal as const char*. This will cause the compiler to complain if you try to write to it, which is good, since the system loader will often mark the memory it points to as read-only.
Answering the OP's question in the comment posted to #antron.
What you need is to allocate a character array, then use strcpy() to initialize it with your text, then overwrite with a-s.
Allocation can be done statically (i.e., char s[10];) but make sure the space is enough to store the length of your init string (including the terminating \0).
Alternatively, you can dynamically allocate memory using malloc() and free it using free(). This enables you to allocate exactly enough space to hold your init string (figure it out in run-time using strlen()).
hey guys I need your help. I'm trying to extract a character from a string and set it as the 2nd element of an array of strings. Yet as usual C is giving me segmentation faults, Ive tried sprintf, strcpy, and still segmentation fault the code is:
int getwords(char *line, char *words[])
{
int nwords=2;
char *result= NULL;
char TABS[]="\t";
char spaces[]=" ";
char commas[]=",";
result = strtok(line,TABS);
words[1]=result[strlen(result)-1];//setting the 2nd element of the array to a char
result[strlen(result)-1]='\0';//removing the extracted char from the string
words[0]=result;//setting 1st element to the new modified word
printf("the opcode is:%s and the type is:%c\n",words[0],result[strlen(result)-1]);
return nwords;
}
e.g. If I give it "bye." it should return 2 and an array having 2 elements: 1st elem="bye" 2nd elem="."
I ran some tests and found out that the error is from the statement:
words[1]=result[strlen(result)-1];
Any help is welcom
Are you sure words is a modifiable string?
Literal strings are unmodifiable strings. For example: this gives segmentation fault:
char *test = "forty two";
test[6] = 'T'; /* make two uppercase */
You need to show how you call getwords and the definitions of the variables involved.
I'm guessing you're passing pointers to string literals.
There are two, perhaps four mistakes in the code below, I explain two of the mistakes in the code comments:
If we assume that "line", for the purposes of explaining what happens, is "hey\tthere"...
We also assume that "words" is an array of two pointers to char.
// Find the first token, in this case "hey", and return it in "result".
result = strtok(line,TABS); // NOTE: 'line' has been modified by the function!
// Take the last character in the returned statement, which is 'y', and
// copy it to the second cell in the 'words' array, however we are supposed
// to be copying a *pointer* to char there...
words[1]=result[strlen(result)-1];
Additionally, if "line" is static and can not be changed, the first line above will crash.
If "words" is not allocated or doesn't reference an array of at least two pointers to char, then the second line will crash.
If code execution gets past this point, any code that uses the "words" array will crash because the code will expect pointers, but is getting chars!
I have a program I am writing for uni in C, and when my code reaches this line:
strcat("md5 ", "blah");
I am getting a EXC_BAD_ACCESS error, and my app crashes. As far as I can tell, there isn't anything wrong with it, so I thought a fresh pair of eyes might solve my problem. Any ideas?
You're trying to modify a constant string. The first argument of strcat is also the destination string and in your case it is a constant string. You should use something like:
char s[100];
strcpy(s, "md5 ");
strcat(s, "blah");
In C, you have to provide the space of where to store something yourself. "md5 " has only room for 4 characters (+ a nul terminator). There is not enough space to append "blah" there.
More important, you cannot modify a string literal. They are usually read only.
So, you have to:
Provide storage where you can store the new, concatenated result.
Makes sure there's enough room in that storage for the resulting string.
E.g.:
char result[9]; //the result here needs 8 characters + a nul terminator
strcpy(result,"md5 ");
strcat(result,"blah"
Or, e.g.
const char *a = "md5 ";
const char *b = "blah";
char *result = malloc(strlen(a) + strlen(b) + 1);
if(result == NULL) {
out of memory
return;
}
strcpy(result,a);
strcat(result,b);
The first string supplied to strcat needs to be a writable buffer with enough space for the second string past its current contents. That means that you cannot use string literals as the first argument for strcat()!
According to http://www.cplusplus.com/reference/clibrary/cstring/strcat/ :
Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a new null-character is appended at the end of the new string formed by the concatenation of both in destination.
Since your first string is a constant, you don't know where it is in memory and you aren't allowed to play with the memory that follows it.
I am getting segmentation fault when using strncpy and (pointer-to-struct)->(member) notation:
I have simplified my code. I initialise a struct and set all of it's tokens to an empty string. Then a declare a pointer to a struct and assign the address of the struct to it.
I pass the pointer to a function. I can print out the contents of the struct at the beginning of the function, but if I try to use the tp -> mnemonic in a strncpy function, I get seg fault. Can anyone tell me what I am doing wrong?
typedef struct tok {
char* label;
char* mnem;
char* operand;
}Tokens;
Tokens* tokenise(Tokens* tp, char* line) {
// This prints "load"
printf("Print this - %s\n", tp -> mnem);
// This function gives me segmentation fault
strncpy(tp -> mnem, line, 4);
return tp;
}
int main() {
char* line = "This is a line";
Tokens tokens;
tokens.label = "";
tokens.mnem = "load";
tokens.operand = "";
Tokens* tp = &tokens;
tp = tokenise(tp, line);
return 0;
}
I have used printf statements to confirm that the code definitely stops executing at the strncpy function.
The problem is that tp->mnem is pointing to a string literal, which is generally allocated in a read-only segment of memory. Therefore it's illegal to overwrite it. Most likely what you need to do instead is something like this:
Tokens tokens;
tokens.label = "";
tokens.mnem = strdup("load");
tokens.operand = "";
This will give you a dynamically allocated block of memory for mnem, which you can then write into as much as you like. Of course, you have a couple of other problems too: first, you'll need to remember to release that memory with free later; second, you'll have to be aware of the size of the buffer you've allocated so that you don't overwrite it.
If you know that the contents of mnem will never exceed 4 bytes, then you might instead change your structure declaration like so:
typedef struct tok {
char* label;
char mnem[5]; // note: +1 byte for a NULL terminator
char* operand;
}Tokens;
Then, you'd initialize it like this:
Tokens tokens;
tokens.label = "";
strcpy(tokens.mnem, "load");
tokens.operand = "";
This relieves you of the responsibility of managing the memory for mnem, although you still have some risk of overrunning your buffer.
Following line
tokens.mnem = "load"
assigns mnem to address of string literal, which is typically located in read-only data segment, so changing this memory with strncpy() or any other function will fail.
The problem is you've assigned string literals to the members of your Tokens structure and are trying to overwrite that memory (specifically, the mnem field) in tokenise.
Most modern OSes will allocate memory for string literals from a special read-only section of your program's address space. If you try to write to that memory, then your program will die with a segfault.
This is why the type of a string literal is const char *, not char *. Your compiler should warn you when you try to assign these to the fields of tokenise.
If you want to overwrite the memory later, you need to allocate the memory dynamically using malloc or change the members of the Tokens structure to fixed-length arrays, then copy the initial value into the allocated memory. Of course if you allocate the memory dynamically you need to free it later too.
You're calling strncpy() without having allocated the buffer spacem, just like Shadow said.
The literal string "load" you set the mnem member to in the initializer is not overwritable.
If you want to be able to change the string stored, and the size is reasonable, it might be easiest to just change the declaration of the struct field to char mnem[5];.
Also, please note that strncpy() has quite weird semantics. Check if you have strlcpy(); it's a better function.
You're getting a segmentation fault because this line:
strncpy(tp -> mnem, line, 4);
Is trying to copy four characters from 'line' into a location occupied by a string literal as assigned here:
tokens.mnem = "load";
The string literal is stored in a special text part of your program and may not be modified.
What you need to do is allocate a buffer of your own into which the string will be copied:
tokens.mnem = (char*) malloc (bufferSize);
And free the buffer when you are done using it.
This line is questionable:
strncpy(tp -> mnem, line, 4);
You are relying on a function that returns a pointer to memory that is not allocated. The return of *tokenise() is undefined. Its returning a pointer to memory that could contain all kinds of stuff, and that you don't have permission to modify.
It should return an allocated pointer.
You might malloc the tp variable. If you don't malloc there is no guarantee that the memory is actually yours. Don't forget to free the memory when you are finished.