Understanding String assignments in C - c

Okay I've read through a massive amount of of the answers here on SO, and many other places but I just can't seem to grasp this simple function. Please forgive me for something so simple I haven't done c/c++ code in over 8 years and I'm very much trying to re-learn, so please have patience...
I've tried many different ways to do this from assigning a string through a function param by shifting in the value to just straight returning it, but nothing seems to work within the while. I also get no errors during compile time, but I do get segfaults at runtime. I would very much like to find out why the following function does not work... I just don't understand why the else returns fine as type char *content, but strcat(content, line); does not. Even though the man pages for strcat shows that strcat's definition should be (char *DEST, const char *SRC). As I currently understand it trying to do a cast to a const char on the line variable within the while would just return an integer to the pointer. So I'm stumped here and would like to be educated by those who have some time!
char * getPage(char *filename) {
FILE *pFile;
char *content;
pFile = fopen(filename, "r");
if (pFile != NULL) {
syslog(LOG_INFO,"Reading from:%s",filename);
char line [256];
while (fgets(line, sizeof line, pFile) != NULL) {
syslog(LOG_INFO,">>>>>>>Fail Here<<<<<<<");
strcat(content, line);
}
fclose(pFile);
} else {
content = "<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title></head><body><h1>Does Work</h1></body></html>";
syslog(LOG_INFO,"Reading from:%s failed, serving static response",filename);
}
return content;
}
Very much appreciate all the great answers in this post. I would give everyone in the discussion a checkmark but unfortunately I can't...

This is pretty simple, but very surprising if you're used to a higher-level language. C does not manage memory for you, and C doesn't really have strings. That content variable is a pointer, not a string. You have to manually allocate the space you need for the string before calling strcat. The correct way to write this code is something like this:
FILE *fp = fopen(filename, "r");
if (!fp) {
syslog(LOG_INFO, "failed to open %s: %s", filename, strerror(errno));
return xstrdup("<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title>"
"</head><body><h1>Does Work</h1></body></html>");
} else {
size_t capacity = 4096, offset = 0, n;
char *content = xmalloc(capacity);
size_t n;
while ((n = fread(content + offset, 1, capacity - offset, fp)) > 0) {
offset += n;
if (offset == capacity) {
capacity *= 2;
content = xrealloc(content, capacity);
}
}
if (n < 0)
syslog(LOG_INFO, "read error from %s: %s", filename, strerror(errno));
content[offset] = '\0';
fclose(fp);
return content;
}
Notes:
Error messages triggered by I/O failures should ALWAYS include strerror(errno).
xmalloc, xrealloc, and xstrdup are wrapper functions around their counterparts with no leading x; they crash the program rather than return NULL. This is almost always less grief than trying to recover from out-of-memory by hand in every single place where it can happen.
I return xstrdup("...") rather than "..." in the failed-to-open case so that the caller can always call free(content). Calling free on a string literal will crash your program.
Gosh, that was a lot of work, wasn't it? This is why people tend to prefer to write web apps in a higher-level language. ;-)

You need to allocate memory for content. It has to be big enough for the entire file the way you are doing it. You can either allocate a huge buffer up front and hope for the best, or allocate a smaller one and realloc it as needed.
Even better would be rearranging the code to avoid the need for storing the whole file all at once, although if your caller needs a whole web page as a string, that may be hard.
Note also that you need to return the same type of memory from both your code paths. You can't return a static string sometimes and a heap-allocated string other times. That's guaranteed to call headaches and/or memory leaks. So if you are copying the file contents into a block of memory, you should also copy the static string into the same type of block.

content is just a pointer to a string not an actual string - it has 0 bytes of space reserved for your string. You need to allocate memory large enough to hold hour string. Note that after you will have to free it
char *content=malloc(256);
And your code should be ok - oh and I suggest using strncat
The 2nd assignment to content worked ok before - because you are setting the pointer to point to your const string. If you change content to a malloc'ed region of memory - then you would also want to strncpy your fixed string into content.
Ideally if you can use C++ std::string.

char *foo is only a pointer to some piece of memory holding the characters that form the string. So you cannot use strcat because you don't have any memory to copy to. Inside the if statement you are allocating local memory on the stack with char line[256] that holds the line, but since that memory is local for the function is will disappear once it returns, so you cannot return line;.
So what you really want is to allocate some persistent memory, e.g. with strdup or malloc, so that you can return it from the function. Note that you cannot mix constants and allocated memory (because the user of your function must free the memory - which is only possible if it is not a constant).
So you could use something like this:
char * getPage(const char *filename) {
FILE *pFile;
char *content;
pFile = fopen(filename, "r");
if (pFile != NULL) {
syslog(LOG_INFO,"Reading from:%s",filename);
/* check the size and allocate memory */
fseek(pFile, 0, SEEK_END);
if (!(content = malloc(ftell(pfile) + 1))) { /* out of memory ... */ }
rewind(pFile);
/* set the content to be empty */
*content = 0;
char line [256];
while (fgets(line, sizeof line, pFile) != NULL) {
syslog(LOG_INFO,">>>>>>>Fail Here<<<<<<<");
strcat(content, line);
}
fclose(pFile);
} else {
content = strdup("<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title></head><body><h1>Does Work</h1></body></html>");
syslog(LOG_INFO,"Reading from:%s failed, serving static response",filename);
}
return content;
}
It is not the most efficient way of doing this (because strcat has to find the end every time), but the least modification of your code.

An earlier answer suggested the solution:
char content[256];
This buffer will not be large enough to hold anything but the smallest files and the pointer content goes out of scope when return content; is executed. (Your earlier line, content = "static.."; is fine, because the string is placed in the .rodata data segment and its pointer will always point to the same data, for the entire lifetime of the program.)
If you allocate the memory for content with malloc(3), you can "grow" the space required with realloc(3), but this introduces the potential for a horrible error -- whatever you handed the pointer to must clean up after the memory allocation when it is done with the data (or else you leak memory), and it cannot simply call free(3) because the content pointer might be to statically allocated memory.
So, you have two easy choices:
use strdup(3) to duplicate the static string each time you need it, and use content = malloc(size); for the non-static path
make your caller responsible for providing the memory; every call needs to provide sufficient memory to handle either the contents of the file or the static string.
I would probably prefer the first approach, if only because the size needed for the second approach cannot be known prior to the call.

content is a wild pointer; the variable contains garbage, so it's pointing somewhere into left field. When you copy data to it using strcat, the data goes to some random, probably bad, location. The cure for this is to make content point somewhere good. Since you want it to outlive your function call, it needs to be allocated someplace besides the function's call stack. You need to use malloc() to allocate some space on the heap. Then the caller will own the memory, and should call free() to delete it when it's no longer needed.
You'll need to change the else part that directly assigns to content, as well, to use strcpy, so that the free() will always be valid. You can't free something that you didn't allocate!
Through all of this code, make sure you remember how much space you allocated with malloc(), and don't write more data than you have space, or you'll get more crashes.

Related

Does C automatically free char * return if I use it for another function?

I have a function called combine that combines two strings, potentially with a / when I use it for file path combinations:
char* combine(const char* input1, const char* input2, int slash) {
char* output = malloc(512);
if (slash)
sprintf(output, "%s/%s", input1, input2);
else
sprintf(output, "%s%s", input1, input2);
return output;
}
I then call this function directly in many cases and input right into another such as:
mkdir(combine(localname, "metadata", 1), 0700);
Will the memory allocated for combine(); be freed afterward? I use this method of coding instead of assigning the combine() output to a local variable and freeing it because it looks much nicer, and it's easier to read, but if this is going to cause memory leaks I will stop.
No,
mkdir(combine(localname, "metadata", 1), 0700);
will pass the alloced pointer to mkdir, and then it is lost, so you get memory leak.
There are at least two ways to solve this
Return pointer to a static buffer (or static pointer to a single malloc buffer, so there is just one "memory leaked" block), which will be re-used every call. Bad for multi-threaded apps, and you need to take a copy (like with strdup) if you need to store the buffer accross multiple calls, so you have to be careful with nested calls using the function. Not recommended.
Pass the buffer as parameter, return pointer to it, much like strncat.

Using realloc to reduce the size of a memory block

A little more than 20 years ago I had some grasp of writing something small in C , but even at that time, I probably didn't really do things right all the time. Now I'm trying to learn C again, so I'm really a newbie.
Based on this article:
Using realloc to shrink the allocated memory
, I made this test, which works, but troubles me:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int test (char *param) {
char *s = malloc(strlen(param));
strcpy(s, param);
printf("original string : [%4d] %s \n", strlen(s), s);
// reduce size
char *tmp = realloc(s, 5);
if (tmp == NULL) {
printf("Failed\n");
free(s);
exit(1);
} else {
tmp[4] = 0;
}
s = tmp;
printf("the reduced string : [%4d] %s\n", strlen(s), s );
free(s);
}
void main(void){
test("This is a string with a certain length!");
}
If I leave out "tmp[4] = 0", then I still get back the whole string. Does this mean the rest of the string is still in memory, but not allocated anymore?
how does c free memory anyway? Does it keep track of memory by itself or is it something that is handled by the OS?
I free the s string "free(s)", do I also need to free the tmp str (it does point to the same memory block, yet the (same) address it holds is probably stored on another memory location?
These are most likely just basics, but none of what I have read so far has given me a clear answer (including mentioned article).
If I leave out "tmp[4] = 0", then I still get back the whole string.
You've invoked undefined behavior. All the string operations require the argument to be a null-terminated array of characters. If you reduce the size of the allocation so it doesn't include the null terminator, you're accessing outside the allocation when it tries to find it.
Does this mean the rest of the string is still in memory, but not allocated anymore?
In practice, many implementations don't actually re-allocate anything when you shrink the size. They simply update the bookkeeping information to say that the allocated length is shorter, and return the original pointer. So the remainder of the string stays the same unless you do another allocation that happens to use that memory.
This can even happen when you grow the size. Some designs always allocate memory in specific granularities (e.g. powers of 2), so if you grow the allocation but it doesn't exceed the granularity, it doesn't need to copy the data.
how does c free memory anyway? Does it keep track of memory by itself or is it something that is handled by the OS?
Heap management is part of the C runtime library. It can use a variety of strategies.
I free the s string "free(s)", do I also need to free the tmp str (it does point to the same memory block, yet the (same) address it holds is probably stored on another memory location?
After s = tmp;, both s and tmp point to the same allocated memory block. You only need to free one of them.
BTW, the initial allocation should be:
char *s = malloc(strlen(param)+1);
You need to add 1 for the null terminator, since strlen() doesn't count this.

Adding char* to a linked list does not work

I'm sorry to ask what I believe is a silly question but I feel like I have a concept very wrong and need to solve it.
I have this code for listed links, that I've played with a bit to get my head arround it:
http://www.thegeekstuff.com/2012/08/c-linked-list-example/
And now I need that same way of operating with lists, but instead of the struct being int val, I want it to be char * val, so the struct would be like:
struct nodoTemas{
char * nombreTema;
struct nodoSuscriptor * nodoCerodeSuscriptores;
struct nodoTemas * next;
};
I get the char * from reading from a file like this:
while ((read = getline(&line, &len, fp)) != -1) {
add_to_list(line,true);
}
I can not fin the difference at a conceptual level, at all. Yeah getline adds a \n to the char * so it kind of messes my fprintf's and what not, but the result I get from executing the code is:
creating list with headnode as [tema1
]
Adding node to end of list with value [tema2
]
-------Printing list Start-------
[tema2
]
[tema2
]
When it should be
-------Printing list Start-------
[tema1
]
[tema2
]
Since that's what it got first!!! I just don't see the difference, I've re-read info about char*, char and char[], I've re-read getline man, and I simply don't understand why if the code in the link works perfectly fine with int's, if I use char*, it adds and iterates properly and then kind of... breaks down and overwrites?? Is that what it's happening? No clue.
Also if I should be posting this somewhere else please tell me.
I also found this post but I think the problem comes from a different matter in my case, not completly sure though
Adding Char Value to a Linked List using Looping [C]
As I guessed in my original comment, you have problem because you are not managing the space allocated by getline() and used by add_to_list() correctly.
You say you are using:
while ((read = getline(&line, &len, fp)) != -1) {
add_to_list(line, true);
}
You haven't shown the code for add_to_list(), but the symptoms are that you simply assign the value of line (the pointer) in the list, rather than making a copy for the space in the function. You can probably fix the code superficially by:
char *line = 0;
size_t len = 0;
ssize_t nbytes;
while ((nbytes = getline(&line, &len, fp)) != -1)
{
add_to_list(line, true);
line = 0;
len = 0;
}
free(line);
This will allocate new memory for each line read. The add_to_list() function can then use it.
However, you have to be sure that you do end up freeing the memory eventually — when you free the list. Note that the free() shown is not the only one you need. It releases any memory pre-emptively allocated by getline() before it detected EOF.
An alternative design is:
char *line = 0;
size_t len = 0;
ssize_t nbytes;
while ((nbytes = getline(&line, &len, fp)) != -1)
add_to_list(line, true);
free(line);
Now you have to modify add_to_list() to make a copy of the data from line. You still have to free the line, as shown, and the code that frees the list still has to release the copied data.
I already had a free(line), but you mention that's not the only free I need. Mind expanding a bit on that one? Would it be necesary to free every node of the list? The list is supposed to hold topics in a publisher-subscriber model so unless the process ends, I believe the memory should hold the list?
There is no need to free anything until you've finished with it; premature freeing is as bad as never freeing. When you allocate memory, you always have to know when the memory will be freed. For each allocation, make sure you can identify how the memory will be freed, and when it will be freed.
It is sometimes a legitimate decision that the memory will never be released (or not until the process exits), but for long-running processes, that is something to decide carefully. And even then, it is only legitimate if there is a pointer (or chain of pointers) that allows you to get at the memory. If there is no way to access the memory, then it is leaked and wasted, and that is not a good idea in big programs (especially not in long-running programs).
If you allocated memory for each item in the list, then you will need to free the memory for each item in the list when you free the item. That may occur when you demolish the whole list, or it may happen piecemeal as you decide that an item is no longer needed. What's crucial is that you know that memory was allocated, and that it will be freed when it is no longer needed. If you don't ensure this, you will leak memory, and your program may grow until it runs out of memory.
If possible, use a tool like valgrind to help you keep track of memory misuse — it spots both memory leaks and some memory abuse (reading and writing out of bounds).
getline is reusing the pointer of line for storing the char data. use strdup to store it in your link node.

Dynamically allocating lines from stdin?

I'm trying to read a file of 10 lines of varying lengths (which will not exceed 349 characters), and store them in a dynamically allocated array in order to be able to manipulate them later on. I have the below code which gives me a "warning: passing argument 1 of âstrcpyâ makes pointer from integer without a cast" message.
What did I do wrong here? My logic was that addresses should be a pointer to an array of pointers, where each pointer in the array points to a line in the string, which I would try to make happen by using strcpy of that line to that point.
char buffer[350];
char** addresses;
int i = 0;
*addresses = malloc(sizeof(char*)*10); /* Let's start off with 10 addresses */
while(fgets(buffer, sizeof(buffer), stdin) != NULL)
{
strcpy(*addresses[i],buffer);
i++;
}
You've got couple of issues:
Syntax:
Use
strcpy(addresses[i],buffer);
instead of
strcpy(*addresses[i],buffer);
Memory:
You need to allocate memory for addresses[i] before you can copy the contents of buffer to it.
Add a line to allocate memory for addresses[i] before the call to strcpy.
addresses[i] = malloc(sizeof(buffer));
You have to give like this,
strcpy(addresses[i],buffer);
If you give like this,
strcpy(*addresses[i],buffer);
First argument will consider as a single character.
And while allocating the memory you have do like this,
address=malloc(sizeof(char)*10);
You have to allocate the memory for each pointer.
address[i]=malloc(strlen(buffer)+1);
Or else, you can use strdup function which will allocate the memory for given
string length.
address[i]=strdup(buffer);
The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory
for the new string is obtained with malloc(3), and can be freed with free(3).
addresses is a pointer to pointer so you need to allocate memory for your pointers first and later allocate memory to each pointers individually as shown below.
In 2D space if addresses is a pointer to a pointer then addresses[i] is a pointer.So allocate memory for it before writing to this location.
char** addresses;
addresses = malloc(sizeof(char*)*10);// Now you 10 character pointers
int i = 0;
while(fgets(buffer, sizeof(buffer), stdin) != NULL)
{
size_t n = strlen(buffer);
if(n>0 && buffer[n-1] == '\n')
buffer[n-1] = '\0';
if(i>=10)
break;
addresses[i] = malloc(strlen(buffer)+1);//Allocate memory to the pointer before writing to it
strcpy(addresses[i],buffer);
i++;
}
PS: fgets() comes with a newline character so I have added a check to replace a newline character with a null.
To quickly address the errors I see here:
You're dereferencing addresses (eg. *addresses or addresses[0]) without ensuring that addresses points somewhere valid. You need to assign to addresses before you dereference.
The type of *addresses[i] in strcpy(*addresses[i],buffer) is char, where it is expected that it be char *. This is what your compiler is complaining about. I suspect that you meant strcpy(addresses[i], buffer).
Even if you're to run this code in an embedded environment, your requirements seem simple enough that you shouldn't need malloc. In fact, introducing malloc complicates things... I tend avoid it unless absolutely necessary. Use automatic storage duration, instead. It'll make life easier, because you won't have as many error situations to handle (though as it is you aren't handling them anyway)...
#define nelem(array) (sizeof array / sizeof *array) /* number of elements in array */
int main(void)
{
char address[10][351] = { 0 };
size_t size = 0;
while (size < nelem(address) && fgets(address + size, sizeof *address, stdin))
{
address[size][strcspn(address[size], "\n")] = '\0';
size++;
}
}
Note that 10 and 351 only appear once here... Feel free to adjust them as you feel fit, within reason. If you can multiply them into the megabytes region, you might want to consider a different data structure depending on what you intend to do with this.

malloc/free, appear to be getting multiple frees

I've written a function to test if a given path is a valid Maildir directory (standard Maildir has the three subfolders "cur" "new" and "tmp" ). Function takes in the supposed directory, checks for those subfolders, and returns appropriately.
I'm getting a segfault at the second free statement with the current code, and I similarly got an "invalid next size" error with code of slightly different organization. Even more confusing, it only segfaults on some directories, while successfully completing on others, with no discernible reason (though it is consistent on which ones it will segfault on). With the second free() commented out, all accurately-formatted directories complete successfully.
Obviously I'm double-freeing. My question is, why and how? If the first free is inside the conditional statement and we return immediately after freeing, we never get to the second free. If we get to the second free, that means we skipped the first one... right?
I realize in this context it's perfectly fine because the system will reclaim the memory at the end of the program, but I'm more interested in the reason this is happening than in just making the code work. What if I were looking at a different situation, functions called by functions called by functions etc. and memory could possibly be a concern? Don't I need that 2nd free to reclaim memory?
int is_valid_folder(char* maildir)
{
struct stat *buf;
buf = (struct stat *) malloc(sizeof(struct stat));
char* new = strdup(maildir);
char* cur = strdup(maildir);
char* tmp = strdup(maildir);
strcat (cur, "/cur"); strcat (new, "/new"); strcat (tmp, "/tmp");
if(stat(cur, buf) || stat(tmp, buf) || stat(new, buf))
{
printf("Problem stat-ing one of the cur/new/tmp folders\n");
printf("Error number %d\n", errno);
free(buf);
return 1;
}
free(buf);
return 0; //a valid folder path for this function
}
You have several buffer overflows: strdup() probably allocates a char array that is just large enough to hold the maildir string, and the calls to strcat() will then overflow the arrays. (strcat(), as opposed to strdup(), does not create a new char array, so you must ensure yourself that the array you give it is large enough to hold the resulting string.)
By the way, valgrind is your friend when it comes to tracking down memory management bugs.
There's not enough space in the duplicate strings for the concatenation.
try:
char* new = (char*)calloc(strlen(maildir) + 5);
etc
I know you got it, but just as a tip... (too big for a comment)
Check the return value of strdup() for NULL and free() those pointers when you are done with them. If you don't memory will leak (it is leaking in your current code).
The strdup() function shall return a pointer to a new string, which is a duplicate of the string pointed to by s1. The returned pointer can be passed to free(). A null pointer is returned if the new string cannot be created.

Resources