References in C

J

jacob navia

The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of language
design this days, lcc-win considers C not a dead language but a language
that can evolve and propose new features.

In this context, one of the extensions that lcc-win supports is
references, where the design is largely inspired from the C++ design.


What are references?

In fact, a reference is just a pointer. It is different from plain
pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
int i = 23;
int &IntReference = i;

As always in this proposals, the problems hide in the details. For
instance, several days ago someone complained that a reference to
void would not compile, but a pointer to void would. This prompted
me to correct a problem in my implementation: I should have forbidden to
build references to void!

The problem is when defining a reference but when you pass a reference
to a function:
int fn(void &ref);
In this case there is no initialization (that would explode immediately)
but just a definition without any initialization. The compiler must
check, then, that the reference is not to a void pointer.

Since the object pointed to by a reference can't change, many people
think that the value can't never change but that is wrong. Consider:

#include <stdio.h>
int main(void)
{
int i = 23;
int &pi = i;

i = 3;
printf("%d\n",pi);
}

The reference is initialized to an integer whose value is 23.
If the integer changes its value, the reference (like all other
pointers) changes its value also.

The advantages of using references are that they are never NULL,
and that they always point to the same object, two important
things less to test. The only (orthodox) way of declaring a non-NULL
pointer in an argument list is now:

int fn(int arg[static 1]);

This declares that the "arg" argument will have at least one integer
Few people know this, and it wasn't much explained when C99 introduced
it.

jacob
 
B

BGB

The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of language
design this days, lcc-win considers C not a dead language but a language
that can evolve and propose new features.

In this context, one of the extensions that lcc-win supports is
references, where the design is largely inspired from the C++ design.

a partial risk though is that C + bunches-of-extensions is no longer C,
rather, another C derived language (in a similar manner to C++ or
Objective-C, which are also not strictly C).

What are references?

In fact, a reference is just a pointer. It is different from plain
pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
int i = 23;
int &IntReference = i;

As always in this proposals, the problems hide in the details. For
instance, several days ago someone complained that a reference to
void would not compile, but a pointer to void would. This prompted
me to correct a problem in my implementation: I should have forbidden to
build references to void!

The problem is when defining a reference but when you pass a reference
to a function:
int fn(void &ref);
In this case there is no initialization (that would explode immediately)
but just a definition without any initialization. The compiler must
check, then, that the reference is not to a void pointer.

Since the object pointed to by a reference can't change, many people
think that the value can't never change but that is wrong. Consider:

#include <stdio.h>
int main(void)
{
int i = 23;
int &pi = i;

i = 3;
printf("%d\n",pi);
}

The reference is initialized to an integer whose value is 23.
If the integer changes its value, the reference (like all other
pointers) changes its value also.

The advantages of using references are that they are never NULL,
and that they always point to the same object, two important
things less to test. The only (orthodox) way of declaring a non-NULL
pointer in an argument list is now:

int fn(int arg[static 1]);

This declares that the "arg" argument will have at least one integer
Few people know this, and it wasn't much explained when C99 introduced
it.

going OT:

interestingly, in my own current language (more closely related to
JavaScript and ActionScript), I ended up giving references a "look and
feel" more like that of C pointers.

basically, the caller needs to use "&" to pass an argument to a
reference, and by default (no '&' in the argument list) it would be
needed to use '*' to access/assign them. if '&' is given, it means to
insert an implicit '*' on access, and later could also insert basic
sanity checks (barf if the argument is not "sane" when calling the
function).

as-is, I figured doing calls like "foo(&x);" to be a reasonable
trade-off (it serves a similar role to 'ref' in C#).

note that, unlike C, these are not raw pointers, rather a
boxed-pointer-type (some languages use the term "locative" for these
instead of "pointer" or "reference").
 
I

Ian Collins

a partial risk though is that C + bunches-of-extensions is no longer C,
rather, another C derived language (in a similar manner to C++ or
Objective-C, which are also not strictly C).

C++--?
 
B

BartC

#include <stdio.h>
int main(void)
{
int i = 23;
int &pi = i;

i = 3;
printf("%d\n",pi);
}

This doesn't compile in my lcc-win32 unless I make i static ("addressable
object required" in the int &pi line).

Is this a restriction, or do I need an update?
 
S

Shao Miller

The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of language
design this days, lcc-win considers C not a dead language but a language
that can evolve and propose new features.
...
In fact, a reference is just a pointer. It is different from plain pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
int i = 23;
int &IntReference = i;

(I think you meant "four ways". You must have started with 2 and then
remembered 2 more. ;) )

So I'm guessing that if you have:

void foo(struct xxx & bar) { /* ... */ }

If you have an object with "allocated" storage duration, you'd do:

struct xxx * bar;
/* ...See below... */
foo(*bar_ptr);

in order for 'foo' to work with the pointed-to object, right?

But before you perform the call, you must:
- Allocate the object
- Check that the allocation succeeds

If you do these things before calling 'foo', then 'foo' needn't check
for a null pointer, right? For:

void foo(struct xxx * bar) { /* ... */ }

"static" and "automatic" objects can have their addresses passed with
'&', right?

So this seems to be along the lines of #1, above.

Then there's also:

void foo(struct xxx * const bar) { /* ... */ }

and:

int i;
int * const ip = &i;

which appear to be along the lines of #2, above.

Am I understanding these two points correctly?
 
T

Tom St Denis

The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of language
design this days, lcc-win considers C not a dead language but a language
that can evolve and propose new features.

In this context, one of the extensions that lcc-win supports is
references, where the design is largely inspired from the C++ design.

What are references?

In fact, a reference is just a pointer. It is different from plain
pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
    be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
     int i = 23;
     int &IntReference = i;

Simipler...

int func(int *ledata)
{
#define reflefdata (*ledata)
int a;

a = (refledata *= 4);
return a;
}

There, no more lazies.

Also no language change required. You can do things like sizeof
refledata, you can read/assign it, even take the address of it.

Tom
 
D

David Resnick

Simipler...

int func(int *ledata)
{
#define reflefdata (*ledata)
   int a;

   a = (refledata *= 4);
   return a;

}

There, no more lazies.

While I'm not sold on having references in C (not clear the it is THAT
big a win), this macro crud isn't the same thing as having a ref.
Main virtue of references is self-documentation and automated function
argument semantics enforcement IMHO...

i.e. in C you might have:

/* Note: ledata absolutely CAN NOT and MUST NOT be NULL here */
int func(int *ledata)
{
if ( ledata == NULL )
{
/* do something drastic */
}
}

You can express all that extra material without the comments, checks,
whatever, with the one little '&' in the

int func( int &ledata )

Earth shattering? No, but it is sometimes nice.

-David
 
J

jacob navia

Le 24/06/11 16:14, David Resnick a écrit :
While I'm not sold on having references in C (not clear the it is THAT
big a win), this macro crud isn't the same thing as having a ref.
Main virtue of references is self-documentation and automated function
argument semantics enforcement IMHO...

i.e. in C you might have:

/* Note: ledata absolutely CAN NOT and MUST NOT be NULL here */
int func(int *ledata)
{
if ( ledata == NULL )
{
/* do something drastic */
}
}

You can express all that extra material without the comments, checks,
whatever, with the one little '&' in the

int func( int&ledata )

Earth shattering? No, but it is sometimes nice.

-David

Precisely. It is not the best thing since baked bread but it is an
improvement that implies a small change in the language.
 
J

jacob navia

Le 24/06/11 12:14, BartC a écrit :
This doesn't compile in my lcc-win32 unless I make i static
("addressable object required" in the int &pi line).

Is this a restriction, or do I need an update?

I think you need an update... I introduced this several years
ago... I just compiled that and it works without any problems.

Please do not use it with -ansic though.
 
T

Tom St Denis

Le 24/06/11 16:14, David Resnick a écrit :










Precisely. It is not the best thing since baked bread but it is an
improvement that implies a small change in the language.

I can't see a reason to not have it, but just the same I can't say
that my programming career has been taking back by it. What I would
love is Turbo Pascals "with" stmt. e.g.

struct { int foo, bar, whatever; } baz;

with (baz) {
foo = 4;
bar = 3 * foo;
whatever = bar - 5;
}

Which makes all the elements of "baz" local scope. That would beat
crap like

baz.foo = 4;
baz.bar = 3 * baz.foo;
baz.whatever = baz.bar - 5;

Of course then you have

int foo;
with (baz) {
foo = 4; // which foo?
}

But that's something specifications are for...
 
S

Stephen Sprunk

... What I would love is Turbo Pascals "with" stmt. e.g.

struct { int foo, bar, whatever; } baz;

with (baz) {
foo = 4;
bar = 3 * foo;
whatever = bar - 5;
}

Which makes all the elements of "baz" local scope. That would beat
crap like

baz.foo = 4;
baz.bar = 3 * baz.foo;
baz.whatever = baz.bar - 5;

Of course then you have

int foo;
with (baz) {
foo = 4; // which foo?
}

But that's something specifications are for...

Presumably it would be baz.foo, using the same sort of logic as if the
same identifier is used at block scope and file scope; you've just added
a new level.

S
 
S

Stephen Sprunk

In fact, a reference is just a pointer. It is different from plain
pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
int i = 23;
int &IntReference = i;

More plainly, a reference is an implicitly dereferenced pointer. The
compiler can substitute "(*ref)" wherever it sees "ref" and "ref"
wherever it sees "&ref". Also, the address of the initializer is used
rather than the initializer itself.
The problem is when defining a reference but when you pass a reference
to a function:
int fn(void &ref);
In this case there is no initialization (that would explode immediately)
but just a definition without any initialization. The compiler must
check, then, that the reference is not to a void pointer.

Conceptually, when a function is called, its arguments are implicitly
initialized with the corresponding parameters.
The advantages of using references are that they are never NULL,

Well, there are circumstances where a reference could be null, but
creating them requires invoking UB, so there's no point in specifying
what happens after that point.
and that they always point to the same object, two important
things less to test.

I'm not so sure about that. What is so wrong with being able to change
the referent, e.g. by assigning to &ref?
The only (orthodox) way of declaring a non-NULL pointer in an argument
list is now:

int fn(int arg[static 1]);

This declares that the "arg" argument will have at least one integer
Few people know this, and it wasn't much explained when C99 introduced
it.

News to me. Did we really need _yet another_ use for the "static" keyword?

S
 
K

Kenny McCormack

Tom St Denis said:
Of course then you have

int foo;
with (baz) {
foo = 4; // which foo?
}

But that's something specifications are for...

What does Pascal do?

(But I agree with the other poster that it should be baz.foo, although I
could easily imagine that it would be frequently desirable to be able to
access the "uplevel" foo - something that ordinary "variable shadowing"
prevents [unless extreme measures are taken...])

--
One of the best lines I've heard lately:

Obama could cure cancer tomorrow, and the Republicans would be
complaining that he had ruined the pharmaceutical business.

(Heard on Stephanie Miller = but the sad thing is that there is an awful lot
of direct truth in it. We've constructed an economy in which eliminating
cancer would be a horrible disaster. There are many other such examples.)
 
J

jacob navia

Le 24/06/11 15:26, Shao Miller a écrit :
So I'm guessing that if you have:

void foo(struct xxx & bar) { /* ... */ }

If you have an object with "allocated" storage duration, you'd do:

struct xxx * bar;
/* ...See below... */
foo(*bar_ptr);

in order for 'foo' to work with the pointed-to object, right?

yes.


But before you perform the call, you must:
- Allocate the object
- Check that the allocation succeeds

Yes. You must pass an object, not a pointer.
If you do these things before calling 'foo', then 'foo' needn't check
for a null pointer, right? For:

void foo(struct xxx * bar) { /* ... */ }

"static" and "automatic" objects can have their addresses passed with
'&', right?

Yes. For immediate constants (foo(5);) the compiler must generate a
temporary object and pass its address.

So this seems to be along the lines of #1, above.

Then there's also:

void foo(struct xxx * const bar) { /* ... */ }

and:

int i;
int * const ip = &i;

which appear to be along the lines of #2, above.

Am I understanding these two points correctly?

I do not understand the second. Can you explain in more depth?
 
T

Tom St Denis

Error prone; see below.



See above.

Error prone how? Aside from the typo which was easy enough to fix.
If I have a complicated function in which I want to work on it by
reference this is how I would do it. of course I'd switch the names
around so the frequently used one was shorter...

My error was akin to

int foo(int &bar)
{
return (baz *= 3);
}

It's a friggin typo, not a design error.

Tom
 
K

Keith Thompson

jacob navia said:
Le 24/06/11 15:26, Shao Miller a écrit :

yes.
[snip more questions and answers]

Perhaps it would save time to explain how lcc-win's references differ
from standard C++ references, if at all.
 
S

Shao Miller

Le 24/06/11 15:26, Shao Miller a écrit :

yes.

Thanks. :)
Yes. You must pass an object, not a pointer.


Yes. For immediate constants (foo(5);) the compiler must generate a
temporary object and pass its address.

Aha. I'm glad you brought this up. So, assuming the 'foo' above taking
a pointer (not the one above that, taking an lcc-win reference), this is
something that might be difficult with C89, but could be facilitated by C99:

foo(&(int){5});
I do not understand the second. Can you explain in more depth?

Well your point #2 was that references always refer to the same object.
I'm asking if that's akin to 'ip', above. Does that make sense?
 
K

Keith Thompson

jacob navia said:
The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of
language design this days, lcc-win considers C not a dead language but
a language that can evolve and propose new features.

In this context, one of the extensions that lcc-win supports is
references, where the design is largely inspired from the C++ design.


What are references?

In fact, a reference is just a pointer. It is different from plain
pointers in two ways:

(1) It is always assigned to a concrete object when it is defined.
(2) Once defined, it will always point to that same object. It can't
be changed to point into another object.
(3) It is used with the '.' notation, as if it weren't a pointer.
(4) Instead of "*' the symbol "&" is used:
int i = 23;
int &IntReference = i;
[...]

For what it's worth, an initialized const pointer gives you 2 and
*partly* gives you 1, but not 3 or 4.

The "partly" refers to the fact that an initialized local const
pointer object, as long as the initialization isn't a null pointer,
will always point to the same thing, but no such guarantees can be
made for a pointer parameter unless you use the C99 "static" kludge.

(This is an observation, not an argument for or against the
proposal.)
 
S

Shao Miller

The lcc-win compiler is an experimental compiler to promote the
development of C as a language. Contrary to the main trends of language
design this days, lcc-win considers C not a dead language but a language
that can evolve and propose new features.

In this context, one of the extensions that lcc-win supports is
references, where the design is largely inspired from the C++ design.
...

Suppose I have the following C89:

#include <assert.h>
#include <stddef.h>

#define ELEMENTS 5

typedef double a_five_doubles[ELEMENTS];

void foo(a_five_doubles array) {
size_t i;

/*
* 'array' isn't an array, so
* these probably aren't the same size
*/
assert(
sizeof array !=
sizeof (a_five_doubles)
);
for (i = 0; i < ELEMENTS; ++i)
array = 3.14159;
return;
}

int main(void) {
a_five_doubles array;

foo(array);
return 0;
}

Now suppose I have the following C89:

#include <assert.h>
#include <stddef.h>

#define ELEMENTS 5

typedef double a_five_doubles[ELEMENTS];

void foo(a_five_doubles * array) {
size_t i;

/*
* 'array' points to an array, so
* these should be the same size
*/
assert(
sizeof *array ==
sizeof (a_five_doubles)
);
for (i = 0; i < ELEMENTS; ++i)
i[*array] = 3.14159;
return;
}

int main(void) {
a_five_doubles array;

foo(&array);
return 0;
}

If lcc-win references become standard C, if I have:

#include <assert.h>
#include <stddef.h>

#define ELEMENTS 5
#define COUNTOF(array) \
(sizeof (array) / sizeof *(array))

typedef double a_five_doubles[ELEMENTS];

void foo(a_five_doubles & array) {
size_t i;

for (i = 0; i < COUNTOF(array); ++i)
array = 3.14159;
return;
}

int main(void) {
a_five_doubles array;

foo(array);
return 0;
}

Does the 'for' loop execute five times? I'm guessing that C99's
6.3.2.1p3 and 6.7.5.3p7 would need to be adjusted, right? (My C++
memory for this is poor, I'm afraid, if that's relevant here.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top