34

I have a feeling this may be related to C syntax, but I started my programming life with C++ so I am not sure.

Basically I have seen this:

struct tm t;
memset( &t, 0, sizeof(struct tm) );

I am a bit confused with this syntax, as normally I would expect the above to instead look like this:

tm t;
memset( &t, 0, sizeof(tm) );

What is the difference between the two, and why is the former used instead?

Update

The structure tm that I am referring to is in wchar.h, and the definition is as follows:

struct tm {
        int tm_sec;     /* seconds after the minute - [0,59] */
        int tm_min;     /* minutes after the hour - [0,59] */
        int tm_hour;    /* hours since midnight - [0,23] */
        int tm_mday;    /* day of the month - [1,31] */
        int tm_mon;     /* months since January - [0,11] */
        int tm_year;    /* years since 1900 */
        int tm_wday;    /* days since Sunday - [0,6] */
        int tm_yday;    /* days since January 1 - [0,365] */
        int tm_isdst;   /* daylight savings time flag */
        };
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 1
    `struct tm` is from the standard C library and should be found in time.h not wchar.h – Nordic Mainframe Oct 11 '11 at 17:13
  • It probably is, but it's also in wchar.h on my platform as well (MSVC). The struct definition is wrapped in `ifndef` guard checking for `_TM_DEFINED`, but this is irrelevant to my original question. – void.pointer Oct 11 '11 at 17:15
  • possible duplicate of [typedef struct vs struct definitions](http://stackoverflow.com/questions/1675351/typedef-struct-vs-struct-definitions) – Praetorian Oct 11 '11 at 17:20
  • @Praetorian It's only a duplicate in hindsight. My original confusion comes from a different perspective, which is still valuable and a valid & useful alternative to finding arguably the same information. – void.pointer Oct 11 '11 at 17:22

6 Answers6

23

The simple answer is that the struct keyword there is present to restrict the lookup of the identifier tm to only user defined class types. It is probably left for compatibility with C, where it is required.

Contrary to what others say, there is no such thing as auto-typedef, nor do C and C++ differ with respect to how the identifiers for user defined types are managed. The only difference is in lookup.

You can read more here

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 2
    C and C++ do differ with regards to how identifiers for user defined types are managed, in two ways. First, in C the names of `struct`s, `union`s and `enum`s are in a separate namespace, which is used instead of the normal namespace when looking up names behind these keywords. Such names can never conflict with other names in C, and such names will never be found except when they follow one of these keywords. C++ has special rules to allow the conflict, and resolve it, but only if there is a conflict. – James Kanze Oct 11 '11 at 17:32
  • And unlike C++, in C, even if a `struct` is defined in another `struct`, its name is as if it were defined outside the encapsulating `struct`. Thus, in C++ `struct A { struct B {}; };` defines two structs: `A` and `A::B`. In C++, it also defines two structs: `struct A` and `struct B`. – James Kanze Oct 11 '11 at 17:34
  • 1
    @JamesKanze: Conceptually C++ still maintains the two namespaces with the difference that the user defined types will be looked up if the lookup in the general namespace fails. As in C, the names of user defined types cannot conflict with other identifiers (variables, functions) --the typedef would cause an identifier to be added to the general namespace and that can cause problems. Again, as I said before, the difference is in the lookup not. The other difference (nested structs) is true and related to the absence of mangling of identifiers in the C language. – David Rodríguez - dribeas Oct 11 '11 at 20:52
  • That's not the way the C++ standard describes it. §3.3.10/2 speaks of the class name or enumeration name being "hidden" when something else is declared in the same scope, and §3.3.1/4 speaks of declaring a class name or an enumeration name in the same scope as other names. – James Kanze Oct 12 '11 at 08:04
  • §3.4/1 also says "The name lookup rules apply uniformly to all names (including typedef-names (7.1.3), namespace-names (7.3), and class-names (9.1)) wherever the grammar allows such names in the context discussed by a particular rule." The rule, as I understand it, is that the other name will hide the class or enumeration name, except in contexts where the other name cannot legally appear (in practice, only in an elaborated type specifier). – James Kanze Oct 12 '11 at 08:04
  • @JamesKanze: I think we are basically saying the same thing in different ways. You are looking top-down, from the descriptions in the standard, and I am looking bottom-up: to implement those restrictions (in particular hiding) the compiler must (conceptually, it could be implemented with tags) maintain different sets of identifiers in the contexts. I think we do agree that there is no *auto-typedef*, and that the type identifiers must be treated differently from other identifiers, right? – David Rodríguez - dribeas Oct 12 '11 at 10:06
  • Rodriguez The effects should be the same; there is certainly no auto-typedef. As to the implementation---I'd probably go with handling them something like overloaded symbols, with "overload resolution" depending on the context where the symbol was used, rather than maintaining two different lookup tables (both with an implementation of the non-trivial scope rules). In C, the issue is somewhat simpler, because there is no `struct` scope for structures. (There is still function scope, however.) – James Kanze Oct 12 '11 at 11:57
15

In C, the struct tag names do not form identifiers on the global name space

struct not_a_global_identifier { /* ... */ };

To refer to that struct you have to use the keyword struct (to specify the name space)

struct not_a_global_identifer object;

or create a new identifier, in the global name space, with typedef

typedef struct not_a_global_identifer { /* ... */ } global_name_space_identifier;

There are 4 namespaces in C, see 6.2.3 in the C99 Standard:

  • label names
  • the tags of structures, unions, and enumerations
  • the members of structures or unions (not a single name space ... as many as structures or unions are defined)
  • global name space, for all other identifiers

This is a legal C program :-)

int main(void) {
  typedef struct foobar { int foobar; } foobar;
  foobar boo;
  boo.foobar = 42;
  if (boo.foobar) goto foobar;
foobar:
  return 0;
}
pmg
  • 106,608
  • 13
  • 126
  • 198
  • 2
    So basically with that in mind, in C I can declare a global struct named `struct foo {};` and also right beside it a global variable with the same name, `int foo;` and there will be no conflict? – void.pointer Oct 11 '11 at 17:13
  • 3
    Yes. You can have the same identifier in different name spaces without problem. In the code above, `foobar` is at the same time a type (*global* name space), a structure member (*member* name space), a structure tag (*tag name space*), and a label (*label* name space). – pmg Oct 11 '11 at 17:17
  • 1
    @RobertDailey: Not only in C, you can also do that in C++, and then you will hit the problem of having to add `struct` (or `class`) to declare a variable: `struct X {}; int X; struct X x;` Removing the `struct` keyword from the definition of `x` before will cause a compiler error. – David Rodríguez - dribeas Oct 11 '11 at 21:02
  • Thank you so much, very clear now. I suddenly found that it's kind of two-level idea of this... If it's not in current one the goto the drawer of `struct` and find `foobar`. – Kindred Nov 23 '18 at 15:05
1

The user-defined types have their own identifier space, i.e. when the compiler parse the file it stores each identifier in its corresponding space.

When you refer to tm, the C compiler (as the C++ one) will search for this identifier in the global identifier space. The C++ compiler will then lookup in the user-defined types identifier space if it hadn't found the symbol before.

Basically, if you want to have the same behavior as in C++, add this line :

typedef struct tm tm;

You can combine struct declaration and typedef like that :

typedef struct tm { int field } tm;

Or using a anonymous struct :

typedef struct { int field } tm;

The same behavior applies for enum and union :

typedef enum { VALUE } myEnum;
typedef union { int integer; char charArray[4]; } myUnion;
Jaffa
  • 12,442
  • 4
  • 49
  • 101
  • The auto typedef just do what I did, but automatically. As C++ is more oriented on type, the type is directly available. C is more oriented on the meaning of variable, thus it is important to know that t is a struct tm, not just a tm. Notice that the same thing happens with enums and unions – Jaffa Oct 11 '11 at 17:01
  • 4
    There is no *auto typedef* in C++ either. The effect of a typedef is declaring a new identifier in the global identifier space to refer to the type in the user-defined-types identifier space. That is, `typedef struct tm tm;` creates a new identifier `tm` to refer to `struct tm`. The behavior of typedef is exactly the same in C and C++. The actual difference is in lookup, where C++ will automatically lookup identifiers in the user-defined-types identifier space *if the identifier was not previously found*. – David Rodríguez - dribeas Oct 11 '11 at 17:06
  • @DavidRodríguez-dribeas Could you please add some more detail to your last statement: `will automatically lookup identifiers in the user-defined-types identifier space if the identifier was not previously found`. Not sure what the "user-defined-types identifier space" is, nor what the exact lookup logic is that you are referring to. – void.pointer Oct 11 '11 at 17:09
  • 2
    @RobertDailey: You can read more [here](http://stackoverflow.com/questions/1675351/typedef-struct-vs-struct-definitions/1675446#1675446) – David Rodríguez - dribeas Oct 11 '11 at 17:11
  • @DavidRodríguez-dribeas Thanks David. You should post an actual answer with all of this information! – void.pointer Oct 11 '11 at 17:19
1

Using struct tm t; is for compatibility with C, in which declaring a struct named "tm" defines a type named "struct tm" but not one named "tm" (as opposed to C++, in which both names for the type are declared).

jwodder
  • 54,758
  • 12
  • 108
  • 124
1

In your example tm can have been a typecasted structure.

e.g.

typedef struct tm_t
{
  int x; 
}tm;

and then you can do

tm t; 
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Jay D
  • 3,263
  • 4
  • 32
  • 48
0

You can see the reference linked below and quoting from there "In C, you must explicitly use the struct keyword to declare a structure. In C++, this is unnecessary once the type has been defined." See the link for more information and examples.

http://msdn.microsoft.com/en-us/library/64973255%28v=vs.80%29.aspx

zar
  • 11,361
  • 14
  • 96
  • 178