Setting a specific element of a char** array to NULL - c

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.

Related

Why does free() cause an error in my code when it's there, but everything runs well when it is not there?

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;
}

How to improve the code for dynamically allocated strings in C?

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;
}

How to properly free char** in C

I have really been confused about this 2D char array
char **arg = malloc(sizeof(char*) * argc)
for (int i = 0; i < argc; i++)
arg[i] = malloc(sizeof(char) * size)
...
...
Now suppose after a series of operations, I forget the variable argc, how can I free those memory?
Can I do something like this? Is this absolutely correct under all circumstances?
char **tmp = arg;
while (*tmp != NULL){
free(*tmp);
tmp++;
}
free(arg);
No
while(*tmp != NULL){
you may reach above a point where you will dereference memory which hasn't been assigned to and trigger undefined behaviour.
Or as suggested you can explicitly assign a NULL to the last allocated pointer, and in that case it will work.
As others have said, the problems with freeing in a loop as shown is that an extra item (argc + 1) needs to be allocated and it has to be set to NULL. An alternative technique is to first allocate the space for the pointers as you have done
char **arg = malloc(sizeof(char*) * argc)
Then, if you know all the subsequent items are the same size, allocate it in one huge block and set the rest of the elements at spaced offsets
arg[0] = malloc(sizeof(char) * size * argc);
for (int i = 1; i < argc; ++i)
arg[i] = arg[i - 1] + size;
Freeing the space is a doddle: no need to even remember argc
free(arg[0]); /* this will free the memory used by all the elements */
free(arg);
The big disadvantages to this technique is that if any of the array elements overrun, it will corrupt the next item. This cannot be detected so easily with a heap check unless it is the last item.
if you define your char * array like this:
char **arg = calloc( 1, sizeof( char * ) * argc );
you can be sure that every undefined pointer will be equal to NULL
and then you can use the while loop almost like you suggested:
char *tmp = *arg;
while ( tmp != NULL ) {
free( tmp );
tmp++;
}
free(arg);

How can I make a copy of char** in C?

I am having trouble understanding the concept of pointers in C. I have an array of pointers to character strings,
char ** args;
and I want to copy args to another array of pointers to character strings.
char ** args2;
I tried doing,
args2 = args;
but when I manipulate args2, I also manipulate args. I know this is because of the fact that the arrays are pointers. Also, if I run a for loop,
// by the time I get to the for loop
// args holds {"string1", "string2", "string3"}
// args2 = NULL;
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
I get a segmentation fault error.
Thanks in advance.
You need to allocate space for args2 first! Even when you do that, don't forget that you'll still just be copying pointers to the original strings, so if you modify the strings in args2, the original strings are the ones being being modified.
You probably want something like:
i = 0;
while (args[i] != NULL)
i++;
args2 = malloc(i * sizeof *args2);
for(j = 0; j < i; j++)
{
args2[i] = strdup(args[i]);
}
Which both allocates memory for the pointers in arg2 and also duplicates all of the original strings. Add one to i before the second loop and set args2[i] to NULL after the second loop if you want to have a NULL terminated array like you started with.
Imagine char** as a pointer of pointers. Each element of it is another pointer, which points to a string (basically you have an array of strings).
Assuming you have a char** args2. If you do:
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
You copy the value of each pointer from args to args2. It is the same thing as saying:
char *a = "abc";
char *b; // allocate memory
b = a;
Or, in order to copy strings you need to copy their content, not just the pointer to the first element. So, you need to use something like strcpy. Note that in order for this to work, you also need to allocate memory for args2, which I omit here.
for(i = 0; args[i] != NULL; i++){
strcpy(args2[i], args[i]);
}
Also, you might want to add the NULL terminator of args:
args2[i] = NULL;
In order to allocate memory, you need to know the size of the args array. Assume args has n element. Then, the memory allocation for args2 could be done as following (I also add the copying part):
// Allocate memory for n pointers to char
char **args2 = malloc(sizeof(char*) * n);
for(i = 0; args[i] != NULL; i++){
// For each pointer of args 2, allocate memory to store the
// correspondent string of args, including the NUL.
args2 = malloc(strlen(args[i] + 1));
strcpy(args2[i], args[i]);
}
You might want to consider using a static char matrix at the beginning that handles memory allocation, in order to facilitate learning.

is this a nice way to allocate memory for an array of arrays? (C)

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);

Resources