2

I've been using C for a little over a year now, and I was wondering exactly how #define works. I know you can use it as a macro, eg. #define MUL(x, y) (x*y), but what's happening when you define something without giving a definition? e.g.

    #ifndef _SYNNOVESLIB_H
    #define _SYNNOVESLIB_H
    #endif

Does it just get marked as "defined", or true (as in 1) by GCC or whatever compiler is being used?

14
  • 2
    The macro name is then marked as defined, with just an empty replacement list. Are you interested in compiler source code? More interesting for users are specific C language rules, like en.cppreference.com/w/c/preprocessor/replace . "How" is a vague question. Commented Jul 2, 2024 at 18:39
  • @KamilCuk yeah, exact compiler source code would be great. Thanks for the replacement list thing, that helps! Commented Jul 2, 2024 at 18:50
  • 6
    "exact compiler source code would be great." no it will be completely useless. It will not show anything related to your question Commented Jul 2, 2024 at 18:57
  • 1
    It's really not much different from #define NAME x. In this case, x is an empty string, so when you use the macro it's replaced with an empty string. Commented Jul 2, 2024 at 19:21
  • 2
    Yes, @dan04, the OP's particular example looks like an include guard, but that's by no means the only use for macros with empty replacement lists. They seem to be interested in such macros generally, not just those used for include guards. Commented Jul 2, 2024 at 20:17

3 Answers 3

2

#define _SYNNOVESLIB_H tells the compiler to replace _SYNNOVESLIB_H with nothing. It will be true for the purposes of things which check whether a macro is defined such as defined, #ifdef, and #ifndef.

It can also be handy for conditionally defining certain features for compatibility. Here's an example in Perl 5.


6.10 Preprocessing directives provides the grammar for #define.

control-line:
  # define identifier replacement-list new-line

replacement-list: 
  pp-tokens(opt)

pp-tokens:
  preprocessing-token
  pp-tokens preprocessing-token

replacement-list is an optional list of pre-processor tokens, so it can be empty.

From 6.10.1 Conditional inclusion

  1. The expression that controls conditional inclusion shall be an integer constant expression except that: identifiers (including those lexically identical to keywords) are interpreted as described below;169) and it may contain unary operator expressions of the form

defined identifier

or

defined ( identifier )

which evaluate to 1 if the identifier is currently defined as a macro name (that is, if it is predefined or if it has been the subject of a #define preprocessing directive without an intervening #undef directive with the same subject identifier), 0 if it is not.

Note that the value does not matter, simply that it has been defined.

  1. Preprocessing directives of the forms

# ifdef identifier new-line groupopt

or

# ifndef identifier new-line groupopt

check whether the identifier is or is not currently defined as a macro name. Their conditions are equivalent to #if defined identifier and #if !defined identifier respectively.

Exactly how this is handled inside the compiler is up to the compiler, but a simple hash table would do. #define _SYNNOVESLIB_H would add a key of _SYNNOVESLIB_H with a null or empty value. #ifndef _SYNNOVESLIB_H would check whether the key exists in the hash table, ignoring the value. #undef _SYNNOVESLIB_H removes the key from the table.

Sign up to request clarification or add additional context in comments.

7 Comments

This answer could be read as saying that the only use for a macro defined with an empty replacement list is to test its definedness. That is not at all the case. Indeed, being able to test macro definedness is pretty much orthogonal to the contents of macros' replacement lists.
@JohnBollinger That is not the implication, it's just the scope of the question.
That's not the scope of the question as I read it. It's merely the nature of the particular example given within. "what's happening when you define something without giving a definition? [...] Does it just get marked as 'defined' [...]?" -- NO. It gets defined as a macro with the specified replacement list, which happens to be empty.
@JohnBollinger You seem to be wanting to discuss the fine distinction between "empty list" and "no value". You're technically correct, the best kind of correct, but that's not the question. I see now what bit implies definedness is the only use case, I'll soften that.
Not really. I don't care whether you say "defined as nothing" or "defined as an empty list". I do care that you say, incorrectly, "no definition". Ultimately, the important point is that a macro definition with an empty / no replacement list is not a special case, contrary to what the OP seems to think.
|
2

what's happening when you define something without giving a definition?

Every valid #define directive associates a macro name with a corresponding replacement list (a "definition", in your terms). An empty replacement list is allowed, and there's nothing special about that as far as the preprocessor is concerned. You should not think of such cases as "without giving a definition" because that will mislead you -- into thinking that there is some kind of special behavior associated, for example, or perhaps into supposing that you cannot expand such a macro like you do those with other kinds of replacement lists. Also because distinguishing a special class of macros as being without a definition makes your mental model of the language more complex than it needs to be.

Does it just get marked as "defined", or true (as in 1) by GCC or whatever compiler is being used?

It gets defined with an empty replacement list, which is entirely valid and has no special significance. Among the consequences is that preprocessor #ifdef directives and defined operators will recognize the macro as being defined, just as they will any other defined macro. Like any other macro, it can be expanded by the preprocessor, which replaces the macro name with its (empty) replacement list.

Although macros with empty replacement lists are frequently used specifically for definedness-testing purposes, such as in your inclusion-guard example, they are not limited to such use in practice. Sometimes you want to a symbol that expands to nothing, at least under some circumstances.

For example, to avoid multiple definitions of the same variable, variables defined in headers usually need to be declared extern, yet there needs to be one source where they are not (explicitly) extern. One way this is sometimes achieved (though I do not actually recommend it) goes something like this:

header.h

#ifndef EXTERN
#define EXTERN extern
#endif

// The following appearance of EXTERN will be expanded by the preprocessor:
EXTERN int my_variable;

most_source_files.c

#include "header.h"

// gets:
// extern int my_variable;

one_special_source_file.c

#define EXTERN
#include "header.h"
#undef EXTERN

// gets:
//  int my_variable;

With that, the same header provides explicitly extern declarations ordinarily, which is what is needed in most places, but corresponding non-explicitly-extern declarations in one chosen place where EXTERN is defined to expand to nothing.

8 Comments

You're emphasizing the fine distinction between an empty list and no list, but it's unclear how that answers the question. You've mentioned a number of times that an empty definition has other uses, but haven't shown any. Could you provide some examples where the distinction between empty list vs no list is important?
No, @Schwern, I am emphasizing that a macro definition such as the OP is asking about is not a special case. See the bold italic text? It does provide a definition for the symbol, whatever words you want to use for that, contrary to what the OP seems to suppose. All the other semantics flow from that. As for examples of uses other than testing for definedness, sure, I could provide some, but weren't you the one insisting elsewhere on a tight focus on the scope of the question?
The OP is new to C, I doubt much of this conversation makes sense to them. You're an expert, you're emphatic about this distinction. I assume it's important in some subtle way, but I can't see it. For my own education, would you show an example where this distinction is important?
I would not sell the OP short, @Schwern, but inasmuch as you're appealing to their presumed unsophistication with respect to the language, I submit that "it's not a special case" has less cognitive burden than "here are special rules for that case", and the former avoids priming them for confusion when they later discover a case where a macro such as they ask about is used other than strictly as a flag. Even ignoring the second of those, I simply do not fathom why you favor the special case description. Anyway, I have provided an example that I think is fairly approachable.
Thank you for an example, and your answer is much better. I think I see the distinction you're making: "EXTERN is defined as an empty list [of what?] which expands to nothing". Whereas "EXTERN is defined as nothing and expands to nothing" implies a special case. But that requires knowing it's a list of tokens (and that "nothing" and "empty list" are different) which requires knowing the grammar. If you already know that, you're probably not the audience. "Nothing will expand to nothing" is the natural assumption. It's the teaching technique of introducing complexity slowly, not all at once.
|
1

exact compiler source code would be great.

The #define is processesed in libcpp/directives.cc around https://github.com/gcc-mirror/gcc/blob/cc63b59e8843f049587b7a548a530f710085e577/libcpp/directives.cc#L652 . The definition is saved in https://github.com/gcc-mirror/gcc/blob/master/libcpp/macro.cc#L3831 . Internally macros are stored in a hash table , but have not found where does that happen.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.