Is there an order of lookup in the namespaces namely, tag namespace and ordinary name space ? Consider the following code :
#include <stdio.h>
int main (void){
typedef struct{ //This belongs to ordinary name space
int min;
} st;
st myst;
myst.min=6;
struct myst{ // This belongs to tag name space
int min;
};
myst.min=7;
printf("%d\n%d\n",myst.min,myst.min);
return 0;
}
Output
7
7
The compiler looks for the variable to print in the tag namespace first , I guess. I don't know if the lookup is even done for the same identifier in the ordinary namespace and if it is done I am clueless why it doesn't print it.
There is no namespace lookup order in C. Only one namespace is ever considered for any particular identifier; it is determined by what kind of identifier is being looked up. Structure tags are one kind, with their own namespace; variable names are in the broader category of "ordinary identifiers", which have a separate namespace. There are other name spaces, too, but the compiler can always tell from context which one is relevant to any given identifier.
Thus, in your program, both uses of myst.min refer to the variable declared as st myst; and there isn't any second variable in the "tag" namespace (as you seem to have thought there would be).
You can see this for yourself by commenting out everything within main above myst.min = 7:
#include <stdio.h>
int main (void){
#if 0
typedef struct{ //This belongs to ordinary name space
int min;
} st;
st myst;
myst.min=6;
#endif
struct myst{ // This belongs to tag name space
int min;
};
myst.min=7;
printf("%d\n%d\n",myst.min,myst.min);
return 0;
}
Attempting to compile this will produce a hard error:
test.c: In function ‘main’:
test.c:14:3: error: ‘myst’ undeclared (first use in this function)
What the struct myst declaration does is declare another type, which you can then use to declare variables. But it's not used for anything unless you actually do that. For instance
#include <stdio.h>
typedef struct { int min; } st;
struct myst { int min };
int main(void)
{
// uses the typedef name 'st', in the ordinary namespace,
// to declare the variable 'myst', also in the ordinary namespace
st myst = { 6 };
// uses the struct name 'myst', in the tag namespace,
// to declare the variable 'myst2', in the ordinary namespace
struct myst myst2 = { 7 };
printf("%d %d\n", myst.min, myst2.min);
return 0;
}
will print 6 7. This program also illustrates how myst, the variable, and myst, the struct tag, are indeed in two different namespaces and can be referred to independently.
(Thanks to John Bollinger for the new first paragraph. —ed)
C namespaces are completely disjoint. Each identifier is searches ony in one namespace. For example:
Identifiers that follow structand union keywords are searched in the tag namespace
Identifiers that follow the goto keyword are searched in the label namespace
Identifiers that follow . or -> symbols are searched in their structure or union member namespace (each structure and union has its own member namespace; type of the preceding expression determines which one to search)
Other identifiers are searched in the ordinary namespace.
There is no lookup order. The (unique) namespace to search is completely determined by the context.
Related
Quoting from here,
In C, there are two different namespaces of types: a namespace of struct/union/enum tag names and a namespace of typedef names.
name.c
$ cat name.c
#include<stdio.h>
typedef long long long2;
int long2 () {
return 4;
}
int main() {
printf("hello, world!");
return 0;
}
$ gcc name.c -o name
name.c:4: error: 'long2' redeclared as different kind of symbol
name.c:3: error: previous declaration of 'long2' was here
$
name2.c
$ cat name2.c
#include<stdio.h>
int four() {
return 4;
}
struct dummy {
int member;
};
int main() {
struct dummy four;
}
$ gcc name2.c -o name2
$
I am trying to understand C namespace conflicts.
In the first case, why is there a conflict? Do functions also belong to the typedef namespace?
In the second case, why is there no conflict at all? The function and the variable both are named four. Why does the compiler allow that? How is &four supposed to be resolved?
C has four different name spaces for identifiers:
Label names (the goto type).
Tags (names of structures, unions and enumerations).
Members of structures and unions (these have a separate namespace per structure/union).
All other identifiers (function names, object names, type(def) names, enumeration constants, etc).
See also C99 6.2.3.
So your two question can be answered as:
Yes, function names and typedef names share the same name space.
No conflict, because the compiler will use scope rules (for function or object names). The identifier in main is said to shadow the global function name, something your compiler will warn you about if you set the warning levels high enough.
But the crucial point on your examples isn't about namespace, but the scope of the names.
In name.c, both long2 are "ordinary identifiers" (share the same name space), and both of them are defined in the same scope, so there is a conflict. (C99 §6.7/3)
If name2.c, the local variable four is in a scope deeper than the function four, so the variable hides the function four (C99 §6.2.1/4).
Your 2nd example does not show "no conflict". There is a conflict! Try this:
#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
struct dummy four;
four.member = four();
}
And now this
#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
int (*fx)(void) = four; /* "save" function */
struct dummy four; /* hide it */
four.member = fx(); /* use "hidden" fx */
}
In your 2nd example, the variable four hides the function four().
To answer your two questions quick.
Q1. In the first case, why is there a conflict? Do functions also belong to the typedef namespace?
A1. Yes, identifiers for functions and typedef'ed types share the same namespace. So there's a conflict.
Q2. In the second case, why is there no conflict at all? The function and the variable both are named four. Why does the compiler allow that? How is &four supposed to be resolved?
A2. There's no conflict in your 2nd example even though the two identifiers named four belong to the same identifier namesapce, which is Ordinary namespace. Because the variable identifier four in the main is in a function scope, whereas the function identifier four in a file scope. So the latter one is hidden by the former one as the basic scope rule. If you move the variable four to the file scope(global) like below, you'll see an error.
...
struct dummy four; // Error! there's a previous definition
// in this file scope(global).
int main() {
// struct dummy four; // Commented out.
}
Actually, there are four different identifier namespaces: Label, Tag, Member, and Ordinary. You can find details about them at https://en.cppreference.com/w/c/language/name_space
I have a simple C code example using a struct in a struct.
I need for documentation (doxygen) a named struct, in my example HermannS.
In doxygen I can refer to age with \ref ottoS::HermannS::age.
In doxygen I can not use \ref ottoS.name.age etc
For logic the name HermannS have to be the same.
Problem: But now the code create an redefinition-error
struct ottoS {
struct HermannS {
int age;
} name;
};
struct otherS {
struct HermannS {
int size;
} name;
};
int main ()
{
return 0;
}
I get the following error-message:
main.c:16:10: error: redefinition of ‘struct HermannS’
struct HermannS {
^~~~~~~~
main.c:10:10: note: originally defined here
struct HermannS {
^~~~~~~~
Question: how to define a struct in a struct and reuse the name HermannS ?
better Question: is there a gcc extension (prefix) to hide this error?
→ but this would be a very high price to pay for a simple doxygen documentation issue.
A struct declaration does not create a new namespace in C the way it does in C++, so you can't create type names that are "local" to a struct type. The tag name HermannS can only be used for one struct type definition.
C has four name spaces:
all labels (disambiguated by : or goto);
all tag names (disambiguated by struct, union, or enum)
member names (disambiguated by . or ->)
all other names (typedef names, enumeration constants, variable names, function names, etc.)
Unfortunately, what you're trying to do won't work in C - you'll have to use a different tag name for each inner struct definition.
You can't, as the compiler is telling you, and you really do not need to either. If you're not using that structure anywhere else, just don't name it:
struct ottoS {
struct {
int age;
} name;
};
struct otherS {
struct {
int size;
} name;
};
The only other (obvious) solution is to name the two inner structs differently.
If your logic/code/documentation is based on the fact that you need those two different structures to have the same name, then I would advise revisiting the logic, because it looks flawed.
is there a gcc extension (prefix) to hide this error?
No there is not. This is not a simple error, it's invalid C code: you are effectively defining two different types under the same name. The compiler will not be able to compile your code unless you avoid this.
C standard say in section 6.7.2.1 para 8
The presence of a struct-declaration-list in a
struct-or-union-specifier declares a new type, within a translation
unit.
So all structs within the same translation unit share the same namespace.
HermannS is redefined thus code is ill-formed.
Some workaround would be adding a prefix with the parent structure name:
struct ottoS {
struct ottoS_HermannS {
int age;
} name;
};
struct otherS {
struct otherS_HermannS {
int size;
} name;
};
Latest C standards allow the implementation to accept characters from the extended set like $. It will help avoid collisions with more conventional names. It works with gcc. However, the code will no longer be portable.
struct ottoS {
struct ottoS$HermannS {
int age;
} name;
};
struct otherS {
struct otherS$HermannS {
int size;
} name;
};
It mimics :: operator form C++ a bit. You could use some other unicode character and use it to postprocess your Doxygen documentation with sed-like tool. I mean replacing magic character with ::.
As the program is ill-formed it will not compile. However if struct HermannS is never used except the declaration then you could use the following trick.
If HermannS is removed then ottoS.name and otherS.name would become anonymous structs and the code would compile. All anonymous structs are independent types. Just pass -DHermannS= to the GCC's command.
gcc prog.c -DHermannS=
It will add a macro HermannS that expands to an empty token.
Do not pass this option to doxygen to let it generate a proper documentation.
I am new to the C language and struggling with how to access enum's within a struct.
My code is the following:
bankHeader.h File
struct bankAcct{
int amount;
enum typeOfAcc{chck = 0, saving = 1};
int balance;
}
bank.c File
#include <stdio.h>
#include "bankHeader.h"
struct bankAcct test;
test.amount=100;
// I want to be able to get the value within my typeOfAcc
// example something like test.typeOfAcct = "chck" should return 0;
I reviewed some of the forms but I dont see anything that was easy to understand or worked.
If the enum is meant to be local to the struct, use an anonymous enum:
struct bankAcct{
int amount;
enum {chck = 0, saving = 1} type_of_acct;
int balance;
};
You could also put a tagged enum inside the struct:
struct bankAcct{
int amount;
enum typeOfAcc {chck = 0, saving = 1} type_of_acct;
//^this misleadingly puts `enum typeOfAcc` in filescope
int balance;
};
but a tagged (as opposed to an anonymous one) inner definition of an enum (or struct or union) will be hoisted. In effect, the latter snippet is just a confusing way of doing:
enum typeOfAcc {chck = 0, saving = 1};
struct bankAcct{
int amount;
enum typeOfAcc type_of_acct;
int balance;
};
Note that as peter-reinstate-monica points out in his comment below, the chck and saving constants will be "hoisted" regardless of whether or not you choose to use an anonymous embedded enum type.
Every field in your struct declaration is in the form
type fieldName;
With
enum typeOfAcc{chck = 0, saving = 1};
you have specified the type... but not the field name. That's just like defining a structure in this way
struct foo {
int;
}
So, basically, what you need is a field name:
struct bankAcct{
int amount;
enum typeOfAcc{chck = 0, saving = 1} type;
int balance;
}
You will be able to access it with
struct bankAcct var;
printf("%d\n", var.type);
Addendum
I would not recommend defining an enumeration inside a struct, first of all for readability reasons. Another reason might be incompatibility with C++: I wasn't able to compile an example C++ code in which the symbols of the inner enum were accessed. The following assignment
struct bankAcct var;
var.type = chck;
raised an error on gpp because the symbol chck could not be referenced outside the struct definition scope. Even assigning an integer to the enum field lead the compiler to complain, and I could not even perform the casting
b.type = (enum typeOfAcc) 1;
because an error was raised: the enum typeOfAcc wasn't be accessible as well.
But in C these assignments would be ok, and both enum tags and constant identifiers (named and anonymous) would be "reachable". As explained in C specification 6.2.1§4, the scope of an identifier outside any code block is the whole translation unit:
Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit.
Just for completeness, after saying where the scope ends, we must say where the scope begins (C specification 6.2.1§7):
Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.
You should declare the enum type outside the structure, then declare a member variable with that type.
typedef enum {
chck = 0,
saving = 1
} accountType;
struct bankAcct{
int amount;
accountType typeOfAcc ;
int balance;
}
Why are enum values accessible outside the block in which enum is defined in C, but not in C++?
Consider the following C program.
#include <stdio.h>
struct mystruct
{
enum {INT, FLOAT, STRING} type;
int integer;
float floating_point;
} tu;
/* Why is INT accessible here? */
int main()
{
tu.type = INT;
tu.integer = 100;
return 0;
}
It compiles and runs fine in C.
But in C++ it fails in compilation.
#include <iostream>
struct mystruct
{
enum {INT, FLOAT, STRING} type;
int integer;
float floating_point;
} tu;
/* Why is INT accessible here? */
int main()
{
tu.type = INT;
tu.integer = 100;
return 0;
}
[Error] 'INT' was not declared in this scope
Are enum and scope rules different in C and C++?
In C, there is simply no rule for scope for enums and struct. The place where you define your enum doesn't have any importance.
In C++, define something inside another something (like an enum in a class) make this something belong to the another something.
If you want to make your enum global in C++, you will have to define it outside your class, or access from your struct path:
#include <iostream>
struct mystruct
{
enum {INT, FLOAT, STRING} type;
int integer;
float floating_point;
} tu;
int main()
{
tu.type = mystruct::INT; // INT is not in global scope, I have to precise it.
tu.integer = 100;
return 0;
}
Note: This works in this exemple, because you are using a struct, where everything is public by default. Be careful; you can access your enum type and values from outside your struct or your class only if the enum is in a public scope, as any field or function.
The main difference is that opposite to C, C++ has a class scope.
In C (6.2.1 Scopes of identifiers)
4 Every other identifier has scope determined by the placement of its
declaration (in a declarator or type specifier). If the declarator or
type specifier that declares the identifier appears outside of any
block or list of parameters, the identifier has file scope, which
terminates at the end of the translation unit.
Thus in this program
#include <stdio.h>
struct mystruct
{
enum {INT, FLOAT, STRING} type;
int integer;
float floating_point;
} tu;
/* Why is INT accessible here? */
int main()
{
tu.type = INT;
tu.integer = 100;
return 0;
}
Enumerators INT, FLOAT, STRING are declared outside any block scope and therefore have the file scope.
In C++ there is defined a separate scope - class scope:
3.3.7 Class scope
1 The following rules describe the scope of names declared in classes.
1) The potential scope of a name declared in a class consists not only
of the declarative region following the name’s point of declaration,
but also of all function bodies, default arguments,
exception-specifications, and brace-or-equal-initializers of
non-static data members in that class (including such things in nested
classes).
and
2 The name of a class member shall only be used as follows:
— in the scope of its class (as described above) or a class derived
(Clause 10) from its class,
— after the . operator applied to an expression of the type of its
class (5.2.5) or a class derived from its class,
— after the -> operator applied to a pointer to an object of its class
(5.2.5) or a class derived from its class,
— after the :: scope resolution operator (5.1) applied to the name of
its class or a class derived from its class.
Take into account that (9.2 Class members)
1 ...Members of a class are data members, member functions (9.3),
nested types, and enumerators.
Thus in this program
#include <iostream>
struct mystruct
{
enum {INT, FLOAT, STRING} type;
int integer;
float floating_point;
} tu;
/* Why is INT accessible here? */
int main()
{
tu.type = INT; // Invalid access of class member
tu.integer = 100;
return 0;
}
You shall access class member INT in one of the following ways.
tu.type = mystruct::INT;
or
tu.type = tu.INT;
or even like
tu.type = ( &tu )->INT;
The answers given by Vlad and Arachtor are good as far as they go, but there is a question they do not address: why C++ does it differently. If someone is familiar with Stroustrup's book, they may be able to improve this, but I suppose:
C was designed long ago to be fairly easy to compile, while C++ aims to make programming more reliable by using OO, whose main ideal is “in one place, tell the user of a construct all they need to know to use it and no more”; this has the often unspoken benefit of bringing together what belongs together.
This leads to the decisions to use type definitions to limit the scope of some definitions, and to place constructs within a hierarchy of namespaces.
By restricting the scope of a nested enum it is possible to use shorter names without risk of ambiguity or clashes.
Quoting from here,
In C, there are two different namespaces of types: a namespace of struct/union/enum tag names and a namespace of typedef names.
name.c
$ cat name.c
#include<stdio.h>
typedef long long long2;
int long2 () {
return 4;
}
int main() {
printf("hello, world!");
return 0;
}
$ gcc name.c -o name
name.c:4: error: 'long2' redeclared as different kind of symbol
name.c:3: error: previous declaration of 'long2' was here
$
name2.c
$ cat name2.c
#include<stdio.h>
int four() {
return 4;
}
struct dummy {
int member;
};
int main() {
struct dummy four;
}
$ gcc name2.c -o name2
$
I am trying to understand C namespace conflicts.
In the first case, why is there a conflict? Do functions also belong to the typedef namespace?
In the second case, why is there no conflict at all? The function and the variable both are named four. Why does the compiler allow that? How is &four supposed to be resolved?
C has four different name spaces for identifiers:
Label names (the goto type).
Tags (names of structures, unions and enumerations).
Members of structures and unions (these have a separate namespace per structure/union).
All other identifiers (function names, object names, type(def) names, enumeration constants, etc).
See also C99 6.2.3.
So your two question can be answered as:
Yes, function names and typedef names share the same name space.
No conflict, because the compiler will use scope rules (for function or object names). The identifier in main is said to shadow the global function name, something your compiler will warn you about if you set the warning levels high enough.
But the crucial point on your examples isn't about namespace, but the scope of the names.
In name.c, both long2 are "ordinary identifiers" (share the same name space), and both of them are defined in the same scope, so there is a conflict. (C99 §6.7/3)
If name2.c, the local variable four is in a scope deeper than the function four, so the variable hides the function four (C99 §6.2.1/4).
Your 2nd example does not show "no conflict". There is a conflict! Try this:
#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
struct dummy four;
four.member = four();
}
And now this
#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
int (*fx)(void) = four; /* "save" function */
struct dummy four; /* hide it */
four.member = fx(); /* use "hidden" fx */
}
In your 2nd example, the variable four hides the function four().
To answer your two questions quick.
Q1. In the first case, why is there a conflict? Do functions also belong to the typedef namespace?
A1. Yes, identifiers for functions and typedef'ed types share the same namespace. So there's a conflict.
Q2. In the second case, why is there no conflict at all? The function and the variable both are named four. Why does the compiler allow that? How is &four supposed to be resolved?
A2. There's no conflict in your 2nd example even though the two identifiers named four belong to the same identifier namesapce, which is Ordinary namespace. Because the variable identifier four in the main is in a function scope, whereas the function identifier four in a file scope. So the latter one is hidden by the former one as the basic scope rule. If you move the variable four to the file scope(global) like below, you'll see an error.
...
struct dummy four; // Error! there's a previous definition
// in this file scope(global).
int main() {
// struct dummy four; // Commented out.
}
Actually, there are four different identifier namespaces: Label, Tag, Member, and Ordinary. You can find details about them at https://en.cppreference.com/w/c/language/name_space