Tomalak's answer is great advice for calling existing functions. When you're writing a function (or when you are free to change the function) and you want parameters to be optional, you should try to design it so the usage is correct, intuitive and self-documenting. If we compare:
1) bool really_test = ...;
test(really_test, my_test);
2) if (...)
test(my_test);
3) test(... ? &my_test : NULL);
2) is more comprehensible for using if
- a universally understood keyword, and not simply implying that the function's first argument can be set to bypass the test. The dangerous scenario is that someone writes code like 3) misremembering or misunderstanding the API; it works happily until that NULL condition is satisfied (which might be in prod rather than testing).
Similarly, when some behaviour is optional, I prefer to use an enum to document those options instead of a boolean that communicates nothing at the call site. Contrast...
test(false);
test(true);
...with...
test(Text_Output);
test(Html_Output);
If there are multiple arguments, then it can be useful to use a sentinel such as NULL to indicate that some of them aren't applicable. It's harder to get this wrong if the arguments are of distinct type:
test(my_test, /* output stream */ NULL, &error_counter, /* skip list */ NULL);
In such usage, you can expect a compile-time error if an argument is accidentally omitted or the order is wrong.
When the compiler can't check the argument order/count it's dangerous:
test(&my_test /* mandatory, this one has three stages */,
/* stderr else out */ true, /* update counters */ false,
/* skip 1st stage */ true, /* skip 2nd */ false, /* skip 3rd */ true);
That might satisfy...
test(Test*, bool use_error_stream, bool update_counters, ...);
...which would be a spectacularly bad API.
Note that references mandatorily point to a valid object, so some people like to use them where possible to communicate the fact the option is mandatory. Other people think it's more important to hint at whether a parameter is modifiable at the call site, and write functions accepting non-const pointers:
f(my_object); // obviously mandatory, but is it const or not?
f(&my_object); // _might_ be modifiable (if that conventions followed),
// but is it mandatory or not?