1

I'm a beginner trying to learn dynamic memory allocation in c.

I'm trying to return NULL if text doesn't have anything in it, if it has something in it I want to return the text.

char* check_for_NULL(const char *text){
    if(text == NULL){
        return NULL;
    }
...
}

(the rest of the program works as intended)

If I put NULL where I call it:

int main(){
char *check_NULL;

    check_NULL = check_for_NULL(NULL);
    printf("%s", check_NULL);
    free(check_NULL);

}

I get segmentation fault instead of null.

when I run in valgrind I get the result:

Invalid read of size 1

Address 0x0 is not stack'd malloc'd or (recently) free'd

tried to make space for NULL with calloc, tried to put 0 instead of NULL but nothing works

saw a similar post:

Valgrind: Invalid read of size 1

but didn't get what the fix was.

asdfg
  • 23
  • 5
  • `check_NULL = char* check_for_NULL(NULL);` isn't valod. You probably wanted `check_NULL = check_for_NULL(NULL);` But if youi do that you have to check the return for NULL before you do `printf("%s", check_NULL); free(check_NULL);` – Jerry Jeremiah Mar 02 '23 at 22:24
  • 3
    @asdfg It is obvious that this statement printf("%s", check_NULL); invokes undefined behavior when the pointer equal to NULL. So what is the problem? – Vlad from Moscow Mar 02 '23 at 22:25
  • Yes, sorry about that, it was a typo, couldn't copy it from virtual machine – asdfg Mar 02 '23 at 22:26
  • There's no point in making a function that tests if a pointer is NULL. You said "if text doesn't have anything in it" - are you trying to test it is an empty string? `text[0] == '\0'` – pmacfarlane Mar 02 '23 at 22:31
  • You're also calling `free()` on an automatic variable. You don't need to explicitly free those, only things allocated via `malloc()`, `calloc()` and `realloc()`. – pmacfarlane Mar 02 '23 at 22:33
  • @VladfromMoscow shouldn't it print out (null) ? – asdfg Mar 02 '23 at 22:34
  • 2
    @asdfg No, it should not. It does not output a pointer. It trues to output the string pointed to by the pointer accessing the memory the address of which is stored as a value in the pointer. – Vlad from Moscow Mar 02 '23 at 22:37
  • @pmacfarlane the whole program is longer but error only occurs here. this part just tests for NULL (and empty string). – asdfg Mar 02 '23 at 22:40
  • @asdfg: As Vlad said, no — `printf()` should not print `(null)` when given a null pointer to print as a string. There are implementations that do that, but passing a null pointer to be formatted by `%s` is undefined behaviour. Personally, I think printing `(null)` is the wrong behaviour — the library should call `abort()`. But it is fairly widespread — and since the standard regards it as undefined behaviour, it is legitimate to print `(null)`, even though I regard it is ill-advised. – Jonathan Leffler Mar 02 '23 at 23:11

4 Answers4

2

The other answers here are good. Essentially, you can't dereference NULL. I want to also give some intuition for this.

Recall that in C, strings are just sequences of bytes (representing ASCII characters) in memory. We can treat any place in memory (which itself is just one long sequence of bytes) as a string, and we will count the string to be terminated once we hit a byte with value 0 (the character '\0').

To drive this point home, let's look at a sample strlen implementation:

int strlen(char *str) {
    int len = -1;
    // will evaluate to 0 (false is just 0 in C) when we reach a null terminator.
    while (str[++len]);

    return len;
}

We start at the address referenced to by str and keep iterating through memory until we find a 0 value, incrementing the length each time.

Remember NULL is #define'd to be 0, so when you choose to pass it as a pointer, it references address 0x0.

First off, your function check_for_NULL is essentially saying: take a pointer text. If its address is NULL, return NULL. Otherwise return its address. See how this is extraneous? The function just takes a pointer and spits the same pointer back out.

Now you decide to pass NULL to check_for_NULL and print the resultant string. %s wants to treat its argument as a string, in other words as a char *, and is going to start dereferencing values from that pointer until it reads a byte with value 0 ("null terminator", char '\0'). The issue here is this means that the first value printf dereferences is at address 0x0, which we're not allowed to dereference.

Understand that address 0x0 is not a valid address for us to use. That's the reason we use NULL pointers to represent errors, invalid values, etc. It's the ideal junk value because it will never not be junk.

A couple of side points:

Compile your programs with the -g flag (for gcc, assuming that's the compiler you're using) to add debugging information. If you do this, valgrind will point at the exact line in your code line where the issue is, which makes debugging much easier.

Don't free() check_NULL. You use free() on heap pointers, not stack pointers. If you don't understand that yet, just know that unless a pointer was provided to you by malloc() (or any other alloc function), it doesn't need to be freed.

Fun little side point: arr[i] is always equivalent to *(arr + i). My Advanced Programming professor called this "the grand unifying theorem of pointers".

1

If the pointer check_NULL is equal to NULL then this statement

printf("%s", check_NULL);

invokes undefined behavior.

At least you should write

if ( check_NULL != NULL ) printf("%s", check_NULL);

Pay attention to that to make this statement correct

free(check_NULL)

the function should return either a null pointer or a pointer to a dynamically allocated memory.

Pay attention to that the phrase

text doesn't have anything in it,

means that an empty string is passed to the function. To check whether a string is empty you should write for example

if ( text[0] == '\0' )
//...

or

if ( !*text )
//...
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Another solution to replace your printf():

printf( "%s", check_NULL ? check_NULL : "(null)" );
Fe2O3
  • 6,077
  • 2
  • 4
  • 20
0

It seems you want something like this:

char* check_for_NULL(const char *text){
    if(text == NULL){
        return "(null)";
    }
...
}

C isn't like Ruby or Python or whatever. It won't convert a NULL pointer into a string by magic.

You're also calling free() on a pointer that was not dynamically allocated. You must not explicitly free those; you may only free things allocated via malloc(), calloc() and realloc(). String literals like "(null)" live forever and can't be freed.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
pmacfarlane
  • 3,057
  • 1
  • 7
  • 24