7

I am writing some header files that will be included by C++ code and also included by C code. I would prefer for the header source code to be consistent and always use "nullptr" rather than NULL (or the integer constant 0) in these headers.

This brings up the possibility of adding a piece of code such as:

#ifndef __cplusplus
#define nullptr 0      /* or ((void *)0) */
#endif

Or alternatively:

#include <stdio.h>
#ifndef __cplusplus
#define nullptr NULL
#endif

Edit: Or from Ben Voigt's suggestion:

#ifndef __cplusplus
static const void *nullptr = 0;
#endif

The question is, what is the downside to doing this? Is there some reason why nullptr was added to C++ but not to C? This seems like something that would make code more consistent and readable when C++ and C have to inter-operate.

Edit: a sample header

#ifdef __cplusplus
extern "C" {
#endif

extern struct MyStruct *globalMyStruct;
extern int myFunc(struct MyStruct *ptr,int arg);

#ifdef __cplusplus
}
#endif

#define MYMACRO(x) ((globalMyStruct!=nullptr) ? myFunc(globalMyStruct,(x)) : -1)

Even if MyStruct is a C++ object, then it is legal for C code to include the above header file and use the MYMACRO definition. I want to include the above header code in both C and C++ files. But if I include the above header from a C file, then it won't compile due to the use of nullptr.

It is perfectly valid for C code to contain pointers to C++ objects, and those pointers can be passed as arguments to functions (and perhaps those functions are written in C++). This is just one way C++ and C can inter-operate.

deltamind106
  • 638
  • 7
  • 19
  • 1
    nullptr is not holding the value null or zero (0). The source code for it is at https://code.woboq.org/llvm/libcxx/include/__nullptr.html – knoxgon Sep 11 '17 at 16:27
  • 3
    Don't do this. It will just make the C code more difficult to read, and `0` is not a good representation for the null pointer in C. –  Sep 11 '17 at 16:27
  • @NeilButterworth "and `0` is not a good representation for the null pointer in C". I don't follow this. C typically uses `NULL` for the null pointer which is usually just a macro defined to 0. – Nir Friedman Sep 11 '17 at 16:28
  • 3
    @NirFriedman It's also commonly a macro expanding to `((void *) 0)`. There's a semantic difference in some situation, but mostly using plain `0` should work. – Some programmer dude Sep 11 '17 at 16:30
  • @Nir it's normally defined as something like `(void *)0` –  Sep 11 '17 at 16:30
  • Okay, points above are fair enough, but covered by OP's "Or alternatively...". I think the question is fairly interesting, a trade-off between different behavior in the languages, or simply using NULL in both and throwing away the advantages of nullptr. – Nir Friedman Sep 11 '17 at 16:31
  • @delta Of course, this begs the question - why do you have to differentiate between C and C++ code, as for this to work you must completely control the use of both? –  Sep 11 '17 at 16:35
  • Why would a header to be included into both C and C++ make use of null? – user7860670 Sep 11 '17 at 16:40
  • 2
    @VTT Well there are lots of reasons this might happen, too numerous to articulate in a SO comment. If you can't think of any then you probably aren't going to be able to contribute to the question. – deltamind106 Sep 11 '17 at 17:57
  • @Neil Butterworth In Microsoft land with the cl.exe compiler, NULL is defined to just the integer constant 0 (for c++). In GNU land with the gcc compiler, NULL is defined to __null, which is an internal keyword. None of my modern systems define NULL as (void *)0 for c++. – deltamind106 Sep 11 '17 at 18:34
  • 1
    One can obviously squeeze almost anything into header file, but it does not mean that it is a good idea to do so. Can you list at least one case when you need null in a header to be included into both C and C++? For me it looks like an XY problem. – user7860670 Sep 11 '17 at 18:53
  • @delta In `corecrt.h` NULL is defined as `((void *) 0)` for C code. –  Sep 11 '17 at 19:00
  • @Neil Butterworth Strange because if I perform "cl -E try.cpp" on a simple C++ program that contains: #include int main() { void *a=NULL; } Then the resulting pre-processor output is "void *a=0;". Maybe your corecrt.h header is used for C, or for a different version of Visual Studio? – deltamind106 Sep 11 '17 at 19:22
  • 1
    @VTT Yes there are lots of things end up being done in header files that might not be a good idea in a perfect theoretical world, but there is the business of getting something essential to work with legacy code, without re-designing all of said legacy code from scratch. – deltamind106 Sep 11 '17 at 19:27
  • 1
    "Maybe your corecrt.h header is used for C" - we are talking about C, not C++. –  Sep 11 '17 at 19:40
  • 2
    What cl.exe does with a .cpp file is likely (and should be) different than what cl.exe does with a .c file. C and C++ are different languages, with subtly different rules in some areas. NULL is one of those areas. I once forgot that and was bitten by the fact the in C `0` and `(void*)0` are different. – Rob K Sep 11 '17 at 19:43
  • @Rob K Indeed they are different in C and C++, which is at root of this question. The C++ standards committee nicely solved the problem of NULL/0 for C++, but didn't define nullptr for C where it seems they obviously should have done this... Setting a pointer (of C++ objects, of C structs, of primitive integers) to point at "nothing" is a basic requirement of both C++ and C. My question is (an admittedly lame) attempt at rectifying what the standards committee failed to do. I'm not saying my code above should be "the standard", but there's work to get done in the real world. – deltamind106 Sep 11 '17 at 20:19
  • 1
    Using `NULL` or some refined null value substitution in the given header example won't really make a difference. It would be better to think about substituting exposed global variable and user macro with appropriate alternatives. Macros and global variables are definitely a nasty mix. – user7860670 Sep 11 '17 at 20:45
  • 4
    @deltamind106: You said the C++ standards committee "obviously" should have defined `nullptr` for the C language, but they have no control over the C language. They have some influence, since the committees talk back and forth and certainly read each others' work, but the C++ committee cannot make changes to the C language standard. At all. As for why the C committee didn't "steal" this feature of C++, maybe in C they considered breakage by changing `nullptr` from a common identifier into a reserved word to be much more severe than the C++ committee did. – Ben Voigt Sep 11 '17 at 20:52
  • 1
    @VTT It's just a contrived example, because providing the real situation would take 20 pages of explanation, all of which is irrelevant to the question at hand. I didn't come here to debate the merits of when to redesign legacy code vs. when to make what you have work. – deltamind106 Sep 11 '17 at 20:54
  • @Ben Voigt Point well taken. – deltamind106 Sep 11 '17 at 21:01

1 Answers1

1

Two downsides off the top of my head:

  • NULL/0 and nullptr have different behavior (as explained here) You wouldn't want nullptr(macro) and nullptr(language feature) to have different behavior even though they look the same, would you?
  • Macros should be avoided (as much as possible in C, anyway...)

Edit: Something I do in older versions of C++ (but not C):

#if __cplusplus >= 201103L || (__cplusplus < 200000 && __cplusplus > 199711L)
//use C++ 11 nullptr
#else
    struct nullptr_t
    {
        template <class T>
            operator T* (){return (T*)0;}
    }nullptr;
#endif

It mimicks the behavior of nullptr without being a macro.

  • 4
    macros should be avoided in C++ but in C, they are applicable. – knoxgon Sep 11 '17 at 16:29
  • 7
    @V.G They are applicable in both languages, and should be avoided in both languages, where possible. –  Sep 11 '17 at 16:31
  • 2
    @NeilButterworth Not necessarily, there are times where macros make the life much easier in C (IMO) but I haven't yet needed any macro implementation in C++ due to a huge range of available features. First and foremost, different standards save the situation from the macro-madness. In C, we have limited choices and using the macro at the correct and safe way isn't so obsolete. I like it. – knoxgon Sep 11 '17 at 16:38
  • 2
    @V.G include guards, debugging macros https://latedev.wordpress.com/2012/08/09/c-debug-macros/ are both either needed or useful in C++. –  Sep 11 '17 at 16:40
  • @NeilButterworth Indeed. – knoxgon Sep 11 '17 at 16:41
  • Ok on the sample code, but the question is about a potentially suitable definition of nullptr in C (not C++). It is clear that within C++, nullptr is different than NULL and 0. But within C, cannot nullptr be defined as something that mimics NULL? After all, the differences between nullptr and NULL/0 are only apparent in C++. – deltamind106 Sep 11 '17 at 20:40
  • 1
    @deltamind: In C, just `static void* const nullptr = 0;` would be at least as good as the macro, without being a macro. It also wouldn't break code that locally uses `nullptr` as an identifier, because scoping rules still apply. – Ben Voigt Sep 11 '17 at 20:54