reference type for C

J

James Kuyper

On 05/17/2013 09:29 AM, Malcolm McLean wrote:
....
Pointer arithmetic is seldom necessary and usually indicates old-fashioned
programming. Pointers usually point either to structures or arrays, and
don't need modifying.

How do you access any element of an array other than the first without
pointer arithmetic? array is defined to be behave as *(array+i), and
therefore does involve pointer arithmetic.
 
J

James Kuyper

I had thought "restrict" might help:

$ cat ref.c
int foo(int *restrict const p) { return *p; }
int bar(void) { return foo(0); }
$ gcc -std=c99 -pedantic-errors -Wall -Wextra -Os -S ref.c
/* NO WARNING */

I expected a warning.
(AFAIU, restrict pointers must be valid, foo(0) is not allowed.)
Perhaps I should test with something more recent than 4.6.3

The main thing that "restrict" does is give undefined behavior to
certain uses of the restricted pointer. Those uses are not constraint
violations, so no diagnostics are required. Note that foo(0) would have
had undefined behavior even if 'restrict' had not been used.
 
M

Malcolm McLean

On 05/17/2013 09:29 AM, Malcolm McLean wrote

How do you access any element of an array other than the first without
pointer arithmetic? array is defined to be behave as *(array+i), and
therefore does involve pointer arithmetic.

Counting on isn't usually considered to be arithmetic, though it's
a semantic argument. Someone who knows the names, order and value
of the numbers in his native language is not considered to be
numerate.
If you take the value of the pointer expression and store it,
then I'd define that as pointer arithmetic. In the olden days,
pointer arithmetic could be faster than array notation, because

for(i=0;i<N;i++)
array = 0;
would generate N additions,
whilst

for(ptr=array;N--;ptr++)
*ptr = 0;

would generate N increments, which were a single machine instruction.

But nowadays you'd be unlucky to find a compiler which didn't optimise
the first loop.
 
G

glen herrmannsfeldt

Malcolm McLean said:
How do you access any element of an array other than the first without
pointer arithmetic? array is defined to be behave as *(array+i),
and therefore does involve pointer arithmetic.

Counting on isn't usually considered to be arithmetic, though it's
a semantic argument. Someone who knows the names, order and value
of the numbers in his native language is not considered to be
numerate.
If you take the value of the pointer expression and store it,
then I'd define that as pointer arithmetic. In the olden days,
pointer arithmetic could be faster than array notation, because
for(i=0;i<N;i++)
array = 0;
would generate N additions,
whilst

for(ptr=array;N--;ptr++)
*ptr = 0;

Yes, the latter is what I would call "using pointer arithmetic".
would generate N increments, which were a single machine instruction.

As well as I remember the stories, it was for processors like the
PDP-11 where one worried about such.

Well, it gets more interesting when you do:

for(i=0;i<N;i++) array[3*i] = 0;

where the compiler might do a multiply, on a processor with a
slow (or no) multiply instruction.

But this kind of things was done by the IBM Fortran H compiler
over 40 years ago, so it isn't new technology. Doing it in small
memory is harder, though.
But nowadays you'd be unlucky to find a compiler which
didn't optimise the first loop.

And, often the pointer form is less readable.

-- glen
 
N

Noob

James said:
The main thing that "restrict" does is give undefined behavior to
certain uses of the restricted pointer. Those uses are not constraint
violations, so no diagnostics are required.

I had QoI in mind, rather than compliance.
Note that foo(0) would have
had undefined behavior even if 'restrict' had not been used.

Right. I shouldn't have given the definition of foo,
juste the prototype.

int foo(int *restrict const p);
int bar(void) {
return foo(0);
}

In that situation, I expect a "good" compiler (in terms of
QoI) to flag the UB.

Regards.
 
B

BartC

For me, gowen has been the best advocate for why reference is a very good
thing to have in C.

I would also say that using a reference has a semantic value (the alias
role) for the reader of the code, which (as gowen said) is lost if one
used the army-knife-pointer - in that sense it helps legibility.

But what I am hearing is that it will never make it, either because people
might not see the value of it, or because you can do without it or even
because updating the standard would be too difficulte -- sob...

If that's the only C++ feature you need, why not just use C++, and use
mainly C programming style?
 
R

roland.arthaud

If that's the only C++ feature you need, why not just use C++, and use
mainly C programming style?

You are right, I thought about doing that.
I only wondered if there wasn't areas where I would get unanticipated side effects, i.e. where C++ would compile differently C code than a C compiler would.

Other features that I would like to have in C, are function overloading and default parameters. But I think that it render sometimes your code less obvious when C/C++ automatic conversions happen.

-roar
 
B

Bart van Ingen Schenau

You are right, I thought about doing that. I only wondered if there
wasn't areas where I would get unanticipated side effects, i.e. where
C++ would compile differently C code than a C compiler would.

There are a few less used, darker corners of C and C++, where a C and a C+
+ compiler silently give different results. The most notable is the type
of character literals (and consequently, the result of sizeof on them):

#include <stdio.h>
int main()
{
if (sizeof(char) == sizeof(int))
puts("Can't tell. char and int have same size");
else if (sizeof('a') == 1)
puts("C++");
else
puts("C");
return 0;
}

This is the only silent difference between C and C++ that I am aware of.
If there are any others, they will likely affect you even less than this
one.

On course, there are also some C constructs that are not valid in C++
(e.g. implicit conversion from void* to T*), but of those the compiler
will complain loudly enough.
Other features that I would like to have in C, are function overloading
and default parameters. But I think that it render sometimes your code
less obvious when C/C++ automatic conversions happen.

If you use them incorrectly, then they can certainly harm the
understandability of the code, but that holds also for most of the
constructs that C already has.

Bart v Ingen Schenau
 
J

Jorgen Grahn

On Sun, 2013-05-19, (e-mail address removed) wrote:

[attribution lost; apparently bartc]
You are right, I thought about doing that.

Slightly easier and quicker than getting a new C feature approved and
added to all compilers you care about, teaching coworkers to like and
use it ...
I only wondered if there wasn't areas where I would get
unanticipated side effects, i.e. where C++ would compile differently C
code than a C compiler would.

There are some such differences, but it's more common to simply get
compilation errors on things like void pointers.

In general, if you code in C++ (in order to have stuff like
references), you have to accept that you are, in fact, coding in C++.
You cannot pretend it's your own C-with-references dialect.
Other features that I would like to have in C, are function
overloading and default parameters. But I think that it render
sometimes your code less obvious when C/C++ automatic conversions
happen.

It is true that in C++ you can lose yourself good in combinations of
overloading, default parameters and constructors which lack the
"explicit" keyword. So don't do that!

Nothing wrong with mild use of overloading though; it's one of the C++
features I miss the most in C.

/Jorgen
 
M

Malcolm McLean

On Sun, 2013-05-19, (e-mail address removed) wrote:


It is true that in C++ you can lose yourself good in combinations of
overloading, default parameters and constructors which lack the
"explicit" keyword. So don't do that!
One feature tends to pull in another.
For instance if you use the "class" keyword, you're going to need to allow
a constructor to fail. In C, you return a null pointer. In C++, you can't do
this, so the only real answer is to call and exception.
Then in C we often need twin constructors, e.g.

COMPLEX *complex(double real, double imaginary);
COMPLEX *complexpolar(double theta, double magnitude);

In C++ you've got to overload the two functions. But in C they've got the same
signature. So it needs to be


class Complex{
Complex(double real, double imaginary);
Complex(Angle &theta, double magnitude);
};

and in fact you'll probably want Angle templated before long.
 
J

James Kuyper

How do you access any element of an array other than the first without
pointer arithmetic? array is defined to be behave as *(array+i), and
therefore does involve pointer arithmetic.

Counting on isn't usually considered to be arithmetic, though it's
a semantic argument. Someone who knows the names, order and value
of the numbers in his native language is not considered to be
numerate.


I'm unfamiliar with the use of the phrase "count on" with any meaning
other than "rely upon". So are the dictionaries I've consulted. What do
you mean by that? I can't imagine a reasonable definition of "pointer
arithmetic" that excludes pointer+integer, which is, in my opinion, the
most prototypical example of pointer arithmetic.
If you take the value of the pointer expression and store it,
then I'd define that as pointer arithmetic.

I consider "pointer arithmetic" to refer exclusively to the use of
arithmetic operators when one of the operands is a pointer value. In
other words, pointer+integer, integer+pointer, pointer-integer,
pointer-pointer, pointer+=integer, pointer-=integer, pointer++,
++pointer, pointer--, or --pointer. Technically, that definition also
covers +pointer, but I don't want to worry about that edge case. I
consider subscripting to be an example of hidden pointer arithmetic,
since pointer[integer] is defined as being equivalent to *(pointer+integer).

Arguably, the member selection operator is another example of hidden
pointer arithmetic, because pointer->member is normally implemented in a
manner equivalent to

*(member_type*)((char*)pointer + offsetof(struct_type, member))

However, since the standard does not actually define the member
selection operator in those terms, I only mark that as "arguable".

Your definition does not require that the pointer expression involve
arithmetic operators, which I find odd, and requires that the result be
stored, which I find even odder.

The standard never defines the meaning of the phrase "pointer
arithmetic", and uses it in only one place, 6.5.6p10:
EXAMPLE Pointer arithmetic is well defined with pointers to variable length array types.
{
int n = 4, m = 3;
int a[n][m];
int (*p)[m] = a; // p == &a[0]
p += 1; // p == &a[1]
(*p)[2] = 99; // a[1][2] == 99
n = p - a; // n == 1
}

The wording implies that the example contains pointer arithmetic on a
pointer to a VLA type. Unfortunately, both of our definitions are
consistent with that implication:

According to your definition, as I understand it, the following
expressions from the example involve pointer arithmetic on pointers to a
VLA type. I would disagree with the first one, because it doesn't
involve any arithmetic operators:
int (*p)[m] = a;
p += 1

According to my definition, the following expressions from the example
involve pointer arithmetic on pointers to a VLA type. Am I correct in
concluding that you would disagree with the second expression, because
it does not involve storage of the value of an expression with pointer type?
p += 1
p - a

I also consider (*p)[2] to be an example of hidden pointer arithmetic,
which does not meet your definition, since it doesn't involve storage of
the value of an expression of pointer type. However, since *p is not a
pointer to a VLA type, (*p)[2] couldn't be the example pointer
arithmetic whose existence is implied by the sentence at the start of
6.5.6p10.

My definition seems consistent with the uses of the phrase "pointer
arithmetic" within the wikipedia article on pointers; insofar as there's
a difference between our definitions, yours does not.
 
J

James Kuyper

On 05/19/2013 05:35 AM, Bart van Ingen Schenau wrote:
....
There are a few less used, darker corners of C and C++, where a C and a C+
+ compiler silently give different results. The most notable is the type
of character literals (and consequently, the result of sizeof on them):

#include <stdio.h>
int main()
{
if (sizeof(char) == sizeof(int))
puts("Can't tell. char and int have same size");
else if (sizeof('a') == 1)
puts("C++");
else
puts("C");
return 0;
}

This is the only silent difference between C and C++ that I am aware of.

There's a few others.
If there are any others, they will likely affect you even less than this
one.

You're probably right about that.
On course, there are also some C constructs that are not valid in C++
(e.g. implicit conversion from void* to T*), but of those the compiler
will complain loudly enough.

I've posted versions of the following material three times, most
recently in a message dated 2011-06-19. It has never provoked much
discussion, which disappointed me. I haven't updated it for the most
recent versions of the C and C++ standards, but I doubt that it needs
modification for either one.

The following is very carefully designed to make many different points
in a program that is as small as possible. I make no claim that it is an
example of good programming practice

It is syntactically valid code in both C and C++, conforms strictly to
the C99 standard, and is well-formed code according to both the C++98
and C++03 standards. It's behavior under C90 is technically undefined,
but only by reason of it's use of __cplusplus, an identifier which a C90
compiler could, in principle, have reserved for it's own incompatible
usage - but such compilers are rare, and probably non-existent.

You can compile and link both modules as C code, or as C++ code; the
resulting executables are guaranteed by the applicable standards to exit
with an failure status, for two entirely different sets of reasons,
depending upon which language is used (note that many compilers
automatically infer the language to be used from the extension on the
filename - you might need to rename the files to get them to actually
compile in one language rather than the other).

If you compile the first module with C, and the second with C++, it is
guaranteed to return a successful exit status. If C++ were really just
an extension to C, then what I've said about how this program's behavior
varies with the programming language would be impossible.

shared.h:
=========
#ifndef SHARED_H
#define SHARED_H

extern char tag;
extern int enumer[2];

typedef void voidvoid(void);

int Cnmtyp(void);
int Cfunc(voidvoid*);

#endif

First module:
=============
#ifdef __cplusplus
extern "C" {
#endif

#include "shared.h"

char tag = 0;

static int hidden(voidvoid *pfunc)
{
(*pfunc)();
tag = sizeof 'C' == sizeof(int);
return Cnmtyp() && enumer[0] && enumer[1];
}

int Cfunc(voidvoid* pfunc)
{
struct tag
{
enum { enumer, other } in;
int integer;
} out;

out.integer = sizeof(tag) == 1 && sizeof(enumer) == sizeof out.in;

return hidden(pfunc) && out.integer;
}

#ifdef __cplusplus
}
#endif

Second module:
==============
#ifdef __cplusplus
extern "C" {
#endif

#include "shared.h"

int enumer[2] = {0, 1};

static void Cppname_Cpptype(void)
{
enumer[0] = sizeof 'C' == 1;
return;
}

#ifdef __cplusplus
}
#endif

int Cnmtyp(void)
{
struct tag
{
enum { enumer, other } in;
int integer;
} out;

out.integer = sizeof(enumer) == 2 * sizeof(int);

return out.integer && sizeof(tag) == sizeof out;
}

static voidvoid Cppname_Ctype;

static void Cppname_Ctype(void) {
Cppname_Cpptype();
}

int main(void) {
return Cfunc(&Cppname_Ctype) && tag;
}

As an exercise for the student: explain precisely why three different
conditional expressions in the above code are guaranteed to have
different values in C and C++, and why two other conditionals will have
different values except in the unlikely case that sizeof(int)==1.
 
I

Ian Collins

Bart said:
There are a few less used, darker corners of C and C++, where a C and a C+
+ compiler silently give different results. The most notable is the type
of character literals (and consequently, the result of sizeof on them):

#include <stdio.h>
int main()
{
if (sizeof(char) == sizeof(int))
puts("Can't tell. char and int have same size");
else if (sizeof('a') == 1)
puts("C++");
else
puts("C");
return 0;
}

This is the only silent difference between C and C++ that I am aware of.
If there are any others, they will likely affect you even less than this
one.

One of the other significant differences between the two is with an
expression like

const size_t n = 42;
int i[n];

In C, i is a VLA, in C++ it is a normal array.
 
G

glen herrmannsfeldt

James Kuyper said:
On 05/17/2013 07:01 PM, Malcolm McLean wrote:
(snip)
I'm unfamiliar with the use of the phrase "count on" with any meaning
other than "rely upon". So are the dictionaries I've consulted. What do
you mean by that? I can't imagine a reasonable definition of "pointer
arithmetic" that excludes pointer+integer, which is, in my opinion, the
most prototypical example of pointer arithmetic.

The idea is to distinguish the two programming styles:

for(i=0;i<n;i++) sum += x;

and

for(i=0;i<n;i++) sum += *x++;
x -= n;

(For the latter, it might be that x isn't needed after this
construct, so there is no need to restore x. More commonly it
would be done with a copy of the pointer, though.)

If you want to suggest a different name to distinguish the two,
then you might convince others to use it.

In the past, maybe on some of the early processors that C was
implemented on, there was a suggestion that one form might be
faster than the other.
I consider "pointer arithmetic" to refer exclusively to the use of
arithmetic operators when one of the operands is a pointer value.
In other words, pointer+integer, integer+pointer, pointer-integer,
pointer-pointer, pointer+=integer, pointer-=integer, pointer++,
++pointer, pointer--, or --pointer. Technically, that definition also
covers +pointer, but I don't want to worry about that edge case. I
consider subscripting to be an example of hidden pointer arithmetic,
since pointer[integer] is defined as being equivalent
to *(pointer+integer).

Note that Fortran has pointers, Java has Object reference variables,
both have subscripting (indexing) but not pointer arithmetic.
Arguably, the member selection operator is another example of hidden
pointer arithmetic, because pointer->member is normally implemented in a
manner equivalent to
*(member_type*)((char*)pointer + offsetof(struct_type, member))
However, since the standard does not actually define the member
selection operator in those terms, I only mark that as "arguable".
Your definition does not require that the pointer expression involve
arithmetic operators, which I find odd, and requires that the result be
stored, which I find even odder.

I suppose [] isn't an arithmetic operator.

The point of the result being stored is to distinguish the two
styles shown above. But one might even do:

for(i=0;i<n;i++) {
temp=x+i;
sum += *temp;
}

which does store a pointer, but is logically more like the first
case than the second.

Now, consider the Fortran code:

DO 1 I=1,N
1 SUM=SUM+Y(2*I)

Fortran compilers have known how to optimize this case, keeping
an address in a register and adding the appropriate amount each
time through the loop, since before C even existed.

-- glen
 
J

James Kuyper

James Kuyper said:
On 05/17/2013 07:01 PM, Malcolm McLean wrote:
(snip)
I'm unfamiliar with the use of the phrase "count on" with any meaning
other than "rely upon". So are the dictionaries I've consulted. What do
you mean by that? I can't imagine a reasonable definition of "pointer
arithmetic" that excludes pointer+integer, which is, in my opinion, the
most prototypical example of pointer arithmetic.

The idea is to distinguish the two programming styles:

for(i=0;i<n;i++) sum += x;

and

for(i=0;i<n;i++) sum += *x++;
x -= n;



In any context where the second style is better, I'd expect the
following to be even more efficient (I'm expecting the optimizer to lift
"array+n" out of the loop).

for(int *pi = array; pi < array+n; pi++)
sum += *pi;

But I would never consider using the term "pointer arithmetic" to
distinguish the two styles. One uses explicit pointer arithmetic, the
other hides it inside an array subscript, but they both use it.
If you want to suggest a different name to distinguish the two,
then you might convince others to use it.

I seldom have a need to distinguish them, and I normally use the term
"pointer arithmetic" for purposes other than distinguishing them. For
instance, I'll say "pointer arithmetic is not allowed on void pointers",
a statement which would be false if I were using Bart's definition of
"pointer arithmetic".

....
... I
consider subscripting to be an example of hidden pointer arithmetic,
since pointer[integer] is defined as being equivalent
to *(pointer+integer). ....
Your definition does not require that the pointer expression involve
arithmetic operators, which I find odd, and requires that the result be
stored, which I find even odder.

I suppose [] isn't an arithmetic operator.

Correct, which is why I consider it to be an example of hidden pointer
arithmetic.
 
M

Malcolm McLean

On 05/17/2013 07:01 PM, Malcolm McLean wrote:

I'm unfamiliar with the use of the phrase "count on" with any meaning
other than "rely upon". So are the dictionaries I've consulted. What > do you mean by that? I can't imagine a reasonable definition of
"pointer arithmetic" that excludes pointer+integer, which is, in my
opinion, the most prototypical example of pointer arithmetic.
Counting one is where you take a number, let's say 42, and
ask the subject what is the nest number.
It's a necessary ability for numeracy, but it isn't in itself
considered enough to establish numeracy. It's a semantic argument,
of course.
Array notation is counting on for pointers. So it's not pointer
arithmetic. Again, it's a semantic argument. But it's not
unreasonable. Knowing that number 42 is a reasonable but
not excessive distance down the street doesn't equate to
knowing that 42 = 6 times 7.
 
K

Keith Thompson

James Kuyper said:
I consider "pointer arithmetic" to refer exclusively to the use of
arithmetic operators when one of the operands is a pointer value. In
other words, pointer+integer, integer+pointer, pointer-integer,
pointer-pointer, pointer+=integer, pointer-=integer, pointer++,
++pointer, pointer--, or --pointer. Technically, that definition also
covers +pointer, but I don't want to worry about that edge case. I
consider subscripting to be an example of hidden pointer arithmetic,
since pointer[integer] is defined as being equivalent to *(pointer+integer).
[...]

There is no unary "+" operator for pointers. The operand of unary "+"
or "-" must be of arithmetic type; this is a constraint.
 
G

glen herrmannsfeldt

Keith Thompson said:
I consider "pointer arithmetic" to refer exclusively to the use of
arithmetic operators when one of the operands is a pointer value. In
other words, pointer+integer, integer+pointer, pointer-integer,
pointer-pointer, pointer+=integer, pointer-=integer, pointer++,
++pointer, pointer--, or --pointer. Technically, that definition also
covers +pointer, but I don't want to worry about that edge case. I
consider subscripting to be an example of hidden pointer arithmetic,
since pointer[integer] is defined as being equivalent
to *(pointer+integer).
There is no unary "+" operator for pointers. The operand of unary "+"
or "-" must be of arithmetic type; this is a constraint.

So, you can say:

x = pointer + (pointer - pointer);

or

x = pointer - pointer + pointer;

but not:

x= pointer + pointer - pointer;

Maybe too for off topic, but the OS/360 (and succssor)
assemblers allow for it. That is, relocation factors can be one
of {-1, 0, 1, 2}. (It only takes one more bit in the object
program, though does complicate the relocation routine.)

-- glen
 
R

ralph

I suppose [] isn't an arithmetic operator.

This thread is tough because we are mixing in what might be going on
and what did go on back then. In the "legacy" days the array operator
was nothing but an arithmentic operator.

Mr Kuyper pointed out that the 'modern' C standard defines array to
*behave* as *(array+i), the fact is back then, array *was*
*(array+i). The array operator was merely short-hand. Underneath,
after the preprocessor, array became *(array + i ) which is
definitely 'arithmetic'. <g>

This was easily demonstrated as both the following would compile and
produce the same result...

x = array;
or
x = i[array];

The latter statement varied depending on vendor and warning levels.
eg, some would allow
x = 5[array]; // a constant, but not
x = i[array];

While the 'replacement' represented no real impact in most cases,
there was still a lot of mythology surrounding the 'array operator',
thus those of us programming in late 90s and 80s tended to avoid array
notation like the plague, unless clarity was required (or requested).
<bg>

-ralph
 
R

ralph

While the 'replacement' represented no real impact in most cases,
there was still a lot of mythology surrounding the 'array operator',
thus those of us programming in * late * 90s and 80s tended to avoid array
notation like the plague, unless clarity was required (or requested).
<bg>

Make that EARLY 90's.
I started out typing late 70's, 80's, and early 90's - then edited.
<g>
 

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
474,078
Messages
2,570,570
Members
47,204
Latest member
MalorieSte

Latest Threads

Top