38

In the following code, are the functions test and test2 equivalent?

typedef int rofl;

void test(void) {
    rofl * rofl = malloc(sizeof(rofl)); // Is the final rofl here the TYPE?
}

void test2(void) {
    rofl * rofl = malloc(sizeof *rofl); // Is the final rofl here the VARIABLE?
}

In other words:

  1. Does rofl in sizeof(rofl) correctly pick the rofl type because of the parentheses?
  2. Does rofl in sizeof *rofl correctly pick the rofl variable because of a lack of parentheses?

Note: This is a silly-looking example, but it can actually happen in practice that you have a type name that's the same as a variable name. Hence the question.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
ccoder987
  • 421
  • 3
  • 6
  • 8
    Why did people vote this down? It is a perfectly reasonable question, and the answer is not clear given that C does have multiple name spaces, so some names can have multiple visible meanings, so you cannot answer this question definitively unless you specifically know that typedef names and object names share the same name space and can hide each other. – Eric Postpischil Nov 15 '17 at 01:43
  • 2
    The short answer is no, the parentheses make no difference. `sizeof` is an operator, not a function, so the parentheses are merely redundant. – user207421 Nov 15 '17 at 01:47
  • 2
    By the way, to answer your question 2 explicitly, yes, the lack of parentheses forces the `sizeof` operand to have only one option: It must be a “unary-expression” of some sort, so it cannot be a typedef name. In regard to question 1, the parentheses do not **cause** the compiler to require a typedef name. The thing in parentheses, if it is just an identifier, can only validly be the name of one typedef, function, or object at that point, so the compiler will only find one resolution; it does not need to choose. – Eric Postpischil Nov 15 '17 at 01:48
  • @EricPostpischil You've contradicted yourself. Either the parentheses make a difference or they don't. *unary-expression* can be an *identifier*. – user207421 Nov 15 '17 at 01:55
  • 4
    @EJP: There is no contradiction. When parentheses are present, two syntactic options are possible. When parentheses are absent, there is only one syntactic option. With an identifier in parentheses, `sizeof (foo)` can match either the “`sizeof` *unary-expression*” shown in clause 6.5.3 of the C standard or the “`sizeof ( ` *type-name* `)`” in the same clause. The match is then determined by which meaning the identifier has, not by local syntax. Without parentheses, only the first is possible. – Eric Postpischil Nov 15 '17 at 01:59
  • [Do parentheses make a difference when determining the size of an array?](https://stackoverflow.com/q/30557710) [Why (and when) do I need to use parentheses after sizeof?](https://stackoverflow.com/q/5894892) – Bernhard Barker Nov 15 '17 at 06:00
  • It would've been a good idea to start with less convoluted examples just to (separately) see exactly how sizeof and scoping works. – Bernhard Barker Nov 15 '17 at 06:04
  • @EricPostpischil With over 46k rep, you should have seen the "Avoid answering questions in comments" many times by now. It just leads to long and convoluted comment threads, with no means to accept a correct answer or to downvote a bad one. – pipe Nov 15 '17 at 12:02
  • If you have type names which are the same as variable names, one needs to be changed. – Bob Jarvis - Слава Україні Nov 15 '17 at 12:49
  • @pipe: The questions I answered were not actually the main question posted but a rewording (“in other words”). As the main question had already been answered reasonably well, I judged that posting a different answer addressing technicalities raised by the alternate wording would be more of a distraction than a comment. – Eric Postpischil Nov 15 '17 at 13:02
  • @pipe: If a 46k rep is defying it it calls the rule into question. – Joshua Nov 15 '17 at 14:38
  • Note that after the line shown in each function, there is no way to introduce another variable of _type_ `rofl` in the same scope; the variable name hides the type name. Also note that if the typedef were in the function, the variable could not be defined in the same scope. (That's moderately carefully worded to allow a nested scope to (re)define the type or define variables of the same name as the type in the outer scope.) – Jonathan Leffler Nov 21 '17 at 22:57

3 Answers3

27

In both cases, the last rofl is the variable name. A variable name is in scope as soon as it appears; and for the remainder of the current scope, that identifier in an ordinary context(*) always means the variable name.

The sizeof operator does not introduce any special cases for name lookup. In fact, there are no language constructs that will use the hidden meaning of an identifier.

In practice it is a good idea to not use the same identifier for a type and a variable name.


(*) There are three special contexts for identifiers: label names, structure tags, and structure members. But in all other contexts, all identifiers share a common name space: there are no distinct identifier name spaces for type names versus variable names versus function names etc.

Here is a contrived example:

typedef int A;      // "A" declared as ordinary identifier, meaning a type name

struct A { A A; };  // "A" declared as struct tag and member name -- OK as these are three different name spaces. Member type is "int"

A main()            // int main() - ordinary context
{
    struct A A();   // "A" declared as ordinary identifier, meaning a function name; hides line 1's A
    // A C;         // Would be error: ordinary A is a function now, not a typedef for int
    struct A B;     // OK, struct tags have separate name space
    A:+A().A;       // OK, labels and struct members have separate name space, calls function
    goto A;         // OK, label name space
}
M.M
  • 138,810
  • 21
  • 208
  • 365
  • You should explain explicitly that `rolf` as an identifier (name) for an object hides `rolf` as an identifier for a typedef name. This is not clear because C does have multiple name spaces. Its use as a label, for example, would not hide the use for an object. A person could think “sizeof(rofl)” could parse as the size of the typedef name or the size of the object; they could both be visible of they are in separate name spaces. How does the compiler pick? But if we explain that the typedef name is not visible because of the hiding in the same name space, then it is clear how it parses. – Eric Postpischil Nov 15 '17 at 01:39
  • BTW, the C standard uses “hide,” rather than “shadow.” – Eric Postpischil Nov 15 '17 at 01:41
  • @EricPostpischil Re. your first comment, my text "for the remainder of the current scope, that identifier always means the variable name" does convey that already? I even tried to reinforce this in the second paragraph saying that the hidden meaning is never used. – M.M Nov 15 '17 at 01:45
  • 1
    Well, the statement “that identifier always means the variable name” is not actually true. An identifier can simultaneously be the name of an object, the name of a label, the name of several struct or union members, and a tag of a structure, union, or enumeration. These possibilities are distinguished by syntactic context. So this answer is not really explaining how C name spaces work in the context of the question—the OP’s question essentially asks whether the two syntactic forms of `sizeof` distinguish type names from object names… – Eric Postpischil Nov 15 '17 at 01:53
  • … So I think it could be helpful to clarify that, although C has multiple name spaces for identifiers, typedef names and object names do share one name space (along with function names), so one can hide the other, unlike labels. – Eric Postpischil Nov 15 '17 at 01:54
  • @EricPostpischil I added from further explanation, thanks for pointing out the issue – M.M Nov 15 '17 at 01:56
13

In this declaration

rofl * rofl = malloc(sizeof(rofl)); // Is the final rofl here the TYPE?

the name of the variable rofl hides the typedef name rofl. Thus in the sizeof operator there is used the pointer rofl that is the expression has the type int *.

The same is valid for this declaration

rofl * rofl = malloc(sizeof *rofl); 

except that there is used an expression with the dereferenced pointer rofl that has the type of the typedef name rofl that is the type int.

It seems that the confusion arises due to this C grammar definition

sizeof unary-expression
sizeof ( type-name )

However unary-expression can be a primary expression that is an expression enclosed in parentheses.

From the C Standard (6.5.1 Primary expressions)

primary-expression:
    ( expression )
    //...

So for example if x is a name of a variable then you may write either

sizeof x 

or

sizeof( x )

For clarity you could insert blanks between the sizeof operator and the primary expression

sizeof    ( x )
operator  primary expression

For comparison consider another unary operator: the unary plus. You can write for example

+ x

or

+ ( x )

Now just substitute the unary plus for another unary operator sizeof.

As for hiding names the problem is resolvable for structures, unions and enumerations because their names include keywords for tags.

For example

typedef struct rofl { int x; } rofl;

void test(void) {
    rofl * rofl = malloc(sizeof( struct rofl));
}

In this function with the sizeof operator there is used type-name struct rofl.

While in this function

typedef struct rofl { int x; } rofl;

void test(void) {
    rofl * rofl = malloc(sizeof( rofl));
}

with the sizeof operator there is used a primary expression with the variable rofl, that has the type struct rofl *.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

There is no "picking" or "choosing" involved. In both cases the rofl referred to in each sizeof invocation is the variable, not the type, due to scoping rules. The variable is declared at inner scope, and thus overrides the type name. The parenthesization of the argument to the sizeof operator is irrelevant.

Best of luck.