In some programming languages [...] it is possible to pass a NULL parameter as an argument, but in C I always thought this would result in Undefined Behavior.

Passing a NULL parameter for a pointer by itself does not result in UB; it's attempting to access the memory pointed to by a pointer set to NULL that does.

Passing NULL is a very common practice for situations when something is not specified. The caller is expected to check parameters for NULL before performing the access. For example, the standard lets you pass NULL to free, which makes the function a lot more convenient.

don't NULL pointers simply point to nothing?

Yes, they do. But that "nothing" is globally well-known, so using a NULL lets you communicate the fact that a pointer points to nothing to functions that you call. In other words, the check

if (myPointer == NULL)

is well-defined*, so you can use it to your advantage.

* Unless you use a dangling pointer, i.e. a pointer that you have freed, or a pointer that points to object that went out of scope. You can prevent the first situation from happening by assigning NULL to every pointer that you free(), and the second situation by declaring pointers in the scope that has the same or higher level of nesting as the scope of an automatic object to which the pointer is pointing.

Answer from Sergey Kalinichenko on Stack Overflow
Top answer
1 of 4
9

In some programming languages [...] it is possible to pass a NULL parameter as an argument, but in C I always thought this would result in Undefined Behavior.

Passing a NULL parameter for a pointer by itself does not result in UB; it's attempting to access the memory pointed to by a pointer set to NULL that does.

Passing NULL is a very common practice for situations when something is not specified. The caller is expected to check parameters for NULL before performing the access. For example, the standard lets you pass NULL to free, which makes the function a lot more convenient.

don't NULL pointers simply point to nothing?

Yes, they do. But that "nothing" is globally well-known, so using a NULL lets you communicate the fact that a pointer points to nothing to functions that you call. In other words, the check

if (myPointer == NULL)

is well-defined*, so you can use it to your advantage.

* Unless you use a dangling pointer, i.e. a pointer that you have freed, or a pointer that points to object that went out of scope. You can prevent the first situation from happening by assigning NULL to every pointer that you free(), and the second situation by declaring pointers in the scope that has the same or higher level of nesting as the scope of an automatic object to which the pointer is pointing.

2 of 4
4
void func_with_optional_arg(char *optional)
{
    if (optional == NULL) {
        // do something differently
    }

    /* ... */
}

Why would that invoke UB? Dereferencing a NULL pointer certainly would, but passing one around does not. NULL is a sentinel value used to determine whether or not a pointer is valid (not that invalid pointers cannot have other values, but we use this one explicitly.) If passing it to a function invoked UB then what would be the point of its existence in the first place?

whereas passing a NULL non-pointer value is not?

There is no such thing as a "NULL non-pointer" in C, so I'm not sure what you mean here.

Discussions

c - Clarification of NULL assignment to char * - Stack Overflow
The C Standard allows NULL to be either an integer constant or a pointer constant. While passing NULL as an argument to a function with a fixed number of arguments will cause NULL to be cast to the appropriate pointer type, when it is passed as a variadic argument, this will not happen if ... More on stackoverflow.com
🌐 stackoverflow.com
April 12, 2022
How to pass a NULL pointer as a function argument?
Hi, let's say we have a function like this one (fun.C): #include void fun(const char* name){ if(name) std::cout << "name=" << name << std::endl; else std::cout << "name is NULL" << std::endl; } … More on root-forum.cern.ch
🌐 root-forum.cern.ch
7
0
August 10, 2010
c - Passing Null Pointer in Function - Stack Overflow
That's fine as far as it goes, but the pointer to the allocated memory is not conveyed back to the caller. This is because all C functions pass arguments by value, and in particular, your main() passes the first argument of set() by value (a value of type int *). More on stackoverflow.com
🌐 stackoverflow.com
c - How to pass null parameters to a function - Stack Overflow
This way you don't have to pass fixed number of parameters; if you only want to display one value, you'd call it as ... The first argument n is fixed and must always be present. The remaining arguments are read based on the value of the first argument. If you pass a 1, displayNum will only ... More on stackoverflow.com
🌐 stackoverflow.com
October 21, 2014
Top answer
1 of 1
2

The referenced article is wrong and should be disregarded.

  1. Assuming that NULL was 32-bit int 0 on a system, wouldn't compiler do an implicit cast of 32-bit - int to 64-bit 0 when it encounters char *string = NULL.

An assignment automatically converts the right operand to the type of the left operand. So char *string = NULL will convert the NULL value to char *, not to “64-bit 0”.

If not, then are we saying that each expression like char *string = NULL is non-portable and must be always replaced with an explicit cast like char *string = (char *)NULL for portable C?

No, char *string = NULL is portable C code; it is strictly conforming.

  1. If NULL was 32-bit int 0, and char *string was 64-bit then why would printf run out of bits to print like it is suggested in the blue highlight. Shouldn't printf get full 64 bits as it was passed string and not NULL.

The code referenced, char* string = NULL; followed by printf("%s %d\n", string, 1);, does not pass NULL to printf. It passes string to printf, and the prior assignment converts NULL to char *. So printf is passed a char * that has the value of a null pointer. This will not cause any problem in interpreting the variable arguments to printf. (It is, however, improper to pass a null pointer for the %s conversion.)

If the call were instead printf("%s", NULL);, then there is a problem. Arguments corresponding to the ... part of a variable-argument function are not automatically converted to a parameter type. They are processed by the default argument promotions, which largely promote narrow integer types to int and promote float to double, but they will not convert an int to any type of pointer. Thus, if NULL is defined as 0, then printf("%s", NULL); passes an int where a char * is expected, and this may cause various misinterpretations of the arguments.

In consequence, never use the NULL macro as a direct argument to a function with a variable argument list. Using a pointer variable that has been assigned from NULL is okay.

🌐
Cprogramming
cboard.cprogramming.com › cplusplus-programming › 101278-how-pass-null-parameter-function.html
How pass NULL for a parameter in a function?
April 2, 2008 - void doSmth(std::string str) { //This causes the error only if NULL was actually passed if ( &str == NULL ) printf( "null" ); } int main(...) { //This one works fine doSmth( "string" ); //This cause it to crash doSmth( NULL ); //This will also cause a crash std::string tmp(NULL); doSmth( tmp ); }
🌐
CERN
root-forum.cern.ch › t › how-to-pass-a-null-pointer-as-a-function-argument › 10744
How to pass a NULL pointer as a function argument? - ROOT - ROOT Forum
August 10, 2010 - Hi, let's say we have a function like this one (fun.C): #include <iostream> void fun(const char* name){ if(name) std::cout << "name=" << name << std::endl; else std::cout << "name is NULL" << std::endl; } …
Top answer
1 of 5
7

I'm going to guess what you really mean with your question and try to give a decent answer.

I guess you want to change num_results to be equal to num by passing it to the function set as a pointer, and I can see a few mistakes you've made:

  1. You probably have this in your full code, but don't forget to #include <stdio.h> to use printf() and to #include <stdlib.h> to use malloc()
  2. Your function set() should be declared before main(), you can do this by declaring a prototype before main() or simply defining the set() function before main().

Now let's go to your solution: you want to pass the num_results as a parameter to a function, allocate some memory and assign the address to num_results and then update the value to the one in num.

When you pass an int * as a parameter, I guess you already know that by passing int you are simply giving a copy of what is inside an int variable. This works the same way with an int *, you are not passing a reference to num_results so that you can update the address of the pointer, you are passing a copy of the current address, NULL, which will not be modified. What could be modified is what is inside of the current address, but the current address is NULL, so not can really be modified.

Since you want to allocate memory and assign its address to num_results, you must pass a pointer to a pointer, so you are passing the address of where your int* variable is being kept and where you can actually change it.

This way, your function should look like void set(int ** results, int num) and you should call it with set(&num_results, num), so you are passing a reference to num_results, where you can change the address it points to, currently NULL and then the address returned by malloc().

You'd also have to change the body of your function, because you're now using an int **, you want to assign the new address to *results, because results == &num_results, and assign num to **results, because *results = num_results.

I hope my explanation is not very confusing and hopefully someone can explain it better with another answer or by editing mine. Final code:

#include <stdio.h>
#include <stdlib.h>

void set(int ** results, int num){
    *results = malloc(sizeof(int));
    **results = num;
}

int main(void) {
    int * num_results = NULL;
    int num = 4;
    set(&num_results, num);
    printf("%d\n", *num_results);
    return 0;
}
2 of 5
1
set(num_results, num);

Here NULL is getting passed to results variable in set(), when you allocate memory to results, NULL is getting replaced by valid memory address, but you need to understand it will be held in results variable only as its a local variable to set()

You should either pass address of num_results to set() so that memory allocated in set() is retained in main(), or just allocate memory to num_results in main function then pass it to set() as done below:

#include <stdio.h>
int main() {
    int *num_results = NULL;
    int num = 4;
    num_results = malloc(sizeof(int));
    set(num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

void set(int *results,int num){
    *results = num;
}

Another example would be:

#include <stdio.h>
#include <stdlib.h>
void set(int **r, int num);
int main() {
    int *num_results = NULL;
    int num = 4;
    /*num_results = malloc(sizeof(int));*/
    set(&num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

void set(int **results,int num){
    *results = malloc(sizeof(int));
    *(*results) = num;
}
Find elsewhere
Top answer
1 of 3
2
void displayNum(int n, int first, int second, int third, int fourth, int fifth){
    switch(n){
        case 5:printf("%d", fifth);
        case 4:printf("%d", fourth);
        case 3:printf("%d", third);
        case 2:printf("%d", second);
        case 1:
            printf("%d", first);
            break;
        default: printf("wrong n value\n");
    }
}
2 of 3
1

If you want to explore a different avenue, you could use a variadic function:

#include <stdarg.h>

void displayNum( int n, ... )
{
  va_list ap;
  va_start( ap, n );
  for ( int i = 0; i < n; i++ )
  {
    int value = va_arg( ap, int );
    printf( "%20s: %d\n", label( i+1 ), value );
  }
  va_end( ap );
  printf( "\n" );
}

where label is a function that will print out the proper label based on the value of i.

This way you don't have to pass fixed number of parameters; if you only want to display one value, you'd call it as

displayNum( 1, first );

If you want to display 3 values, you'd call it as

displayNum( 3, first, second, third );

The first argument n is fixed and must always be present. The remaining arguments are read based on the value of the first argument. If you pass a 1, displayNum will only read and display the first additional argument. If you pass a 3, displayNum will expect there to be at least three additional integer arguments on the stack.

Caveats: variadic functions are not type safe and the compiler will not warn you when you're passing arguments of the wrong type, nor will it alert you if you're passing too few or too many additional arguments. If you call it as displayNum( 1, "this is a test" );, you'll either crash or get garbled output.

Complete example:

#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

const char *label( int n )
{
  const char *units[] = { "zeroth", "first", "second", "third", 
                          "fourth", "fifth", "sixth", "seventh", 
                          "eighth", "ninth" };
  const char *teens[] = { "tenth", "eleventh", "twelfth", "thirteenth", 
                          "fourteenth", "fifteenth", "sixteenth",
                          "seventeenth", "eighteenth", "ninteenth" };
  const char *decades[] = { "", "", "twenty", "thirty", "forty", "fifty", 
                            "sixty", "seventy", "eighty", "ninety" };

  static char buf[256] = {0};

  if ( n < 10 )
    return units[n];
  else if ( n < 20 )
    return teens[n-10];
  else
  {
    int t = n / 10;
    int u = n % 10;
    sprintf( buf, "%s", decades[t] );
    if ( u > 0 )
    {
      strcat( buf, "-" );
      strcat( buf, units[u] );
    }
    else
    {
      buf[ strlen(buf) - 1 ] = 0;
      strcat( buf, "ieth" );
    }
    return buf;
  }

  return "";
}

void displayNum( int n, ... )
{
  va_list ap;
  va_start( ap, n );
  for ( int i = 0; i < n; i++ )
  {
    int value = va_arg( ap, int );
    printf( "%20s: %d\n", label( i+1 ), value );
  }
  printf( "\n" );
  va_end( ap );
}

int main( void )
{
  displayNum( 1, 1 );
  displayNum( 2, 1, 2 );
  displayNum( 3, 1, 2, 3 );
  displayNum( 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
  displayNum( 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
                  11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
                  21, 22, 23, 24, 25 );
  return 0;
}

Sample output:

[fbgo448@n9dvap997]~/prototypes/stdarg: ./mystdarg
               first: 1

               first: 1
              second: 2

               first: 1
              second: 2
               third: 3

               first: 1
              second: 2
               third: 3
              fourth: 4
               fifth: 5
               sixth: 6
             seventh: 7
              eighth: 8
               ninth: 9
               tenth: 10

               first: 1
              second: 2
               third: 3
              fourth: 4
               fifth: 5
               sixth: 6
             seventh: 7
              eighth: 8
               ninth: 9
               tenth: 10
            eleventh: 11
             twelfth: 12
          thirteenth: 13
          fourteenth: 14
           fifteenth: 15
           sixteenth: 16
         seventeenth: 17
          eighteenth: 18
           ninteenth: 19
           twentieth: 20
        twenty-first: 21
       twenty-second: 22
        twenty-third: 23
       twenty-fourth: 24
        twenty-fifth: 25

As written, the label function will only handle inputs up to 99, after which you'll get some funny output. The C language standard guarantees at least 127 arguments in a single function call, but you really don't want to do that.

EDIT

Or you could avoid all this nonsense and pass your values in an array, which is the better approach if all the values are of the same type:

void displayNum( int n, const int *values )
{
  for ( int i = 0; i < n; i++ )
    printf( "%20s: %d\n", label( i + 1 ), values[i] );
}
🌐
Julia Programming Language
discourse.julialang.org › general usage
Passing NULL as an argument through ccall? - General Usage - Julia Programming Language
October 2, 2017 - I cannot seem to figure out how to pass NULL as an argument to a function through the C interface. I’m using Ref{UInt8} as the argument type. I’m thinking that I could probably declare it as an Int and just pass 0, but…
🌐
Quora
quora.com › What-is-the-result-of-passing-NULL-as-a-parameter-to-functions-like-strcpy-in-the-C-programming-language
What is the result of passing 'NULL' as a parameter to functions like strcpy() in the C programming language? - Quora
Answer: If a NULL is passed to strcpy for either parameter, it will, most probably, crash. I asked question decades ago why C standard library functions do not check on NULL and answer was that’s not necessary and just bloats code. Let’s say that’s typical strcpy code: [code]char* ...
🌐
GeeksforGeeks
geeksforgeeks.org › c language › g-fact-44-passing-null-to-printf-in-c
Passing NULL to printf in C - GeeksforGeeks
June 2, 2017 - // Effect of passing null pointers to ( %s ) // printf in C #include <stdio.h> int main() { char* p = NULL; printf( "%s", p); return 0; } Output in GCC: (null) Note that the above program may cause undefined behavior as per C standard.
🌐
Go Forum
forum.golangbridge.org › getting help
Passing NULL to a C function - Getting Help - Go Forum
July 14, 2020 - I am new to GoLang and writing a C-GO binding. The C Function takes an argument which is a const char *. This works fine if I pass NULL for the argument in C. Now in my GO implementation I need to pass NULL in the C call. func AvformatMyfunc(ctx **Context, o *OutputFormat, fo, fi string) int { Cformat_name := C.CString(fo) defer C.free(unsafe.Pointer(Cformat_name)) Cfilename := C.CString(fi) defer C.free(unsafe.Pointer(Cfilename)) return int(C.myfunc((**C.struct_AVFormatContext)(unsafe...
Top answer
1 of 1
2

Look at exactly what this code does:

void setCharacter(char* pCharacter)
{
     pCharacter = malloc(sizeof(char));
    *pCharacter = 'b';
}

Remember that things get passed by copy. First, pCharacter is an address that's passed from main to setCharacter. Then setCharacter changes that address by malloc'ing new memory and assigning the address of that memory to the local pCharacter. Note that it doesn't change the contents of pCharacter in main. That doesn't mean much here because the address passed in to setCharacter was NULL.

If you wanted to both allocate new memory and initialize the contents of that memory, it isn't necessary to pass in any variables here, but the process isn't complete.

The problem that you're having is this. While setCharacter is allocating new memory and initializing the contents (to 'b', in this case), it isn't returning any information to main. The address of the new memory is only known to setCharacter. You would need to return the address to main. So, depending on how much you would want setCharacter to do, there are two ways to do it. One just sets the contents, the other would both allocate the memory and set the value.

Method 1:

int main (void)
{
    char* pCharacter = malloc(sizeof(char));

    setCharacter(pCharacter);
    printCharacter(pCharacter);
}

void setCharacter(char* pCharacter)
{
    //this would only set the contents using the address from main
    *pCharacter = 'b';
}

Method 2:

int main (void)
{
    // the next line will create pCharacter pointer
    // and will initialize with the call to setCharacter
    char* pCharacter = setCharacter();
    printCharacter(pCharacter);
}

char * setCharacter(void)
{
     // this will both allocate memory and set the contents
     char *pCharacter = malloc(sizeof(char));
    *pCharacter = 'b';
     return pCharacter;
}

Note that you need to update the function signatures.

If this answers your questions, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

🌐
The Coding Forums
thecodingforums.com › archive › archive › c programming
Passing NULL as a function pointer | C Programming | Coding Forums
March 5, 2011 - Apparently on your platform NULL is defined as `(void *) 0` or something similar (key part being the `void *` type). It is perfectly valid to use NULL declared this way for initializing function pointers, but lower quality compilers might not be smart enough the realize that. Hence the warning. You can avoid the warning by passing `0` instead of NULL. ... Il 18/02/2011 20:19, Andrey Tarasevich ha scritto: Yes, indeed in the past I passed '0' instead of NULL for function pointers argument, but I don't like this approach anymore...