Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #163260 > unrolled thread

on confusing procedure names with the address of procedures

Started byMeredith Montgomery <mmontgomery@levado.to>
First post2021-10-29 21:37 -0300
Last post2021-11-01 13:03 +0100
Articles 9 — 7 participants

Back to article view | Back to comp.lang.c


Contents

  on confusing procedure names with the address of procedures Meredith Montgomery <mmontgomery@levado.to> - 2021-10-29 21:37 -0300
    Re: on confusing procedure names with the address of procedures Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-10-29 18:29 -0700
    Re: on confusing procedure names with the address of procedures Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-10-29 18:33 -0700
    Re: on confusing procedure names with the address of procedures Manfred <noname@add.invalid> - 2021-10-30 05:32 +0200
      Re: on confusing procedure names with the address of procedures James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-10-30 01:26 -0400
        Re: on confusing procedure names with the address of procedures Manfred <noname@add.invalid> - 2021-10-30 19:05 +0200
          Re: on confusing procedure names with the address of procedures James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-10-30 15:17 -0400
    Re: on confusing procedure names with the address of procedures Bart <bc@freeuk.com> - 2021-10-30 11:36 +0100
    Re: on confusing procedure names with the address of procedures Noob <root@127.0.0.1> - 2021-11-01 13:03 +0100

#163260 — on confusing procedure names with the address of procedures

FromMeredith Montgomery <mmontgomery@levado.to>
Date2021-10-29 21:37 -0300
Subjecton confusing procedure names with the address of procedures
Message-ID<86bl37e49v.fsf@levado.to>
I wrote

--8<---------------cut here---------------start------------->8---
int main()
{
  char c1;
  
  printf("c1 is at %p\n", &c1);
  printf("and the addr of the addr of c1 is at %p\n", &&c1);
}
--8<---------------cut here---------------end--------------->8---

and I didn't this to make any sense.  Indeed, I get

--8<---------------cut here---------------start------------->8---
%make nonsense
gcc     nonsense.c   -o nonsense
nonsense.c: In function 'main':
nonsense.c:12:3: error: label 'c1' used but not defined
   12 |   printf("c1 is at %p\n", &&c1);
      |   ^~~~~~
make: *** [<builtin>: nonsense] Error 1
%
--8<---------------cut here---------------end--------------->8---

The error message totally lost me.

But then I wrote

--8<---------------cut here---------------start------------->8---
#include <stdio.h>
int main()
{
  printf("main is at %p\n", main);
  printf("main is at %p\n", &main);
}
--8<---------------cut here---------------end--------------->8---

and I expected a compilation error of some sort, but I got

--8<---------------cut here---------------start------------->8---
%CFLAGS=-Wall make main-addr
gcc -Wall    main-addr.c   -o main-addr

%./main-addr.exe
main is at 00007ff617431594
main is at 00007ff617431594
%
--8<---------------cut here---------------end--------------->8---

So main must be stored at 00007ff617431594 and so it becomes unintuitive
that getting the address of main yields the same value.  

My interpretation of this last program is that

  main = &main

but for the first program

  &c1 =/= & &c1

because the right side is nonsensical?  It's trying to get the address
of an address, when we should only get the address of a name.  Can't get
the address of numbers.

Pretty confusing.

[toc] | [next] | [standalone]


#163262

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-10-29 18:29 -0700
Message-ID<878rybz4dv.fsf@nosuchdomain.example.com>
In reply to#163260
ram@zedat.fu-berlin.de (Stefan Ram) writes:
> Meredith Montgomery <mmontgomery@levado.to> writes:
>>printf("and the addr of the addr of c1 is at %p\n", &&c1);
>
>   The operand of the unary & operator shall be either a
>   function designator, the result of a [] or unary * operator,
>   or an /lvalue/ that designates an object that is * not a
>   bit-field and is not declared with the register
>   storage-class specifier.
>
>   The unary & operator yields the /address/ of its operand.
>   Such an address is a mere data value, not an object.
>   It is not an lvalue. So it does not have an address.
>   So, one cannot use "&&c1".
>
>   Therefore, the GNU compiler uses "&&" as its notation to
>   take the address of a /label/, but this is not standard C.

And gcc does this because "&&" is already a token (the logical "and"
operator).  Since C uses the "maximal munch" rule, the sequence
    &&c
is tokenized as "&&" followed by "c".  If it were legal to apply unary "&"
to a unary "&" operation you'd have to write it as:
    & &c
or
    &(&c)

That would probably give you (Meredith) an error message closer to what
you were expecting.

The "maximal munch" rule is why
    x+++++y
is tokenized as
    x ++ ++ + y
(a syntax error) rather than as
    x ++ + ++ y
(which could be a valid expression).

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#163263

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-10-29 18:33 -0700
Message-ID<86fssje1p7.fsf@linuxsc.com>
In reply to#163260
Meredith Montgomery <mmontgomery@levado.to> writes:

> I wrote
>
> [..various confusions..]

I recommend:

One: get in the habit of running gcc in a conforming mode -

    gcc -x c -std=c99 -pedantic-errors <whatever>.c

Two: get a copy of the C standard (or a close approximation thereto)

    wget http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

(This link is for a corrected draft of C99.  It isn't the latest
version of C but for starters it is an excellent reference.)

Three: read section 6.3.2.1 paragraphs 1, 3, and 4.

Four: read section 6.5.3.2

Five: after reading the above and looking again at your code,
if you are still confused ask another question here.

Six: in general, if there is ever a question or confusion you
have about C, first try to find the answer yourself in the
document whose link is given above.  If you feel you still
need help then ask in the newsgroup here.

[toc] | [prev] | [next] | [standalone]


#163265

FromManfred <noname@add.invalid>
Date2021-10-30 05:32 +0200
Message-ID<sliebn$16l6$1@gioia.aioe.org>
In reply to#163260
On 10/30/2021 2:37 AM, Meredith Montgomery wrote:
> I wrote
> 
> --8<---------------cut here---------------start------------->8---
> int main()
> {
>    char c1;
>    
>    printf("c1 is at %p\n", &c1);
>    printf("and the addr of the addr of c1 is at %p\n", &&c1);
> }
> --8<---------------cut here---------------end--------------->8---
> 
> and I didn't this to make any sense.  Indeed, I get
> 
> --8<---------------cut here---------------start------------->8---
> %make nonsense
> gcc     nonsense.c   -o nonsense
> nonsense.c: In function 'main':
> nonsense.c:12:3: error: label 'c1' used but not defined
>     12 |   printf("c1 is at %p\n", &&c1);
>        |   ^~~~~~
> make: *** [<builtin>: nonsense] Error 1
> %
> --8<---------------cut here---------------end--------------->8---
> 
> The error message totally lost me.
> 
> But then I wrote
> 
> --8<---------------cut here---------------start------------->8---
> #include <stdio.h>
> int main()
> {
>    printf("main is at %p\n", main);
>    printf("main is at %p\n", &main);
> }
> --8<---------------cut here---------------end--------------->8---
> 
> and I expected a compilation error of some sort, but I got
> 
> --8<---------------cut here---------------start------------->8---
> %CFLAGS=-Wall make main-addr
> gcc -Wall    main-addr.c   -o main-addr
> 
> %./main-addr.exe
> main is at 00007ff617431594
> main is at 00007ff617431594
> %
> --8<---------------cut here---------------end--------------->8---
> 
> So main must be stored at 00007ff617431594 and so it becomes unintuitive
> that getting the address of main yields the same value.

In expressions where the function name is used (but not as a call to 
that function, so not main()), the function is automatically converted 
to a pointer to function (similar to arrays that decay to pointers in 
many circumstances). So using main and &main as argument to printf 
indeed yields the same result: the address of main.
The same applies to the inverse case, if you have a pointer to function:

#include <stdio.h>

void bar()
{
   printf("hello\n");
}

int main()
{
   void (*foo)() = bar;

   // You can execute the call in both of the following ways:

   foo(); // the pointer is implicitly dereferenced
   (*foo)(); //  the pointer is explicitly dereferenced
}

> 
> My interpretation of this last program is that
> 
>    main = &main
> 
> but for the first program
> 
>    &c1 =/= & &c1
> 
> because the right side is nonsensical?  It's trying to get the address
> of an address, when we should only get the address of a name.  Can't get
> the address of numbers.

As a rationale, addresses represent memory locations (for functions it 
is a bit more complicated than that, so let's stick to data for this 
second part). This means that an address may be taken from objects that 
have some memory storage, e.g. c1.
The result of &c1 is indeed a pure value which has no memory storage, so 
its address cannot be taken (in fact it may be physically stored in a 
register and not in any memory location, but even if it were stored 
somewhere in memory, you would miss some identification of that memory 
location in order to get its address).

> 
> Pretty confusing.
> 

Yet pointers and addresses are probably amongst the most powerful 
features of C.

[toc] | [prev] | [next] | [standalone]


#163266

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-10-30 01:26 -0400
Message-ID<slil2c$pke$1@dont-email.me>
In reply to#163265
On 10/29/21 11:32 PM, Manfred wrote:
...
> In expressions where the function name is used (but not as a call to 
> that function, so not main()), the function is automatically converted 
> to a pointer to function (similar to arrays that decay to pointers in 
> many circumstances).

The rule you're referring to is "A function designator is an expression
that has function type. Except when it is the operand of the sizeof
operator, 69) or the unary & operator, a function designator with type
"function returning type" is converted to an expression that has type
"pointer to function returning type"." (6.3.2.1p4).
The "69)" refers to footnote 69, which points out that "Because this
conversion does not occur, the operand of the sizeof operator remains a
function designator and violates the constraints in 6.5.3.4."

Note that the only exceptions to that rule are for sizeof and &; there
is no exception to that rule for actual function calls. That's good,
because the standard imposes a constraint: "The expression that denotes
the called function 102) shall have type pointer to function returning
void or returning a complete object type other than an array type."
(6.5.5.2p1). If it weren't for that automatic conversion, a call to main
would require the following syntax: "(&main)()".

Note that this is a change that occurred in C90. K&R C had no such
conversion, and specified that "A function call is a primary expression
followed by parentheses ... The primary expression must be of type
"function returning ...""
The conversion and the constraint were both introduced when C was first
standardized, so that main() remained a valid function call, albeit for
different reasons, both in K&R C and in C90.

The purpose of the change was to simplify the use of function pointers.
Before the change, they had to be called using (*function_pointer)().
That remains a valid expression, since *function_pointer is an
expression of function type, which automatically gets converted back to
a pointer, but they can now also be called more directly by using
function_pointer().

...
>    void (*foo)() = bar;
> 
>    // You can execute the call in both of the following ways:
> 
>    foo(); // the pointer is implicitly dereferenced

That hasn't been true since C90.

[toc] | [prev] | [next] | [standalone]


#163270

FromManfred <noname@add.invalid>
Date2021-10-30 19:05 +0200
Message-ID<slju07$1ghj$1@gioia.aioe.org>
In reply to#163266
On 10/30/2021 7:26 AM, James Kuyper wrote:
> On 10/29/21 11:32 PM, Manfred wrote:
> ...
>> In expressions where the function name is used (but not as a call to
>> that function, so not main()), the function is automatically converted
>> to a pointer to function (similar to arrays that decay to pointers in
>> many circumstances).
> 
> The rule you're referring to is "A function designator is an expression
> that has function type. Except when it is the operand of the sizeof
> operator, 69) or the unary & operator, a function designator with type
> "function returning type" is converted to an expression that has type
> "pointer to function returning type"." (6.3.2.1p4).
> The "69)" refers to footnote 69, which points out that "Because this
> conversion does not occur, the operand of the sizeof operator remains a
> function designator and violates the constraints in 6.5.3.4."
> 
> Note that the only exceptions to that rule are for sizeof and &; there
> is no exception to that rule for actual function calls. That's good,
> because the standard imposes a constraint: "The expression that denotes
> the called function 102) shall have type pointer to function returning
> void or returning a complete object type other than an array type."
> (6.5.5.2p1). If it weren't for that automatic conversion, a call to main
> would require the following syntax: "(&main)()".
> 
> Note that this is a change that occurred in C90. K&R C had no such
> conversion, and specified that "A function call is a primary expression
> followed by parentheses ... The primary expression must be of type
> "function returning ...""
> The conversion and the constraint were both introduced when C was first
> standardized, so that main() remained a valid function call, albeit for
> different reasons, both in K&R C and in C90.
> 
> The purpose of the change was to simplify the use of function pointers.
> Before the change, they had to be called using (*function_pointer)().
> That remains a valid expression, since *function_pointer is an
> expression of function type, which automatically gets converted back to
> a pointer, but they can now also be called more directly by using
> function_pointer().
> 

Thanks for the detailed reference to the standard.

> ...
>>     void (*foo)() = bar;
>>
>>     // You can execute the call in both of the following ways:
>>
>>     foo(); // the pointer is implicitly dereferenced
> 
> That hasn't been true since C90.
> 

I guess you mean that the pointer is not "dereferenced", but still the 
expression is a valid call to the function, so the statement "You can 
execute the call in both ways" still holds.

[toc] | [prev] | [next] | [standalone]


#163273

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-10-30 15:17 -0400
Message-ID<slk5nr$km2$1@dont-email.me>
In reply to#163270
On 10/30/21 1:05 PM, Manfred wrote:
> On 10/30/2021 7:26 AM, James Kuyper wrote:
>> On 10/29/21 11:32 PM, Manfred wrote:
...
>>>     void (*foo)() = bar;
>>>
>>>     // You can execute the call in both of the following ways:
>>>
>>>     foo(); // the pointer is implicitly dereferenced
>>
>> That hasn't been true since C90.
>>
> 
> I guess you mean that the pointer is not "dereferenced", ...

Correct.

> ... but still the 
> expression is a valid call to the function, so the statement "You can 
> execute the call in both ways" still holds.

Yes. I didn't think about the possibility that I could have been
interpreted as disagreeing with both comments.

[toc] | [prev] | [next] | [standalone]


#163267

FromBart <bc@freeuk.com>
Date2021-10-30 11:36 +0100
Message-ID<slj77j$ufg$1@dont-email.me>
In reply to#163260
On 30/10/2021 01:37, Meredith Montgomery wrote:

>     12 |   printf("c1 is at %p\n", &&c1);


&& is used for label address with some C implementations. Label c1 
doesn't exist. (Variable c1 does, but in C, variables and labels live in 
different namespaces; the same names can coexist).


>    printf("main is at %p\n", main);
>    printf("main is at %p\n", &main);
> }


If you have a function F, then:

    F()             Calls the function
    F               Yields its address
    &F              Yields its address

So F and &F do the same thing. In effect, the first line could also be 
written as:

    (&F)()          Calls the function

[toc] | [prev] | [next] | [standalone]


#163276

FromNoob <root@127.0.0.1>
Date2021-11-01 13:03 +0100
Message-ID<slol20$rh5$1@dont-email.me>
In reply to#163260
On 30/10/2021 02:37, Meredith Montgomery wrote:

> gcc     nonsense.c   -o nonsense
> nonsense.c: In function 'main':
> nonsense.c:12:3: error: label 'c1' used but not defined
>    12 |   printf("c1 is at %p\n", &&c1);
>       |   ^~~~~~

As Tim pointed out, your command-line lacks the proper flags to put
GCC in (mostly) conformant mode, and print useful diagnostics.

https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

CFLAGS = -std=c17 -pedantic-errors -Wpedantic -Wall -Werror

Regards.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web