Lacking money ATM so I'm offering $0.25 via paypal to the first person to point out what I did wrong in this code snippet -- I hope this doesn't violate the site rules or insult anybody.
I want to modify an multi-dimensional array in a function. It gets modified while in the function, but when scope returns to the main function the array is unchanged.
The function headers cannot be modified. Thanks for helping me out.
void getAlignment(char*s1, char*s2, char*s3, char*aligned[])
{
/***********************
Code here which assigns
char**tmp to "different" "words"
***********************/
printf("tmp in getAlignment function\n");
printf("%s %s\n", tmp[0], tmp[1]); // prints "different words", as expected
aligned = tmp;
}
int main(void)
{
// skipped some code
char** aligned = (char**)malloc(sizeof(char*)*2);
aligned[0] = "should";
aligned[1] = "change";
printf("%s %s\n", aligned[0], aligned[1]); // prints "should change", as expected
getAlignment(s1, s2, transcript, aligned); // how do i change aligned during this call?
printf("%s %s\n", aligned[0], aligned[1]); // prints "should change"
return 0;
}
When you write inside getAlignment:
aligned = (char**) malloc(2*sizeof(char*));
you are making the pointer GetAlignment::aligned point to some new memory. It no longer points to the memory that main::aligned points to. When you operate on this new memory, it has no effect on the memory that main::aligned was and is pointing to.
(Note - the :: is not C syntax, but my meaning is to disambiguate your two variables which are both called aligned in their local scope, despite the fact that they are two separate variables).
If your intent is that the code in getAlignmentmodifies the memory being pointed to by main::aligned then just remove the above line.
If your intent is that getAlignment be able to allocate new memory, and main::aligned be switched to use that new memory, then you have to pass main::aligned by reference (i.e. add an extra level of indirection in the function call). And don't forget to free() the previously-allocated memory.
BTW don't cast malloc.
You don't need to allocate 2D array again by aligned = (char**) malloc(2*sizeof(char*)); in function getAlignment because you already allocated it in main.
You require allocate each element of array like
int alignedStrLen = strlen(s3);
aligned[0] = malloc((alignedStrLen+1)*sizeof(char));
aligned[0][alignedStrLen] = '\0';
aligned[1] = malloc((alignedStrLen+1)*sizeof(char));
aligned[1][alignedStrLen] = '\0';
And and you should free the memory allocated by malloc.
Related
I want to store pointers that have been allocated using malloc() in an array and then free all of them after. However even though the program doesn't complain it doesn't work. Below cleanMemManager() won't actually free the memory as when tested inside main() the char* pointer is not NULL and it will print ???.
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void **ptrList = NULL;
void tfree(void** ptr)
{
free(*ptr);
*ptr = NULL;
}
void* talloc(int size)
{
void* ptr = malloc(size);
ptrList[0] = ptr; ///No clue if this actually does what I think it does
return ptrList[0];
}
void initMemManager()
{
ptrList = (void**)malloc(sizeof(void**) * 3);
memset(ptrList, 0, sizeof(void**) * 3);
}
void cleanMemManager()
{
tfree(&ptrList[0]); //Doesn't free the right pointer it seems
}
int main()
{
initMemManager();
char* ptr = (char*)talloc(3);
cleanMemManager();
if (ptr != NULL) //This will trigger and I'm not expecting it to
printf("???");
getchar();
return 0;
}
I don't understand the syntax to use for this, does the pointer not actually get touched at all? What is it freeing then since it doesn't throw any errors?
In main, char *ptr = (char*)talloc(3); declares ptr to be a local variable. It contains a copy of the value returned by talloc, and none of your subroutines know about ptr or where it is. So none of them change the value of ptr. Thus, when you reach if (ptr != NULL), the value of ptr has not changed.
Additionally:
In initMemManager, you should use sizeof(void *) in two places where you have sizeof(void**). In these places, you are allocating and copying void * objects, not void ** objects.
It looks like you are trying to implement a sort of smart memory manager that automatically sets pointers to NULL when they are freed. To do that in C, you would have to give up having copies of pointers. For example, ptr is a copy of ptrList[0], but tfree only sets whichever copy it is passed to NULL. We could give advice on building such a system, but it would quickly become cumbersome—your memory manager needs to keep a database of pointers and their copies (and pointers derived from them, as by doing array arithmetic). Or you have to refer to everything indirectly through that ptrList array, which adds some mess to your source code. Unfortunately, C is not a good language for this.
Freeing doesn't guarantee that pointers pointing to the allocated block will be set to NULL. If you actually try doing
if (ptrList[0] != NULL)
printf("ptrList[0] != NULL");
you will see that the program won't output and if you remove the cleanMemManager() function call, it will output. This means tfree function is working as intended, it's freeing the memory that was allocated.
Now as to why ptr variable being not set to NULL, it's simply because ptr is still storing the old address. cleanMemManager() has no way of mutating the variable ptr. This is commonly called dangling pointer or use after free.
Also free() doesn't clean/zero out the the allocated space, the block is simply marked as "free". The data will most likely remain in the memory for a moment until the free block is overwritten by another malloc request.
I'm trying to add a string at the end of pointer to pointer in C, I'm using the below code the problem is I can't free what I have allocated as the pointer to pointer has values that were not all allocated memory, How can I add string at the end of a pointer to pointer properly?
int add_environ(char *str, char **envp)
{
char **r;
int i;
r = envp;
i = 0;
while (r[i])
{
i++;
}
//how can I add string without using malloc?
// my problem is I can't free this allocated memory
r[i] = malloc(strlen(str));
if (r[i])
{
r[i] = str;
r[++i] = 0;
return (1);
}
return (0);
}
If your question only targets environmental variables, then I would go with setenv. If not, Waxrat gave you one solution. Oh, btw: You're assuming in your code, that envp is arbitrarily long. That's not true either, so your code will crash sooner or later. Depending on your exact requirements, I would suggest to create a deep copy of envp in main and then only operate on that copy, because then everything is malloced and needs to be freed, so nothing special to track any longer.
You'd need to remember somehow which items in the *envp array came from the heap. For example, by storing them in another array of pointers. There's no real general way to do what you describe, you'd need to do something context specific.
I'm creating a source files containing buffer functionality that I want to use for my other library that I'm creating.
It is working correctly but I'm having trouble getting rid of the buffer structure that I'm creating in one of the functions. The following snippets should help illustrate my problem:
C header:
//dbuffer.h
...
typedef struct{
char *pStorage;
int *pPosition;
int next_position;
int number_of_strings;
int total_size;
}DBUFF;
...
C source:
//dbuffer.c
...
DBUFF* dbuffer_init(char *init_pArray)
{
//Find out how many elements the array contains
int size = sizeof_pArray(init_pArray);
//Initialize buffer structure
DBUFF *buffer = malloc(sizeof(DBUFF));
//Initialize the storage
buffer->pStorage = malloc( (sizeof(char)) * (size) );
strncpy( &(buffer->pStorage)[0] , &init_pArray[0] , size);
buffer->number_of_strings = 1;
buffer->total_size = size;
buffer->next_position = size; //size is the next position because array allocates elements from 0 to (size-1)
//Initialize the position tracker which keeps record of starting position for each string
buffer->pPosition = malloc(sizeof(int) * buffer->number_of_strings );
*(buffer->pPosition + (buffer->number_of_strings -1) ) = 0;
return buffer;
}
void dbuffer_destroy(DBUFF *buffer)
{
free(buffer->pStorage);
free(buffer);
}
...
Main:
#include <stdio.h>
#include <stdlib.h>
#include "dbuffer.h"
int main(int argc, char** argv)
{
DBUFF *buff;
buff = dbuffer_init("Bring the action");
dbuffer_add(buff, "Bring the apostles");
printf("BUFFER CONTENTS: ");
dbuffer_print(buff);
dbuffer_destroy(buff);
// Looks like it has been succesfully freed because output is garbage
printf("%s\n", buff->pStorage);
//Why am I still able to access struct contents after the pointer has been freed ?
printf("buff total size: %d\n", buff->total_size);
return (EXIT_SUCCESS);
}
Output:
BUFFER CONTENTS: Bring the action/0Bring the apostles/0
��/�
buff total size: 36
RUN SUCCESSFUL (total time: 94ms)
Question:
Why am I still able to access struct contents using the line below after the pointer to the struct has been freed ?
printf("buff total size: %d\n", buff->total_size);
Once you've called free() on the allocated pointer, attempt to make use of the pointer invokes undefined behavior. You should not be doing that.
To quote C11 standard, chapter §7.22.3.4, free() function
The free() function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. [..]
It never say's anything about a cleanup, which you might be (wrongly) expecting.
Just to add clarity, calling free() does not always actually free up the allocated physical memory. It just enables that pointer (memory space) to be allocated again (returning the same pointer, for example) for successive calls to malloc() and family. After calling free(), that pointer is not supposed to be used from your program anymore but C standard does not guarantee of a cleanup of the allocated memory.
If any attempt is made to read memory that has been freed can crash your program. Or they might not. As far as the language is concerned, its undefined behaviour.
Your compiler won't warn you about it(or stop you from accessing it). But clearly don't do this after calling free -
printf("buff total size: %d\n", buff->total_size);
As a good practice you can set the freed pointer to NULL .
free() call will just mark the memory in heap as available for use. So you still have the pointer pointing to this memory location but it's not available anymore for you. Thus, the next call to malloc() is likely to assign this memory to the new reservation.
To void this situations normally once you free() the memory allocated to a pointer you should set it to NULL. De-referencing NULL is UB also but at least when debugging you can see tha pointer should not be used because it's not pointing to a valid memory address.
[too long for a comment]
To allow your "destructor" to set the pointer passed to NULL modify your code like this:
void dbuffer_destroy(DBUFF ** buffer)
{
if ((NULL == buffer) || (NULL == *buffer))
{
return;
}
free((*buffer)->pPosition);
free((*buffer)->pStorage);
free(*buffer);
*buffer = NULL;
}
and call it like this:
...
dbuffer_destroy(&buff);
...
I tried to extend my ful list of players.
When I use the realloc function, it save my player except the last.
I mean that if I had 4 players in my array and I tried to extend my array to 7 I got a new array in size 7 and just 3 players.
This is part of the function:
void initializeListForTree(Player** players, int listSize)
{
int formulaSize = bla bla bla.....
players = (Player **)realloc(players, sizeof(Player *)*formulaSize);
if (!players)
{
printf("memory allocation failed\n");
}
}
More something like:
void initializeListForTree(Player*** players, int listSize)
{
int formulaSize = bla bla bla.....
void *p = realloc(*players, sizeof(Player *)*formulaSize);
if (!p) {
printf("memory allocation failed\n");
}
else {
*players = p;
}
}
and at the call site
Player **playerslist = NULL;
initializeListForTree(&playerslist, 1);
...
initializeListForTree(&playerslist, 2);
etc..
This, of course, only if your type is a pointer to a list of pointers.
No, you cannot change players itself () from initializeListForTree() function. C uses pass by value for function argument passing. Any changes made to player inside initializeListForTree() function won't be reflected to the actual argument used.
If you have to get that done, you'll need to pass a pointer to players.
If you don't mind having an alternate approach, if choose to return the newly
allocated pointer from initializeListForTree() and collect the same into the actual argument passed, then you'll not be needing to pass a pointer-to-pointer-to-pointer.
That said,
Please do not cast the return value of malloc() and family in C.
Please do not store the return value of realloc() directly into the targeted pointer. In case realloc() fails, it will overwrite the valid entry.
Where realloc() find the extend memory:
If there are enough free space after the original memory, then it will use that, and the original address don't change.
If orignal memory is at end of heap/.. , it extend the memory, so the original address don't change.
If original memory is in middle of heap/.., and there is not enough space, then it will allocate a new memory block, and copy your original memory into the new ones, so the address change.
The last case happens with big chance, so you always need to update your address after call realloc().
And the old address might be put into the free memory list, you should not touch it any more. It might be allocated by another malloc()/.. call in future.
And you can see that, realloc() could be heavy, because it might need to copy memory, so improve code to avoid to use it, if possible.
Here is a possible use case (from TLPI):
// nptr is new pointer, ptr is your original pointer,
nptr = realloc(ptr, newsize);
if (nptr == NULL) {
/* Handle error */
} else {
/* realloc() succeeded */
ptr = nptr;
}
Basicly, first check return value to see whether it succeed:
If failed, then original memory remain unchange.
If succeed, assign new address to original pointer.
And, you need to make sure you don't have another pointer that point to middle of original memory, because that would change too, use offset to start address could make things easier.
If your array size change frequently, then you might allocate a bigger memory, remember its size, and reallocate it only when the size is reached, when resize you also give a bigger value than the new value.
You can choose a factor when resize, e.g factor = 1.5, means: new_size = old_size * factor.
I'm in the process of teaching myself C and I'm mistified as to what's causing the following issue: when I create an array in a method and return it as a pointer to the calling function, none of the content is correct. I've boiled down this problem to the following example:
char * makeArr(void){
char stuff[4];
stuff[0]='a';
stuff[1]='b';
stuff[2]='c';
stuff[3]='d';
printf("location of stuff:%p\n",stuff);
int i;
for(i = 0; i < 4; i++){
printf("%c\n",stuff[i]);
}
return stuff;
}
int main(void){
char* myarr;
myarr = makeArr();
int i;
printf("\n");
printf("location of myarr:%p\n", myarr);
for(i = 0; i < 4; i++){
printf("%c\n",myarr[i]);
}
}
The output returns the following:
location of stuff:0028FF08
a
b
c
d
location of myarr:0028FF08
Ä
ÿ
(
(a null character)
So I've verified that the locations between the two values are the same, however the values differ. I imagine that I'm missing some critical C caveat; I could speculate it's something to do with an array decaying into a pointer or a problem with the variable's scope, but and any light that could be shed on this would be much appreciated.
What you're attempting to do is return the address of a local variable, one that goes out of scope when the function exits, no different to:
char *fn(void) {
char xyzzy = '7';
return &xyzzy;
}
That's because, other than certain limited situations, an array will decay into a pointer to the first element of that array.
While you can technically return that pointer (it's not invalid in and of itself), what you can't do is dereference it afterwards with something like:
char *plugh = fn();
putchar (*plugh);
To do so is undefined behaviour, as per C11 6.5.3.2 Address and indirection operators /4 (my bold):
If an invalid value has been assigned to the pointer, the behaviour of the unary * operator is undefined.
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
Having stated the problem, there are (at least) two ways to fix it.
First, you can create the array outside of the function (expanding its scope), and pass its address into the function to be populated.
void makeArr (char *stuff) {
stuff[0]='a';
stuff[1]='b';
stuff[2]='c';
stuff[3]='d';
}
int main(void) {
char myarr[4];
makeArr (myarr);
// Use myarr here
}
Second, you can dynamically allocate the array inside the function and pass it back. Items created on the heap do not go out of scope when a function exits, but you should both ensure that the allocation succeeded before trying to use it, and that you free the memory when you're finished with it.
char *makeArr (void) {
char *stuff = malloc (4);
if (stuff != NULL) {
stuff[0]='a';
stuff[1]='b';
stuff[2]='c';
stuff[3]='d';
}
return stuff;
}
int main(void) {
char *myarr;
myarr = makeArr();
if (myarr != NULL) {
// Use myarr here
free (myarr);
}
}
stuff[] only exists on the stack during function call, it gets written over after return. If you want it to hold values declare it static and it will do what you want.
However, the whole idea is fundamentally lame, don't do that in real life. If you want a function to initialize arrays, declare an array outside of the function, pass a pointer to this array as a parameter to the function and then initialize an array via that pointer. You may also want to pass the size of the array as a second parameter.
Since you're learning, a sample code is omitted intentionally.
Your array stuff is defined locally to the function makeArr. You should not expect it to survive past the life of that function.
char * makeArr(void){
char stuff[4];
Instead, try this:
char * makeArr(void){
char *stuff=(char*)calloc(4, sizeof(char));
This dynamically creates an array which will survive until you free() it.