For the following code I have the following defintion:
typedef struct string {char* data; int length;} string;
If I run the following code with valgrind, I got conditional jump or move depends on unitinialized value and seg. fault:
string* s = (string*) malloc(sizeof(string));
strcpy("Hello", s->data);
free(s);
First of all, I can't understand why I got above errors.
I thought if I add to that code free(s->data) it will freed memory but program will run ok.
How I think:
I know sizeof(string) equal to 4(pointer to char) + 4(int) = 8.
then we allocate 8 bits for s.
strcpy will copy the string into data but I got a problem here. why?
There are multiple problems:
string* s = (string*) malloc(sizeof(string));
which should better be
string* s = malloc(sizeof(*s));
allocates memory for s->data, but does not make s->data point to any valid memory location. If you want to make use of the memory location, you need to make sure that it points to a valid memory location. For example: you'd need to malloc() for s->data seperately.
That said, the syntax for strcpy() says, it's strcpy(dest, source), so in your case
strcpy("Hello", s->data);
attempts to
read from an unitilized memory location
write into a string literal
either of which invokes undefined behaviour.
You should write
strcpy(s->data, "Hello");
after ensuring s->data is a valid destination.
Related
This question already has answers here:
How can I correctly assign a new string value?
(4 answers)
Closed 4 years ago.
Why does this not return a segmentation fault 11?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
char *test;
test = (char*) (malloc(sizeof(char)*3));
test = "foo";
printf("%s\n", test);
test = "foobar";
printf("%s\n", test);
return 0;
}
My outcome is
foo
foobar
I'm fairly new to C, but when I was compiling this using both gcc on mac and Windows Debugger on Windows 10, it doesn't crash like I expected.
My understanding is that by using (char*) (malloc(sizeof(char)*3)), I am creating a character array of length 3. Then, when I assign test to the string foobar I am writing to 6 array positions.
I'm left sitting here, staring at my apparently valid, runnable code scratching my head.
test = "foo";
Here you do not copy the string to the allocated memory, test no longer points to the allocated memory, instead it points to the string literal "foo". Same goes for "foobar". Also as pointed out in the comments the address of the allocated memory is lost and therefore it is a memory leak (since there is no way to retrieve the address of the memory).
If you want to copy a string to another destination you need to use strcpy or loop over every character.
If you write or read outside bounds of the allocated space you are invoking undefined behavior. That means that basicly everything can happen, also that it works.
Your program never writes to the location pointed to by the return from malloc(). All you've done with e.g. test = "foo"; is change what test points to, which by the way is a memory leak since you've then lost what malloc() returned.
To properly use the memory you allocated with malloc(), use strcpy(), snprintf(), etc.
Also, don't forget the null terminator in your C strings. To properly store e.g. "foobar" you need at least 7 bytes, not 6.
First thing is that you waste the memory allocated by malloc unnecessorily by storing the address of foo into that.
If you are going to point to string in code section then there is no need to allocate memory to the pointer.
When to allocate memory to pointer
e.g. when you intended to scan 'n' number of bytes from keyboard in pointer.
char *ptr,num_char;
scanf("%d",&num_char);
ptr = (char *)malloc(num_char*sizeof(char));
scanf("%s",ptr);
Does the following code contain a memory leak of the first three characters in the string?
char * str = (char*)malloc(21 * sizeof(char));
strcpy(str, "01234567890123456879");
str = str + 3;
free(str);
Thanks.
Its worse than a leak, you are not supposed to call free with a pointer not returned from malloc (or realloc/calloc). You could get a leak, or a crash, or who knows what else... What you do is undefined behavior.
Yes, It leaks.
free expects a pointer that was returned by malloc/realloc/calloc. Since, you have changed, it certainly leaks.
You might want to prove this out by testing, but I believe the answer to your question is that incrementing a pointer can cause undefined behaviour, although you are seeing a memory leak.
I am saying this, because nowhere in your example does it appear that you have preserved the original pointer to the allocated memory. It is possible free could do the wrong thing with the pointer pointing to somewhere other than the base address.
To be sure you would need to
1) Look at RAM in use on your system, with as little else running as possible
2) Run the program a few times, and
3) Then look at memory use again.
Then try all that again by altering your code as follows:
char * mem_ptr = (char*)malloc(21 * sizeof(char));
char * str = mem_ptr;
strcpy(str, "01234567890123456879");
str = str + 3;
free(mem_ptr);
I'm trying to define a path at compile time by passing:
-DDCROOTDEF='"/path/to/stuff"'
on the compile line. I then try to get use this in the code like:
char * ptr_path;
strcpy(ptr_path, DCROOTDEF);
strcat(ptr_path,"/MainCommons/CommonLib/fonts/Arial.ttf");
char *pftf=ptr_path;
gdImageStringFT(pimg,brect,iclr,pftf,pts,ang,ixp,iyp, (char *)cbuf);
Which gives me a segmentation fault. However, if I try to print the string first:
char * ptr_path;
strcpy(ptr_path, DCROOTDEF);
strcat(ptr_path,"/MainCommons/CommonLib/fonts/Arial.ttf");
char *pftf=ptr_path;
printf("%s\n",pftf);
gdImageStringFT(pimg,brect,iclr,pftf,pts,ang,ixp,iyp, (char *)cbuf);
It works just fine. What intricacy of char pointer's am I missing here?
Thanks
char * ptr_path;
strcpy(ptr_path, DCROOTDEF);
You never initialize ptr_path.
It doesn't work in the second code snippet, you are just getting unlucky and it appears to work. You're still using an uninitialized pointer and trying to write to who knows where in memory.
You need to initialize ptr_path to point to an array of char that is at least strlen(DCROOTDEF) + 1 in length. You also need to check the length of DCROOTDEF before copying its contents into the array to be sure that it is not too long. You can do so manually using strlen or you can use a length-checked copy function like strlcpy.
The pointer ptr_path is not initialized to point at writable memory, which is why dereferencing it using strcpy() is crashing.
You need to call e.g. malloc() to get the space, first:
char * ptr_path = malloc(PATH_MAX);
Or something like that.
In
char * ptr_path;
strcpy(ptr_path, DCROOTDEF);
strcat(ptr_path,"/MainCommons/CommonLib/fonts/Arial.ttf");
the pointer is not bound to a legally allocated block of memory, so your program runs into undefined behavior. You need to allocate a buffer first - for example by using malloc(). Be sure that the buffer is large enough to hold the resulting string together with the terminating null character.
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.
So I've got some C code:
#include <stdio.h>
#include <string.h>
/* putting one of the "char*"s here causes a segfault */
void main() {
char* path = "/temp";
char* temp;
strcpy(temp, path);
}
This compiles, runs, and behaves as it looks. However, if one or both of the character pointers is declared as global variable, strcpy results in a segmentation fault. Why does this happen? Evidently there's an error in my understanding of scope.
As other posters mentioned, the root of the problem is that temp is uninitialized. When declared as an automatic variable on the stack it will contain whatever garbage happens to be in that memory location. Apparently for the compiler+CPU+OS you are running, the garbage at that location is a valid pointer. The strcpy "succeeds" in that it does not segfault, but really it copied a string to some arbitrary location elsewhere in memory. This kind of memory corruption problem strikes fear into the hearts of C programmers everywhere as it is extraordinarily difficult to debug.
When you move the temp variable declaration to global scope, it is placed in the BSS section and automatically zeroed. Attempts to dereference *temp then result in a segfault.
When you move *path to global scope, then *temp moves up one location on the stack. The garbage at that location is apparently not a valid pointer, and so dereferencing *temp results in a segfault.
The temp variable doesn't point to any storage (memory) and it is uninitialized.
if temp is declared as char temp[32]; then the code would work no matter where it is declared. However, there are other problems with declaring temp with a fixed size like that, but that is a question for another day.
Now, why does it crash when declared globally and not locally. Luck...
When declared locally, the value of temp is coming from what ever value might be on the stack at that time. It is luck that it points to an address that doesn't cause a crash. However, it is trashing memory used by someone else.
When declared globally, on most processors these variables will be stored in data segments that will use demand zero pages. Thus char *temp appears as if it was declared char *temp=0.
You forgot to allocate and initialize temp:
temp = (char *)malloc(TEMP_SIZE);
Just make sure TEMP_SIZE is big enough. You can also calculate this at run-time, then make sure the size is enough (should be at least strlen(path))
As mentioned above, you forgot to allocate space for temp.
I prefer strdup to malloc+strcpy. It does what you want to do.
No - this doesn't work regardless of the variables - it just looks like it did because you got (un)lucky. You need to allocate space to store the contents of the string, rather than leave the variable uninitialised.
Uninitialised variables on the stack are going to be pointing at pretty much random locations of memory. If these addresses happen to be valid, your code will trample all over whatever was there, but you won't get an error (but may get nasty memory corruption related bugs elsewhere in your code).
Globals consistently fail because they usually get set to specific patterns that point to unmapped memory. Attempting to dereference these gives you an segfault immediately (which is better - leaving it to later makes the bug very hard to track down).
I'd like to rewrite first Adam's fragment as
// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, sizeof(temp), path);
temp[sizeof(temp)-1] = '\0';
That way you:
1. don't have magic numbers laced through the code, and
2. you guarantee that your string is null terminated.
The second point is at the loss of the last char of your source string if it is >=256 characters long.
The important part to note:
destination string dest must be large enough to receive the copy.
In your situation temp has no memory allocated to copy into.
Copied from the man page of strcpy:
DESCRIPTION
The strcpy() function copies the string pointed to by src (including
the terminating '\0' character) to the array pointed to by dest. The
strings may not overlap, and the destination string dest must be large
enough to receive the copy.
You're invoking undefined behavior, since you're not initializing the temp variable. It points to a random location in memory, so your program may work, but most likely it will segfault. You need to have your destination string be an array, or have it point to dynamic memory:
// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, 256, path);
// Or, use dynamic memory
char *temp = (char *)malloc(256);
strncpy(temp, 256, path);
Also, use strncpy() instead of strcpy() to avoid buffer overruns.