Constant strings

K

Keith Thompson

BartC said:
With function names and arrays, these are effectively passed by
reference in C without needing to use & in the caller or * in the
callee (when using () or [] operators).

Referring to the way C handles arrays in function calls as "pass by
reference" glosses over the fact that the behavior is based on rules
that apply to arrays in general. The decay of array expressions
to pointers has nothing specifically to do with function calls;
it applies equally to assignment, initialization, array indexing,
and so forth. The other relevant rule, that an array parameter is
"adjusted" to a pointer parameter, is specific to functions, but
I'd call it a kind of syntactic sugar, not something fundamental
to the semantics of the language as pass-by-reference would be.

Saying that arrays are "passed by reference" has led too many
inexperienced C programmers to assume, quite reasonably, that sizeof
applied to an array parameter should yield the size of the array.

C has pass-by-reference in the same sense that C has linked lists.
It provides tools (pointers) from which you can build code that
has the effect of pass-by-reference, or of linked lists, but it
has neither as a feature of the language.
 
K

Kenny McCormack

Keith Thompson said:
C has pass-by-reference in the same sense that C has linked lists.
It provides tools (pointers) from which you can build code that
has the effect of pass-by-reference, or of linked lists, but it
has neither as a feature of the language.

Nor does it have "subroutines".

Which was my point all along.

Thank you; my work here is done.
 
K

Kaz Kylheku

BartC said:
With function names and arrays, these are effectively passed by
reference in C without needing to use & in the caller or * in the
callee (when using () or [] operators).

Referring to the way C handles arrays in function calls as "pass by
reference" glosses over the fact that the behavior is based on rules
that apply to arrays in general.

The right language for that is that arrays are communicated by reference in the
general sense of any data flow that occurs in the program, not only across
function boundaries; in other words: that arrays have de-facto reference
semantics in C.
C has pass-by-reference in the same sense that C has linked lists.

No because we can make linked lists in umpteen different, mutually incompatible
ways in C, the use of any of which can be justified. The conventions for
passing by reference aren't nearly that numerous.

There aren't umpteen answers to the question "I have an int variable in this
scope here, and I want to communicate it into a function such that the function
can modify the original variable." (There is one right answer, and maybe a
couple of others contrived just to have the last word in this argument, and
thus not technically justifiable in the same way that different linked list
approaches are justifiable.)
 
M

Malcolm McLean

There aren't umpteen answers to the question "I have an int variable in this
scope here, and I want to communicate it into a function such that the function
can modify the original variable." (There is one right answer, and maybe a
couple of others contrived just to have the last word in this argument, and
thus not technically justifiable in the same way that different linked list
approaches are justifiable.)
I can't resist.

void wrap(int *x, int dim)
{
if(*x < 0)
*x += dim;
if(* x >= dim)
*x -= dim;
}

#define wrap(x, dim) (x = x < dim ? x + dim : x >= dim ? x - dim : x)
 
G

glen herrmannsfeldt

Keith Thompson said:
BartC said:
With function names and arrays, these are effectively passed by
reference in C without needing to use & in the caller or * in the
callee (when using () or [] operators).
Referring to the way C handles arrays in function calls as "pass by
reference" glosses over the fact that the behavior is based on rules
that apply to arrays in general. The decay of array expressions
to pointers has nothing specifically to do with function calls;
it applies equally to assignment, initialization, array indexing,
and so forth. The other relevant rule, that an array parameter is
"adjusted" to a pointer parameter, is specific to functions, but
I'd call it a kind of syntactic sugar, not something fundamental
to the semantics of the language as pass-by-reference would be.

I was trying to explain in a previous post the difference between
pass by reference and passing a reference by value. I am not sure
Bart was convinced.
Saying that arrays are "passed by reference" has led too many
inexperienced C programmers to assume, quite reasonably, that
sizeof applied to an array parameter should yield the size
of the array.

In Java, an array reference, passed by value, has a .length
member which does give the length. You can change array elements,
or you can change the local copy of the array reference to refer
to a different array. The latter you normally can't do with
pass by reference.
C has pass-by-reference in the same sense that C has linked lists.
It provides tools (pointers) from which you can build code that
has the effect of pass-by-reference, or of linked lists, but it
has neither as a feature of the language.

Sounds about right to me.

-- glen
 
K

Kaz Kylheku

You should spend some time with a statically typed language that does
use pass by reference. That might help you understand the differences.

I don't see any real misunderstanding in the above paragraph.
For example in C++:

# cat x.cc
void ptr( const char i[5] ) {}

void ref( const char (&i)[5] ) {}

int main()
{
ptr("not 5 chars"); // Will decay to pointer.
ref("not 5 chars"); // Must be correct type.
}

This is apples and oranges: pointer to element, versus reference to array.

In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

int main(void)
{
ptr2(&"abcd"); /* okay */
ptr2(&"abcde"); /* type mismatch */
return 0;
}

Hmm, never address-of on a string literal before. GCC likes it as a C90 compiler
(with -Wall -W -ansi -pedantic); I can't think of a reason why it wouldn't be
allowed.
 
I

Ian Collins

Kaz said:
You should spend some time with a statically typed language that does
use pass by reference. That might help you understand the differences.

I don't see any real misunderstanding in the above paragraph.
For example in C++:

# cat x.cc
void ptr( const char i[5] ) {}

void ref( const char (&i)[5] ) {}

int main()
{
ptr("not 5 chars"); // Will decay to pointer.
ref("not 5 chars"); // Must be correct type.
}

This is apples and oranges: pointer to element, versus reference to array.

The point was to illustrate passing an array by reference compare to
what some claim is passing an array by reference (through the pointer to
it's first element) in C.
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference i
before using it as an array.
 
B

Ben Bacarisse

Ian Collins said:
Kaz Kylheku wrote:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference
i before using it as an array.

This isn't pass by reference because an assignment to i, inside ptr2,
will not alter the object (if there was one) used as an argument in the
caller!
 
K

Kaz Kylheku

Kaz said:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference i
before using it as an array.

Since you have to take the address, and dereference the reference this could be
called "passing by visible reference". If you don't have to then, that is
"passing by transparent/invisible reference".
 
K

Kaz Kylheku

Ian Collins said:
Kaz Kylheku wrote:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference
i before using it as an array.

This isn't pass by reference because an assignment to i, inside ptr2,
will not alter the object (if there was one) used as an argument in the
caller!

Assignment to i resulting in changing a variable in the caller,
is a feature of "pass by transparent/invisible reference".

"Pass by explicit/visible reference" requires that a dereferencing
operator be applied to the reference to designate the caller's object
from which the reference was obtained.
 
B

Ben Bacarisse

Kaz Kylheku said:
Kaz said:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference i
before using it as an array.

Since you have to take the address, and dereference the reference this
could be called "passing by visible reference". If you don't have to
then, that is "passing by transparent/invisible reference".

You could also call it "passing a pointer by value". Do you object to
that way of talking about it? It seems to me less likely to confuse,
but there appears to be a widely-held view that passing something that
refers to something else must be called "pass *by* reference" -- despite
the fact that that phrase used to mean something else.

If I'm just being an old fogey -- clinging to a usage of language that
has been superseded -- I'll accept that, but please tell me what the new
term is for what Fortran does*.

(* In the absence of the relatively new VALUE attribute.)
 
B

BartC

Ian Collins said:
You should spend some time with a statically typed language that does use
pass by reference. That might help you understand the differences.

Good idea; just spent 30 minutes implementing the closest I have to
pass-by-reference on one of my static language compilers, so I can compare
what it does with your example.
For example in C++:

# cat x.cc
void ptr( const char i[5] ) {}

void ref( const char (&i)[5] ) {}

BTW your gratuitous use of 'const' is confusing matters here. (I've spent
half the thread saying that, but no-one believes me.)
int main()
{
ptr("not 5 chars"); // Will decay to pointer.
ref("not 5 chars"); // Must be correct type.
}

Wasn't too sure either about the significance of the [5]; I expected ptr()
to work because it's passing by value (of the pointer to the string); and
ref() to fail (because it's passing by reference), although I don't know
what C++ does with constant strings).

What I think you're saying is that some obscure consequence of how
pass-by-reference works in C++ allows you to type-check a pointer to an
array. If you're implying that I don't understand that, then you're right!

I've done some tests in my own language, and the use of by-reference there
is far more straightforward and consistent. Use & on a formal parameter, and
you'd better pass it an l-value expression. A string constant isn't such an
expression.

So I understand how *I* want by-reference parameters to work, which might
have been be a good way of adding them to C. By-value parameter passing in
C is like this:

void charlie(double x) {
x = 23.03;
}

int main(void) {
double a=0.0;
charlie(a);
}

Change that to by-reference by:

(1) Turn the parameter type into a pointer
(2) Dereference the parameter in the body of the function
(3) Use the address-of operator (where needed) with the argument of the
call.

Which gives:

void charlie(double* x) {
*x = 23.03;
}

int main(void) {
double a=0.0;
charlie(&a);
}

Now charlie() can change the caller's value, which is exactly what we want.
Except we'd prefer to do it by simply writing:

void charlie(double &x) {
x = 23.03;
}

int main(void) {
double a=0.0;
charlie(a);
}
 
B

Ben Bacarisse

Kaz Kylheku said:
Ian Collins said:
Kaz Kylheku wrote:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference
i before using it as an array.

This isn't pass by reference because an assignment to i, inside ptr2,
will not alter the object (if there was one) used as an argument in the
caller!

Assignment to i resulting in changing a variable in the caller,
is a feature of "pass by transparent/invisible reference".

Did you just make that up or is it now a widely used term? I do like
to try to keep up if I can.
"Pass by explicit/visible reference" requires that a dereferencing
operator be applied to the reference to designate the caller's object
from which the reference was obtained.

No more than any assignment does. Some compilers do indeed generate an
address and de-reference it, but they don't have to. It is not
required in all cases. Fortran compilers used to be very good at
spotting aliasing and (in effect) exchanging the assignment to the
parameter for an assignment to the argument.
 
K

Kaz Kylheku

Kaz Kylheku said:
Kaz Kylheku wrote:
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference i
before using it as an array.

Since you have to take the address, and dereference the reference this
could be called "passing by visible reference". If you don't have to
then, that is "passing by transparent/invisible reference".

You could also call it "passing a pointer by value". Do you object to
that way of talking about it?

No; I don't object with any multiple non-conflicting views of the same system
or situation but rather to the denial of any of those views.
 
M

Malcolm McLean

No more than any assignment does. Some compilers do indeed generate an
address and de-reference it, but they don't have to. It is not
required in all cases. Fortran compilers used to be very good at
spotting aliasing and (in effect) exchanging the assignment to the
parameter for an assignment to the argument.
Exactly. Fortran compilers also don't need to worry about pointer aliasing.
But whilst its superbly efficient, it's also a lot harder to keep track of
which variables are being set where, and there's no sort of hierarchy
or grouping such as structures can impose. So it's less flexible, less
easy to write programs whose main objective is data manipulation rather
than calculations.
You interface Fortran with C by passing pointers, however. A C function
that is callable from Fortran takes pointers as parameters, a Fortran
function callable from C is passed pointers.
 
K

Kaz Kylheku

Kaz Kylheku said:
Kaz Kylheku wrote:
<snip>
In C with pointers to array (the "other" way to pass the string by
reference):

void ptr2(char (*i)[5])
{
}

But that isn't really passing by reference as you have to dereference
i before using it as an array.

This isn't pass by reference because an assignment to i, inside ptr2,
will not alter the object (if there was one) used as an argument in the
caller!

Assignment to i resulting in changing a variable in the caller,
is a feature of "pass by transparent/invisible reference".

Did you just make that up or is it now a widely used term? I do like
to try to keep up if I can.

I made it up. I didn't make up "transparent", "invisible" or "reference" of
course and aren't using them in any non-obvious way.
No more than any assignment does.

Yes; and that is because there actually isn't any special "pass by"
data flow that is for functions only. Reducing an expression to a value
and using it to initialize a newly instantiated variable in a new function
invocation is a data flow. Assignment is another example of a data flow.
Both of these can be regarded as "passing" something.

Any time in C a reference to an object moves from one place in the program
graph to another, we use a value to represent that data flow, and have to
manually insert operators to source that kind of flow, and to recover the
referenced value at various "sinks" reached by that value.
 
G

glen herrmannsfeldt

(snip on call by reference, or not)
Assignment to i resulting in changing a variable in the caller,
is a feature of "pass by transparent/invisible reference".
"Pass by explicit/visible reference" requires that a dereferencing
operator be applied to the reference to designate the caller's object
from which the reference was obtained.

So, the C [] operator is a visible dereference operator, but
the Fortran () (dummy array subscript) is not?

But C is different from Fortran in the need for * (or [0])
on scalar arguments.

-- glen
 
G

glen herrmannsfeldt

(snip)
No more than any assignment does. Some compilers do indeed generate an
address and de-reference it, but they don't have to. It is not
required in all cases. Fortran compilers used to be very good at
spotting aliasing and (in effect) exchanging the assignment to the
parameter for an assignment to the argument.

I am not so sure what that means, but Fortran has much stricter
aliasing rules than C.

For one, Fortran doesn't require call by reference, but also allows
for call by value result (also known as copy-in/copy-out).
The difference is very obvious in some aliasing cases.

I have known Fortran compilers where the addresses of the arguments
were passed, and the called routine copied the values in at the
beginning, and copied them back before returning. On some machines,
access to local variables is much faster than the indirect addressing
to access through a reference. Though the actual gain depends on
how often it is accessed.

-- glen
 
G

glen herrmannsfeldt

(snip on call by reference and Fortran)
You interface Fortran with C by passing pointers, however. A C function
that is callable from Fortran takes pointers as parameters, a Fortran
function callable from C is passed pointers.

Actually, Fortran now has the VALUE attribute, and so can also do
call by value, when needed. It isn't the default, though.

With the C interoprability feature now in Fortran, you can easily
call between Fortran and C. If you pass an argument, and don't
use the VALUE attribute, then a pointer is passed. You can also
directly pass a TYPE(C_PTR) or the return value of C_LOC() by
value to a called C function.

-- glen
 
I

Ian Collins

BartC said:
Ian Collins said:
You should spend some time with a statically typed language that does use
pass by reference. That might help you understand the differences.

Good idea; just spent 30 minutes implementing the closest I have to
pass-by-reference on one of my static language compilers, so I can compare
what it does with your example.
For example in C++:

# cat x.cc
void ptr( const char i[5] ) {}

void ref( const char (&i)[5] ) {}

BTW your gratuitous use of 'const' is confusing matters here. (I've spent
half the thread saying that, but no-one believes me.)

It isn't gratuitous, it is necessary. C++ had the good sense to make
string literals what they really are: const.
int main()
{
ptr("not 5 chars"); // Will decay to pointer.
ref("not 5 chars"); // Must be correct type.
}

Wasn't too sure either about the significance of the [5]; I expected ptr()
to work because it's passing by value (of the pointer to the string); and
ref() to fail (because it's passing by reference), although I don't know
what C++ does with constant strings).

What I think you're saying is that some obscure consequence of how
pass-by-reference works in C++ allows you to type-check a pointer to an
array. If you're implying that I don't understand that, then you're right!

There's nothing obscure about it, the type passed to a reference
parameter has to match. "const char [12]" doesn't match "const char
[5]". Attempting to pass an object of type char to "void f( int& i )"
would also fail.
I've done some tests in my own language, and the use of by-reference there
is far more straightforward and consistent. Use & on a formal parameter, and
you'd better pass it an l-value expression. A string constant isn't such an
expression.

Exactly. C++ rules for passing by reference are straightforward and
consistent.
So I understand how *I* want by-reference parameters to work, which might
have been be a good way of adding them to C. By-value parameter passing in
C is like this:

void charlie(double x) {
x = 23.03;
}

int main(void) {
double a=0.0;
charlie(a);
}

Change that to by-reference by:

(1) Turn the parameter type into a pointer
(2) Dereference the parameter in the body of the function
(3) Use the address-of operator (where needed) with the argument of the
call.

Which gives:

void charlie(double* x) {
*x = 23.03;
}

int main(void) {
double a=0.0;
charlie(&a);
}

Now charlie() can change the caller's value, which is exactly what we want.
Except we'd prefer to do it by simply writing:

void charlie(double &x) {
x = 23.03;
}

int main(void) {
double a=0.0;
charlie(a);
}

Which is exactly how C++ does it! You really should look into C++ as
your base language, it looks to be a much better fit. Yes you can use
work arounds to make stuff work in C, but those kludges very often cost
you the ability to perform compile time type checking.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top