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
NULLpointers 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.
Say i have a function "void blabla( int argument )"
if i call it as this: blabla(NULL);
can i then do the following inside the function:
if(argument == NULL){}
?
if so, is there a more straightforward way of doing this?
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
NULLpointers 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.
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.
c - Clarification of NULL assignment to char * - Stack Overflow
How to pass a NULL pointer as a function argument?
c - Passing Null Pointer in Function - Stack Overflow
c - How to pass null parameters to a function - Stack Overflow
Macro expansion is just text replacement, so when you passed NULL, it will expand to NULL->member, clearly it is an error. One way is to use a temporary variable for that:
#define macro1(arg1) \
do{ \
A* p = (arg1);
int _state = 0; \
if (p && p->member_) \
_state = p->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a);
macro1(NULL);
This way both cases will work.
You have to understand what's a macro in order to understand your mistake. Except for the compiler, there's an animal called pre-compiler. It replaces all the macros' references by the actual code defined for this macro. So this code:
#define macro1(arg1) \
do{ \
int _state = 0; \
if (arg1 && arg1->member_) \
_state = arg1->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a); // Works
macro1(NULL); // Error
will be replaced with:
A *a = new A():
do{
int _state = 0;
if (a && a->member_)
_state = a->member_->state_;
printf("%d", _state);
} while(0)
do{
int _state = 0;
if (NULL && NULL->member_)
_state = NULL->member_->state_;
printf("%d", _state);
} while(0)
THIS code will be compiled. And now you can see for yourself what's the root cause of the compilation error.
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:
- You probably have this in your full code, but don't forget to
#include <stdio.h>to useprintf()and to#include <stdlib.h>to usemalloc() - Your function
set()should be declared beforemain(), you can do this by declaring a prototype beforemain()or simply defining theset()function beforemain().
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;
}
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;
}
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");
}
}
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] );
}
You cannot.
The C program receives arguments as zero-terminated strings. Such a string cannot contain a null character, by definition.
If you want to pass a null character, then you must somewhat encode it with some syntax, and your C program must then decode it by interpreting that syntax.
C strings are null-terminated, so passing strings containing NUL characters is not possible in C. :-P
Now, if you just wanted a way to convert \0 (in the user input, i.e., "\\0" as a C string) into actual NUL characters, that's another matter. In that case, your program just needs a parser to treat \0 as separators.