So I'm practicing and learning C right now and came across a rather easy challenge from CodeWars that asked to print out a string of "Aa~", "Pa!", and "Aa!" depending on whether n was <= 6 or not. I know how we can do this with arrays but wanted to try out using dynamically allocated char arrays like with malloc for efficiency sake.
I want to make sure I get the basics down with these questions
So I know in other malloc examples with int we set up a pointer (of type int) to point to an allocated memory block. Is it still a pointer when I declare "char *ptr" to point to an allocated memory block because pardon me I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated" type of error when I try to return the answer especially when the "val" is 1 or 0? I read somewhere that changing the pointer(string?), answer, to NULL will solve the issue but not entirely sure why this works.
3 To continue in general with the above question, for freeing up space, what is the best method to do so if we dynamically allocate a memory block in a function but need to return a value from that function? As in, do we free up the space after or before?
Thank you all for your input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype
int main(int argc, const char * argv[]) {
char *answer = sc(val);
printf("The answer is %s\n", answer);
answer = NULL; // why does this work
free(answer);
return 0;
}
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
char *ptr = (char*) malloc(n*4);
if (ptr == NULL){
printf("malloc failed");
}
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
ptr = "";
}
else {
printf("Error!");
exit(0);
}
return ptr;
}
The main issue you have with your code is that you're not zeroing out the buffer you get from malloc(), so the first strcat() is not necessarily going to write at the start of the string but at the end.
You can fix that with a strcpy() right after the malloc() call and check:
strcpy(ptr, "");
Or, equivalently, you can just set the first byte of the buffer to zero. Since C strings are zero-terminated strings, setting a character to zero will indicate it's at the end:
ptr[0] = 0;
You also seem to be allocating your buffer too short. If you write n-1 copies of Aa~ (4 bytes) plus one copy of Pa! Aa! (8 bytes when you include the terminating zero!) you'll actually need 4 * (n+1) as space. So either always allocate that or do so in case n < 6 which is where you need the additional bytes.
This is also a problem:
ptr = "";
Because now your ptr is no longer pointing to a buffer returned by malloc(), but to a static (empty) string in your binary. It's quite probable that this is the place where you're getting the trouble from free(), since calling it on a static string in your binary is definitely wrong.
Furthermore, after you set ptr = "" you no longer have any reference to the buffer you allocated, which means you most likely just created a memory leak!
In that case, you should simply use strcpy() or set the first byte to zero. But if you do that at the start of the program, you don't need to do it here.
Finally, free(NULL); works (as in, doesn't throw an error) because that's part of its specification, you can pass it a NULL pointer and it will do nothing. But note that it's not freeing the buffer you allocated, so you have a memory leak here too.
I'd further refactor the second part of your code so you don't have too much repetition appending the strings:
char *sc(int n) {
/* if n <= 6 then will have an extra "Aa!"
* after "Pa!" at the nth position.
*/
char *ptr;
if (n < 0) {
printf("Error!");
return NULL;
}
ptr = (char*) malloc(4 * (n+1));
if (ptr == NULL){
printf("malloc failed");
return NULL;
}
strcpy(ptr, "");
if (n > 1) {
for (int i = 1; i < n; i++){
strcat(ptr, "Aa~ ");
}
strcat(ptr, "Pa!");
if (n <= 6) {
strcat(ptr, " Ah!");
}
}
return ptr;
}
Also note you don't need to assign the result of strcat() back to ptr every time, since it always returns its first argument anyways, so assigning it there isn't changing anything really.
I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
You're allocating memory for a string, so it just needs to be char *. char ** would be used for an array of multiple strings, or for a pointer to a variable that contains a pointer to a string.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated"
You get that when you do ptr = "";. After you do this, ptr no longer points to the memory that was allocated with malloc, it points to that string literal. If you want to set the allocated memory to an empty string, you can do
ptr[0] = '\0';
This puts a null terminator in the first element of the string.
You also need to do that before the code that uses strcat() to append to the string. Otherwise you're appending to uninitialized data. Simplest would be to do it immediately after allocating the memory (then you don't need it in the n <= 1 block.
The else block is not needed. There are no other possibilities than the 3 you test for unless the CPU is malfunctioning (in which case all bets are off). However, you should check for n < 1 before calling malloc(), as you can't allocate negative memory, and malloc(0) may return NULL.
When you allocate space for ptr, you need to add 1 byte for the terminating null of the string.
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
if (n >= 1) {
char *ptr = malloc(n*4 + 1);
} else {
char *ptr = malloc(1);
}
if (ptr == NULL){
printf("malloc failed");
exit(1);
}
ptr[0] = '\0'; // initialize empty string
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
// nothing to do
}
return ptr;
}
Related
I have a 2d pointer array:
char **fields = calloc(1, sizeof(char *));
I add to it different strings, like this:
if(i > 0) fields = realloc(fields, (i+1) * sizeof(char *));
fields[i] = calloc(size, sizeof(char));
I then use memcpy into the fields[i] the desired string.
At the end of the program, when I try to free fields, I do it like this:
int j=0
while(fields != NULL && fields[j]){
free(fields[j]);
j++;
}
free(fields);
The program inserts 4 strings into fields.
The first string frees as expected, however on the second iteration of the loop (j=1) the program stops and outputs the error: free(): invalid pointer
EDIT: I made a short program with the same problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
char **fields = calloc(1, sizeof(char *));
int fieldsIndex = 0,i=0;
while (i<4) {
if(fieldsIndex > 0){
fields = realloc(fields, (fieldsIndex + 1) * sizeof(char *));
fields[fieldsIndex] =NULL;
printf("amount of field places: %d\n", (fieldsIndex + 1));
}
fields[fieldsIndex] = calloc(8, sizeof(char));
fields[fieldsIndex] = "88888888";
fieldsIndex++;
i++;
}
int j=0;
for(j=0; j<i; j++){
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
return 0;
}
Can anyone help?
Addressing mainly the MRE.
The main problems are around this line:
fields[fieldsIndex] = "88888888";
It's not right for two reasons:
Firstly you need one more element in the array for the null byte.
Secondly, you make the fields[fieldsIndex] pointers point to string literals, it not only causes a memory leak, but also those string literals are usually stored in a readonly section of memory, either way the behavior freeing a pointer pointing to a string literal is undefined.
You need to copy the strings to the memory you just allocated. Using memcpy should work as long as you reserve enough memory as mentioned in the previous point, a cleaner way would be to use strdup.
Another issue is if(fieldsIndex > 0) because then fields[0] will not have allocated memory.
Some other notes, if you know the amount of strings (i < 4) you shouldn't need to realloc, just allocate space for all the pointers in the first calloc* (assuming that is not brought about by the construction of the MRE) , also i and fieldsIndex seem to be redundant.
Here is a demo keeping realloc (as it's tangential to the OP):
int main()
{
char **fields = NULL;
char **tempfields; // I advise the use of an auxiliary pointer for reallocation
int fieldsIndex = 0;
while (fieldsIndex < 4)
{
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields); //*
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
printf("amount of field places: %d\n", (fieldsIndex + 1));
fields[fieldsIndex] = strdup("88888888");
// Or
// fields[fieldsIndex] = calloc(9, sizeof **fields); // check return
// strcpy(fields[fieldsIndex], "88888888");
fieldsIndex++;
}
// With int iterator
int j = 0;
for (j = 0; j < fieldsIndex; j++)
{
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
}
Or with a sentinel element in fields:
Live demo
// With sentinel
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields);
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
fields[fieldsIndex] = NULL;
while (*fields)
{
printf("field: %s\n", *fields);
free(*fields);
fields++;
}
free(tempfields);
How can I free an array of pointers where each pointer points to an address of a string which I allocated inside of a function?
I created a simple array of pointers, *pointers[], inside my main and I'm passing it to readline which returns the amount of lines I have read.
The purpose of this is to store all the lines of the input separated by '\n'. Each line is stored in a pointer in the array of pointers.
So after readline is called and it returns, I can printf("%s", pointers[0]) which will show the first line the user typed in.
The way I assign a line to each pointer in my array is inside readline by allocating a char pointer, *p, to have MAXLENGTH sizes and passing the current address of p to the respective pointer. After each assignment is done, I jump to the next free address of p.
Finally, my question is if I have to free (and how) my array of pointers after the readline routine is complete and I have printed all the lines stored.
I'll leave the two functions, main and readline, here for you.
#define NUMOFLINES 5
#define MAXLENGTH 1000
void main(void) {
char *pointers[NUMOFLINES]; // Array of pointers to string
int nlines; // Number of pointers read
nlines = readlines(pointers);
writelines(pointers, nlines);
/* FAILED ATTEMPT TO FREE THE ARRAY OF POINTERS */
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
}
int readlines(char *pointers[]) {
char line[MAXLENGTH];
char *p;
int iptr, len;
/* ALLOCATE P */
p = malloc(sizeof(char) * MAXLENGTH);
for(iptr = 0 ; iptr < NUMOFLINES && (len = get_line(line)) > 0 ; iptr++)
{
strcpy(p, line); // copy the line to the array, ending in len-th position
pointers[iptr] = p; // Pass current adress 'p' to the respectively index of pointers
p += len+1; // Next free adress in array p
}
return iptr;
}
As you can see I tried to free by going through each pointer and calling free(pointer[i], but all I get is
malloc(): corrupted top size
Aborted (core dumped)
Do I need to call free?
Thanks in advance.
You are calling malloc() only 1 time, but you are calling free() multiple times. Every char* in your array points within that single malloc()'ed memory block. You can only call free() 1 time on the starting address of that block. Calling free() on addresses inside the block is undefined behavior.
Also, the memory block you are allocating with malloc() is not nearly large enough, assuming each string that get_line() outputs can be up to MAXLENGTH characters. You are only allocating enough memory for the maximum length of 1 single line, so as soon as your reading loop has copied MAXLENGTH characters into the allocated block, you are advancing your p pointer beyond the bounds of the block, causing subsequent strcpy()'s to write into and corrupt random memory.
If you want to stay with a single malloc() and sub-divide it, try something more like this instead:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES], *buffer;
int nlines = readlines(pointers, &buffer);
if (nlines < 0) return -1;
writelines(pointers, nlines);
free(buffer);
return 0;
}
int readlines(char *pointers[], char **buffer) {
*buffer = malloc(sizeof(char) * (NUMOFLINES * MAXLENGTH));
if (*buffer == NULL) return -1;
int iptr, len;
char *p = *buffer;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(p)) > 0); ++iptr)
{
pointers[iptr] = p;
p += len + 1;
}
return iptr;
}
Otherwise, I suggest you allocate a separate buffer for each pointer in the array, eg:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES];
int nlines = readlines(pointers);
if (nlines < 0) return -1;
writelines(pointers, nlines);
for(int i = 0; i < nlines; ++i)
free(pointers[i]);
return 0;
}
int readlines(char *pointers[]) {
char line[MAXLENGTH], *p;
int iptr, len;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(line)) > 0); ++iptr)
{
p = malloc(sizeof(char) * (len + 1));
if (p == NULL) {
for(int j = 0; j < iptr; ++j) free(pointers[j]);
return -1;
}
memcpy(p, line, sizeof(char) * len);
p[len] = '\0';
pointers[iptr] = p;
}
return iptr;
}
You only call malloc once, so you should only call free once.
After readlines returns, pointers[0] points to the start of the memory you allocated, while the remaining array members point someplace within that same block of memory. That means there's only one pointer to free.
So instead of this:
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
Just do this:
free(pointers[0]);
To reiterate, you should only pass to free a pointer value that was returned from malloc (or realloc or calloc).
You don't need to free the other pointers you maintain within your buffer. If you do a single
p = malloc(sizeof(char) * MAXLENGTH);
then you need a single
free(p);
The malloc and free have to match exactly 1-to-1 and be called with the same addresses.
Since you want to modify p, you should do:
char* original = malloc(sizeof(char) * MAXLENGTH);
p = original;
and, once done:
free(original)
My program takes an arbitrary number of words at runtime and stores them in a dynamically-sized array of words.
Currently, my program runs well, except when I use free() to free up the memory of the temporary double pointer temp. I am not quite sure why it does this, as I thought it would cause errors if I didn't use it.
int wordSize = 10, arrSize = 1, i = 0;
char **stringArr, **temp;
char *input;
stringArr = malloc(arrSize * sizeof(char *));
puts("Accepting input...");
for (;;) {
if (i >= arrSize) {
arrSize += 1;
temp = realloc(stringArr, arrSize * sizeof(char *));
if (temp != NULL) {
stringArr = temp;
free(temp); // This is the line that is giving me issues; removing it works
} else {
puts("Could not allocate more memory");
return 0;
}
}
stringArr[i] = malloc(sizeof(input));
input = malloc(wordSize * sizeof(char));
scanf("%10s", input);
if (strcmp(input, "END")) {
strcpy(stringArr[i], input);
i++;
} else
break;
}
free(stringArr);
At the bottom of my program I use free() without any issues. How come it works OK here but not earlier on in the program.
I feel I am missing something about how free() works.
Note: this is my first program implementing malloc() and realloc(), so I am only just getting used to how they work. If you know of a better way to accomplish what I am doing that, please feel free to describe.
The free(temp); line is causing an error (later on) because, in the preceding line, stringArr = temp;, you are assigning the address that is stored in the temp pointer to that in the stringArr pointer. Thus, when you free the memory pointed to by temp you also free the memory pointed to by stringArr, because it is the same memory block. Copying a pointer's value from one variable to another does not make a (separate) copy of the memory.
Omitting the free(temp); line is correct, because that memory is freed later on, in the free(stringArr); call.
You must not free the reallocated array when reallocation was successful. If you do that, the code will modify this freed block, which has undefined behavior and you will have further undefined behavior when you later try and reallocate or free this block.
Note also the following:
pre-allocating stringArr with a size of 1 is not necessary. Just initialize stringArr to 0 and arrSize to 0. realloc() can take a null pointer and will behave like malloc().
stringArr[i] = malloc(sizeof(input)); is incorrect: it will allocate a char array with a size of 4 or 8 depending on the size of a pointer on the target architecture, not 11 bytes as it should.
if the wordSize is the maximum length of a word, you should allocate one more byte for the null terminator. The 10 in %10s must match the value of wordSize, which is cumbersome because there is no easy way to pass this to scanf() as a variable.
you do not check the return value of scanf(), causing undefined behavior in case of premature end of file.
you have memory leaks: input is allocated for each iteration but never freed, freeing stringArr without freeing the strings pointed to by its elements makes them inaccessible.
It would be more efficient to use a local array to try and read the words with scanf() and only allocate the string and reallocate the array if successful.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int arrSize = 0;
char **stringArr = NULL;
char input[11];
puts("Accepting input...");
while (scanf("%10s", input) == 1 && strcmp(input, "END") != 0) {
char **temp = realloc(stringArr, (arrSize + 1) * sizeof(*stringArr));
if (temp != NULL) {
stringArr = temp;
} else {
puts("Could not allocate more memory");
break;
}
stringArr[arrSize] = strdup(input);
if (stringArr[arrSize] == NULL) {
puts("Could not allocate more memory");
break;
}
arrSize++;
}
puts("Array contents:");
for (int i = 0; i < arrSize; i++) {
printf("%i: %s\n", i, stringArr[i]);
}
for (int i = 0; i < arrSize; i++) {
free(stringArr[i]);
}
free(stringArr);
return 0;
}
I am attempting to set the last element in a second char ** array to NULL after I encounter a specific char in the first array.
int test(char ** args){
char ** chmd1;
for(int i = 0; args[i] != NULL; ++i){
if(!strncmp(args[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = args[i];
}
for(int i = 0; chmd1[i] != NULL; ++i){
printf("%s", chmd1[i]);
}
return 0;
}
This code segfaults as the second for loop goes on for more iterations past where the NULL should be.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
This code segfaults as the second for loop goes on for more iterations past where the the NULL should be.
You have not allocated memory for chmd1 and yet you are using it like it points to valid memory.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
You can't do that. You have use malloc (or one of the other functions from the malloc group of functions: calloc, realloc) to allocate memory for chmd1 before you can use it.
allocate memory for pointer
char ** chmd1;
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
Without allocating memory to chmd1, it will not be possible.
You have to allocate memory for char ** chmd1; before assigning value NULL (or copy elements from args) to any element.
It can be something like
char ** chmd1 = malloc(NUMBER * sizeof(char*));
or even
char * chmd1[NUMBER];
To determine NUMBER value find the NULL in the args first.
EDIT:
Also you can use realloc in your loop as:
char **chmd1 = NULL;
int i;
for(i = 0; argv[i] != NULL; ++i){
chmd1 = (char**)realloc(chmd1, i * sizeof(char*) );
if(!strncmp(argv[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = argv[i];
}
// then use i as size of chmd1
for(int cnt = 0; cnt < i; cnt++)
{
if( chmd1[i] == NULL ) ; // do something
}
chmd1[i] = args[i];
chmd[i] is a pointer in 2D space and you are not allocating memory for the pointer.
Okay, imagine I have a char**, would this be the correct way to allocate memory?
I mean: allocate memory for the char** itself and then for each char*...
char** fraseUsuario = NULL;
int length = 100, i = 0;
fraseUsuario = (char **) malloc(sizeof (char*)); //Not pretty sure
for (i = 0; i < 3; i++) {
fraseUsuario[i] = (char *) malloc(length * sizeof (char));
if (fraseUsuario[i] == NULL) {
printf("error\n");
return -1;
}
gets(fraseUsuario[i]);
}
for (i = 0; i < 3; i++) {
printf("%s\n", fraseUsuario[i]);
free(fraseUsuario[i]);
}
And btw, how exactly does free() work? I mean, when I call it at the end, with the debugger it seems as if it does "nothing", if "Hello" is stored in the array, it will continue to be stored there after the free call... is that the normal behavior?
What do you mean allocate memory for the char ** itself? You allocate memory for a variable on the stack when you define it. The following statement defines (allocates memory) fraserUsuario and initializes it to NULL.
char **fraseUsuario = NULL;
I think what you probably meant is how to dynamically allocate an array of char **, i.e., pointer to a pointer to a character. Then you again dynamically allocate an array for each element of the previous allocated array. Do not use gets. It's deprecated and unsafe to use. Use fgets instead. Also, please don't cast the result of malloc. You don't get any benefit and you can run into error if you forget to include the header stdlib.h which contains its prototype. Here's how you do it.
char **fraseUsuario = NULL;
int max_string_len = 100 + 1; // maximum string length. +1 for null byte
int num_string = 3; // number of strings to read
int i, j;
fraseUsuario = malloc(num_string * sizeof *fraseUsuario);
if(fraseUsuario == NULL) { // check for NULL
// handle the case
printf("not enough memory\n");
return -1;
}
for(i = 0; i < num_string; i++) {
fraseUsuario[i] = malloc(max_string_len * sizeof(char));
if(fraseUsuario[i] == NULL) { // check for NULL
printf("not enough memory\n");
for(j = 0; j < i; j++)
free(fraseUsuario[j]); // free memory before returning
free(fraseUsuario); // free memory before returning
return -1;
}
if(fgets(fraserUsuario[i], max_string_len, stdin) == NULL) {
// reading string failed
*fraserUsuario[i] = '\0'; // empty string
}
}
for(i = 0; i < 3; i++) {
printf("%s\n", fraseUsuario[i]);
free(fraseUsuario[i]); // free memory allocated for strings
}
free(fraseUsuario); // free memory allocated for pointers to strings
fraseUsuario = NULL;
When you call free on a memory address which you got by a call to malloc, the memory block is returned to the free pool on the heap. This memory block can then later be reused by malloc. Once you free memory, you have given up your ownership of it. It no longer belongs to you and attempting to use it is illegal and will result in undefined behaviour and likely segfault.
You only allocate memory for one char* but use three.
To fix this do:
#define STR_MAXIMUM (3)
...
size_t length = 100, i = 0; /* No need to use a signed type.
size_t is meant as index and size type. */
char ** fraseUsuario = malloc(STR_MAXIMUM * sizeof(*fraseUsuario));
for (i = 0; i < STR_MAXIMUM; ++i)
{
fraseUsuario[i] = malloc(length * sizeof(*fraseUsuario));
...
Also add error checking to system calls.
Also^2: Do not use gets() as there is no way for the compiler or the machine to prevent the buffer passed in from overflowing. Use fgets() instead.
fgets(fraseUsuario[i], length, stdin);