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.
#define NAME x. In this case,xis an empty string, so when you use the macro it's replaced with an empty string.