Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on Should I check if pointer parameters are null pointers?

Parent

Should I check if pointer parameters are null pointers?

+11
−0

When writing any form of custom function such as this:

void func (int* a, int* b)

Should I add code to check if a and b are null pointers or not?

if(a == NULL)
/* error handling */

When posting code for code reviews, one frequent comment is that such checks against null should be added for robustness. However, these reviewers are then just as often corrected by others telling them to not add such checks because they are bad practice.

What should be done? Is there an universal policy to check or not to check, or should this be done on case-to-case basis?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

I don't think this is enough for a full-fledged answer, but I think adding debug `assert`s for testin... (1 comment)
Post
+2
−4

The kind of comments telling you to add checks against null are typically coming from programmers mostly used to deal with higher level programming languages. They think that generally, more explicit error handling is always a good thing. And that is true in most cases, but not in this one.

The spirit of C has always been performance over safety. Give the programmer the freedom to do what they want, as fast as possible, at the price of potential risks. For example arrays were purposely not designed with a stored size or any bounds-checking (The Development of the C Language - Dennis M. Ritchie).

If we look at the standard library, lots of functions like memcpy/strcpy do for example not support overlapping parameters even though they could. Instead a specialized function memmove was designed for that purpose - safer but slower. Similarly, malloc does not needlessly initialize the allocated memory to zero/null, because that would lead to execution overhead. A specialized function calloc was designed for that purpose. And so on.

We should never add checks against null because they add needless overhead code.

Instead the function should be explicitly documented to not handle the case where an argument which is a null pointer is passed, thereby leaving error handling to the caller.

The reason for this is simple: there are a whole lot of scenarios where the caller is sure that the passed arguments are most definitely not null pointers. Having the function needlessly checking for null pointers only adds bloat in the form of additional branches. Take this example:

char* array[n] = { ... };
for(size_t i=0; i<n; i++)
{
  func(array[i]);
}

Now if this snippet is performance-critical, we stall the whole loop in case func repeatedly checks the passed array for null. We know it isn't a null pointer, but the repeated check may lead to branch prediction problems or cache misses on some systems. On any system, it means a useless check taking up performance. And it cannot get optimized away unless the function is inlined, perhaps not even then.

To give the caller the freedom to do as they like, we should let them handle the null pointer check. Which should ideally be done as close to the point where something might end up as a null pointer, rather than inside some unrelated library function.


As a side note, some very modern compilers like recent gcc or clang versions have limited possibilities of static analysis at compile time, in case we use the exotic static declarator feature (-Wall is necessary for this in gcc). But it has very limited use:

// must point at array of at least 1 item and not null
void func (int a[static 1]); 

int main (void) 
{
  func(NULL); // warning null passed to a callee that requires a non-null argument 
  
  int* x=NULL;
  func(x);   // no warning, the compiler can't predict what x contains
}

It's better to use dedicated static analyser tools to find bugs like this and then we don't need the exotic language feature either. Compilers are still to this date not very good at finding application bugs through static analysis.

History
Why does this post require moderator attention?
You might want to add some details to your flag.

3 comment threads

I find this very dogmatic. "Never" is a very strong word. As someone said: "Blindly following best pr... (8 comments)
Uh... what? (8 comments)
I agree with you analysis. There is a problem with one of the examples: you should use `for(size_t i ... (2 comments)
Uh... what?
elgonzo‭ wrote almost 2 years ago · edited almost 2 years ago

Since bounds checking should be done anyway (depending on the purpose of the function, as well as whether it is just "locally"/"privately" used vs. it being some "public" function of some degree) when passing pointers to an array (or some memory buffer) to avoid buffer-overflow bugs, why not also check for null pointers?

Is your advice really: "Do bounds checking if necessary, but never ever check for null pointers"?

Lundin‭ wrote almost 2 years ago

elgonzo‭ How is that even related? Also I don't understand what you are talking about: either a function gets an array size passed, in which case the caller has to get it right, or it doesn't get an array sized passed, in which case no can do. Either way, a function in C cannot do meaningful bounds-checking of an array parameter. This too is something which should be handled by the caller. (It can do algorithm-level bounds checking when adding stuff to the array, but that's another story.) Please post a code example of a function performing a meaningful bounds checking of it's array parameter.

elgonzo‭ wrote almost 2 years ago · edited almost 2 years ago

Whatever.

Even the C standard defines functions that check for null pointers. To lean on your mention of memmove, how about memmove_s (since C11, i believe), for example.

It's actually quite funny how your absolutist recommendation of "We should never add checks against null [...]" is so obviously and obliviously in opposition to how the C standard itself defines functions like memmove_s and others. Or are you trying to say that it's okay for a C standard function to do null checking, but it's bad practice for any other function that's not a C standard function?

Lundin‭ wrote almost 2 years ago

elgonzo‭ It's not "whatever", you criticise my post and down vote it with the and claim that functions should do bounds checking, which I still don't know what it has to do with null checking. I'm happy to get constructive criticism but this isn't it. As for the optional annex K bounds checking interface, everyone who's been around since C11 knows that it's been massively criticised and barely implemented, for example by the C committee itself here. "It's actually quite funny", you don't even seem to know how those functions work, not a single one has documentation claiming that a null pointer check is done. On the contrary, they do just as I advocate in my answer, cover null checks with documentation. See for example C11 K 3.7.1.2 "The memmove_s function" ... "Neither s1 nor s2 shall be a null pointer." This means that the responsibility is on the caller side through documentation.

Lundin‭ wrote almost 2 years ago

elgonzo‭ So now I'm not only waiting for you to explain how a C function should do bounds checking of its array parameter, I'm also waiting for you to explain exactly how memmove_s checks it's parameters against null, contrary to the run-time constraints in K 3.7.1.2 I just quoted. Or we could just delete this whole silly unfounded comment thread, up to you.

elgonzo‭ wrote almost 2 years ago · edited almost 2 years ago

It's not "whatever" [...]

The "whatever" was in response to not wanting to focus on bounds-checking, but trying to stick to the topic of the Q and A about null checks. But i have to take the blame for it because i wrote my first comment exactly like that. Of course if the function receives pointers to arrays/buffers, it can't do any meaningful and reliable bounds checking (aside from special cases with additionally defined rules, like in the case of null-terminated strings or some such).

As for the optional annex K bounds checking interface, everyone who's been around since C11 knows that it's been massively criticised and barely implemented, for example by the C committee itself here.

Good read. I wasn't aware that there was/is a critial discussion surrounding these functions with a proposal to remove/deprecate K-Annex functions.

(1/3)

elgonzo‭ wrote almost 2 years ago · edited almost 2 years ago

(2/3)

[...] On the contrary, they do just as I advocate in my answer, cover null checks with documentation. See for example C11 K 3.7.1.2 "The memmove_s function" ... "Neither s1 nor s2 shall be a null pointer." This means that the responsibility is on the caller side through documentation.

I know it's bad style, but i would throw a question back: Given the definition of behavior described in the four text paragraphs of C11 K 3.7.1.2, how could the described behavior possibly be realized without some form of null check (regardless of how the null check would be achieved)?

elgonzo‭ wrote almost 2 years ago · edited almost 2 years ago

(3/3)

explain exactly how memmove_s checks it's parameters against null,

What's the point of asking how exactly a memmove_s implementation achieves checking for null? Why would that matter? What matters is that to conform with the behavior described in C11 K 3.7.1.2, memmove_s has to somehow check for null pointers and return with an appropriate value, in whatever way a particular memmove_s implementation chooses to.

Or we could just delete this whole silly unfounded comment thread, up to you.

Well, given that there is useful and/or interesting information to be found between the silliness (like the N1967 discussion and proposal you linked to), i would not like to see this being deleted.

down vote it with the and claim that functions should do bounds checking

Yeah, that was the terrible phrasing of my first comment. Why i downvoted is the absolutist recommendation of never doing null checks regardless of any context in which a function is meant to be used