Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

usecases of void foo(char **b) {}

559 views
Skip to first unread message

fir

unread,
Oct 30, 2016, 8:42:23 AM10/30/16
to
this case when your argument of function is of a type of char**
has couple of usecases, im not sure if people ale fully and clearly
aware of them (personaly even in regard to me who rather knows c this days
im not sure if i totally clear see those usacases)

is someone able to mention it all and maybe note some remarks on it?

BartC

unread,
Oct 30, 2016, 1:17:24 PM10/30/16
to
Here's one:

void printstringarray(char** A,int n) {
int i;
for (i=0; i<n; ++i)
printf("%d: %s\n", i, A[i]);
}

int main (void) {
char* names[]={"Alan","Bill","Charlie","Dick"};

printstringarray(names,4);
}

And another:


char nextchar(char** s){
char c = **s;
if (c) ++*s;
return c;
}

int main (void) {
char* name="bartsimpson";
char* nameptr = name;
char c;

while (c=nextchar(&nameptr)) printf("%c",c);

}

--
bartc


supe...@casperkitty.com

unread,
Oct 30, 2016, 3:14:05 PM10/30/16
to
On many implementations, all pointer types have the same size and
representation; in the days before void* was recognized as a valid
pointer type, function which accepted a char** could use it to manipulate
any kind of object pointer or an array thereof. For example, if one
wanted to find a function which would report whether an array of
pointers contains any nulls:

int containsAnyNulls(char **p, int size)
{
if (size) do
{
if (!*p) return 1;
} while(--size);
return 0;
}

A caller would need to cast a pointer to the array to type (char**), and
on implementations where different pointer types had different
representations such code couldn't be expected to work at all, but in
the days prior to aliasing rules such a function could be used with
arrays of pointers to any kind of object. Once void* entered the
language, the type void** would generally have been preferred to char**,
but either type would have worked equally well on any implementation
where all pointers had the same size and representation [i.e. the vast
majority of implementations].

Rick C. Hodgin

unread,
Oct 30, 2016, 3:50:00 PM10/30/16
to
The only use cases I have for pointer-to-pointers are array lists of pointers,
and to allow the data and underlying pointer to be reset by a common called
function.

int main(int argc, char* argv[])
{
struct SWhatever* p;

p = allocate_via_some_protocol();

// Later code
free_via_some_protocol(&p);
// p is now null
}

struct SWhatever* allocate_via_some_protocol(void)
{
struct SWhatever* p;

p = malloc(sizeof(struct SWhatever));
if (p)
{
// Initialize
}

// Indicate our success or failure
return(p);
}

void free_via_some_protocol(struct SWhatever** p)
{
if (p && *p)
{
// Free however it gets freed

// And finally physically free
free(*p);
*p = NULL;
}
}

Best regards,
Rick C. Hodgin

Keith Thompson

unread,
Oct 30, 2016, 3:52:23 PM10/30/16
to
supe...@casperkitty.com writes:
> On many implementations, all pointer types have the same size and
> representation; in the days before void* was recognized as a valid
> pointer type, function which accepted a char** could use it to manipulate
> any kind of object pointer or an array thereof.

There is of course no such guarantee, and in my opinion it is poor
practice to depend on it.

> wanted to find a function which would report whether an array of
> pointers contains any nulls:
>
> int containsAnyNulls(char **p, int size)
> {
> if (size) do
> {
> if (!*p) return 1;
> } while(--size);
> return 0;
> }

I wonder why you wrote that as a do-while loop inside an if rather than
more simply as a while loop. I initially thought the "if (size) do" was
at typo. Is that intentionally obfuscated?

> A caller would need to cast a pointer to the array to type (char**), and
> on implementations where different pointer types had different
> representations such code couldn't be expected to work at all, but in
> the days prior to aliasing rules such a function could be used with
> arrays of pointers to any kind of object. Once void* entered the
> language, the type void** would generally have been preferred to char**,
> but either type would have worked equally well on any implementation
> where all pointers had the same size and representation [i.e. the vast
> majority of implementations].

void* is a "generic" pointer type. void** is not a generic
pointer-to-pointer type, and IMHO should not be used as one. If I
needed to store arbitrary pointer-to-pointer values of different
types, I'd use void* and some careful conversions.

The OP was asking about use cases for a function that takes a char**
argument. I hardly think that your digression about pointer sizes and
aliasing rules is relevant to that question. If you wanted to show an
example, you could have just discussed the argv parameter to main.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

supe...@casperkitty.com

unread,
Oct 30, 2016, 4:39:02 PM10/30/16
to
On Sunday, October 30, 2016 at 2:52:23 PM UTC-5, Keith Thompson wrote:
> supercat writes:
> > On many implementations, all pointer types have the same size and
> > representation; in the days before void* was recognized as a valid
> > pointer type, function which accepted a char** could use it to manipulate
> > any kind of object pointer or an array thereof.
>
> There is of course no such guarantee, and in my opinion it is poor
> practice to depend on it.

Many implementations specify the bit-level representations of pointers
even though the Standard does not require them to do so. If an
implementation specifies that pointers of type char* are stored as a
linear address stored as four bytes lsb-first, that pointers of type
int* are stored as a linear address of the first byte, stored lsb-first,
etc. would such specifications not imply that all pointers thus described
have compatible representations?

Likewise, in the absence of aliasing rules, what could an assignment made
via char** do other than set the four bytes at the target address to the
bit-level representation of the pointer being stored?

> I wonder why you wrote that as a do-while loop inside an if rather than
> more simply as a while loop. I initially thought the "if (size) do" was
> at typo. Is that intentionally obfuscated?

It's designed to help the kind of peephole optimizers that were in use
during that era. As written, straightforward code generation would start
by saying:

decrement size
branch if size is non-zero [typically expands to two instructions]

On many processors, however, the above can be replace with a faster
"decrement size and branch if non-zero" instruction or pair of instructions.
Such a replacement, however, will only work if the second line can only
execute immediately after the first [as opposed to being the target of a
jump or branch]. If the loop had been "while(size) { ... size--; }" then
it early-stage code generation would have yielded either:

loop:
branch to exit if size is zero
...
decrement size
goto loop
exit:

or else

goto loopcheck
loop:
...
decrement size
loopcheck:
branch to loop if size is not zero

The existence of a branch or label between the decrement and the test would
block a simple peephole optimizer from making the indicated substitution.

> > A caller would need to cast a pointer to the array to type (char**), and
> > on implementations where different pointer types had different
> > representations such code couldn't be expected to work at all, but in
> > the days prior to aliasing rules such a function could be used with
> > arrays of pointers to any kind of object. Once void* entered the
> > language, the type void** would generally have been preferred to char**,
> > but either type would have worked equally well on any implementation
> > where all pointers had the same size and representation [i.e. the vast
> > majority of implementations].
>
> void* is a "generic" pointer type. void** is not a generic
> pointer-to-pointer type, and IMHO should not be used as one. If I
> needed to store arbitrary pointer-to-pointer values of different
> types, I'd use void* and some careful conversions.

Accepting void* would have the advantage of eliminating caller-side casts
but the disadvantage of obfuscating how the parameter will be used. If the
Standard specified that &*expr will be a constraint violation if expr is
not a pointer of some kind, but will be an lvalue if expr is an lvalue, then
the nicest approach would be to have a macro which wraps the argument in
((void**)&&**(p)) so as to eliminate the need for a typecast in user code
(which could mask other problems) while offering far more type safety than
if the function simply took void**.

> The OP was asking about use cases for a function that takes a char**
> argument. I hardly think that your digression about pointer sizes and
> aliasing rules is relevant to that question. If you wanted to show an
> example, you could have just discussed the argv parameter to main.

In the days before void*, char** was the closest thing there was to a
generic pointer-to-pointer type on implementations which defined their
pointer representations so as to allow such a thing. Not a common usage
anymore, but one which was certainly usable in the past.

Rick C. Hodgin

unread,
Oct 30, 2016, 4:49:27 PM10/30/16
to
On Sunday, October 30, 2016 at 3:14:05 PM UTC-4, supe...@casperkitty.com wrote:
> [snip]
> if (size) do
> {
> if (!*p) return 1;
> } while(--size);
> [snip]

I've never seen this use of syntax before. I like it. I would rewrite
slightly to move the do keyword down to the next line:

if (test)
do {
// Code here
} while(second_test);

I really like that syntax. It easily exposes the two test conditions. I
wonder if a dedicated syntax like...:

do if (test)
{
// Code here
} while (second_test);

...would be desirable?

supe...@casperkitty.com

unread,
Oct 30, 2016, 5:05:31 PM10/30/16
to
On Sunday, October 30, 2016 at 3:49:27 PM UTC-5, Rick C. Hodgin wrote:
> On Sunday, October 30, 2016 at 3:14:05 PM UTC-4, supercat wrote:
> > [snip]
> > if (size) do
> > {
> > if (!*p) return 1;
> > } while(--size);
> > [snip]
>
> I've never seen this use of syntax before. I like it. I would rewrite
> slightly to move the do keyword down to the next line:
>
> if (test)
> do {
> // Code here
> } while(second_test);
>
> I really like that syntax.

I don't like it so much, since the "do" doesn't really look like it's
controlled by the "if" [no indent]. Indenting the "do" would in turn
necessitate another level of indent on the contents of the loop, and
would end up with the "} while" line being indented one deeper than
the following code unless one added an extra set of braces for the "if",
making code take up even more vertical space.

> It easily exposes the two test conditions. I
> wonder if a dedicated syntax like...:
>
> do if (test)
> {
> // Code here
> } while (second_test);
>
> ...would be desirable?

How about

if (test) do
{
...
} while (second_test);

Note that your proposed syntax is already syntactically valid C, but has a
meaning which is probably different from what you intend.

Rick C. Hodgin

unread,
Oct 30, 2016, 5:14:41 PM10/30/16
to
On Sunday, October 30, 2016 at 5:05:31 PM UTC-4, supe...@casperkitty.com wrote:
> On Sunday, October 30, 2016 at 3:49:27 PM UTC-5, Rick C. Hodgin wrote:
> > On Sunday, October 30, 2016 at 3:14:05 PM UTC-4, supercat wrote:
> > > [snip]
> > > if (size) do
> > > {
> > > if (!*p) return 1;
> > > } while(--size);
> > > [snip]
> >
> > I've never seen this use of syntax before. I like it. I would rewrite
> > slightly to move the do keyword down to the next line:
> >
> > if (test)
> > do {
> > // Code here
> > } while(second_test);
> >
> > I really like that syntax.
>
> I don't like it so much, since the "do" doesn't really look like it's
> controlled by the "if" [no indent]. Indenting the "do" would in turn
> necessitate another level of indent on the contents of the loop, and
> would end up with the "} while" line being indented one deeper than
> the following code unless one added an extra set of braces for the "if",
> making code take up even more vertical space.

I was thinking:

if (test)
{
// Code
)

Being translated into an iterative condition:

if (test)
do {
} while (second_test);

So that rather than being "if (x) {" it now becomes "if (x) do {".

> > It easily exposes the two test conditions. I
> > wonder if a dedicated syntax like...:
> >
> > do if (test)
> > {
> > // Code here
> > } while (second_test);
> >
> > ...would be desirable?
>
> How about
>
> if (test) do
> {
> ...
> } while (second_test);
>
> Note that your proposed syntax is already syntactically valid C, but has a
> meaning which is probably different from what you intend.

I was unaware my proposed syntax was already valid in C. I just tested
it and it compiled. What does it do? :-)

Rick C. Hodgin

unread,
Oct 30, 2016, 5:20:12 PM10/30/16
to
On Sunday, October 30, 2016 at 5:14:41 PM UTC-4, Rick C. Hodgin wrote:
> On Sunday, October 30, 2016 at 5:05:31 PM UTC-4, supe...@casperkitty.com wrote:
> > On Sunday, October 30, 2016 at 3:49:27 PM UTC-5, Rick C. Hodgin wrote:
> > > It easily exposes the two test conditions. I
> > > wonder if a dedicated syntax like...:
> > >
> > > do if (test)
> > > {
> > > // Code here
> > > } while (second_test);
> > >
> > > ...would be desirable?
> >
> > Note that your proposed syntax is already syntactically valid C, but has a
> > meaning which is probably different from what you intend.
>
> I was unaware my proposed syntax was already valid in C. I just tested
> it and it compiled. What does it do? :-)

I wrote this test:

int main(int argc, char* argv[])
{
int i = 4;
do if (--i)
{
printf("%i\n", i);
} while (--i);
}

It looks like it's doing a header test and a footer test on every
iteration. What would you use that for?

Rick C. Hodgin

unread,
Oct 30, 2016, 5:24:21 PM10/30/16
to
On Sunday, October 30, 2016 at 5:20:12 PM UTC-4, Rick C. Hodgin wrote:
> I wrote this test:
>
> int main(int argc, char* argv[])
> {
> int i = 4;
> do if (--i)
> {
> printf("%d\n", i);

Correction. Should be printf("%d\n", i);

> } while (--i);
> }

Keith Thompson

unread,
Oct 30, 2016, 5:31:25 PM10/30/16
to
supe...@casperkitty.com writes:
> On Sunday, October 30, 2016 at 2:52:23 PM UTC-5, Keith Thompson wrote:
>> supercat writes:
>> > On many implementations, all pointer types have the same size and
>> > representation; in the days before void* was recognized as a valid
>> > pointer type, function which accepted a char** could use it to manipulate
>> > any kind of object pointer or an array thereof.
>>
>> There is of course no such guarantee, and in my opinion it is poor
>> practice to depend on it.
>
> Many implementations specify the bit-level representations of pointers
> even though the Standard does not require them to do so. If an
> implementation specifies that pointers of type char* are stored as a
> linear address stored as four bytes lsb-first, that pointers of type
> int* are stored as a linear address of the first byte, stored lsb-first,
> etc. would such specifications not imply that all pointers thus described
> have compatible representations?

Certainly, an implementation can document whatever it likes. That
doesn't affect my point.

[...]

>> I wonder why you wrote that as a do-while loop inside an if rather than
>> more simply as a while loop. I initially thought the "if (size) do" was
>> at typo. Is that intentionally obfuscated?
>
> It's designed to help the kind of peephole optimizers that were in use
> during that era.
[snip]

So, premature optimization aimed at ancient compilers. You might have
bothered to mention that.

[...]

>> The OP was asking about use cases for a function that takes a char**
>> argument. I hardly think that your digression about pointer sizes and
>> aliasing rules is relevant to that question. If you wanted to show an
>> example, you could have just discussed the argv parameter to main.
>
> In the days before void*, char** was the closest thing there was to a
> generic pointer-to-pointer type on implementations which defined their
> pointer representations so as to allow such a thing. Not a common usage
> anymore, but one which was certainly usable in the past.

So you assumed, without stating your assumption, that the use of char**
for a parameter was intended as some kind of generic pointer-to-pointer,
a usage that would apply only to very old code. And you ignored the
much more common use of char** as a pointer to pointer to char, such as
the argv parameter of main.

If you want to make some unrelated point about aliasing and pointer
representations, I suggest starting a new thread rather than hijacking a
simple question that probably had nothing to do with what you're talking
about.

Or, as I've suggested before, you could start a blog.

Ike Naar

unread,
Oct 30, 2016, 9:27:18 PM10/30/16
to
On 2016-10-30, supe...@casperkitty.com <supe...@casperkitty.com> wrote:
> On many implementations, all pointer types have the same size and
> representation; in the days before void* was recognized as a valid
> pointer type, function which accepted a char** could use it to manipulate
> any kind of object pointer or an array thereof. For example, if one
> wanted to find a function which would report whether an array of
> pointers contains any nulls:
>
> int containsAnyNulls(char **p, int size)
> {
> if (size) do
> {
> if (!*p) return 1;
> } while(--size);
> return 0;
> }

p does not change in that loop, so it tests the first pointer in the
list over and over again. Did you miss a ++ somewhere?

David Brown

unread,
Oct 31, 2016, 7:38:02 AM10/31/16
to
Put in the curly brackets, and use indentation, then you can see:

>>> do if (test)
>>> {
>>> // Code here
>>> } while (second_test);

means

do {
if (test) {
// Code here
}
} while (second_test);


You could consider making the brackets and indentation obligatory in
your language - then such mixups would be impossible.

Rick C. Hodgin

unread,
Oct 31, 2016, 9:01:37 AM10/31/16
to
Well, CAlive won't allow the "do x while" syntax without braces, so it's
a non-issue. I think that syntax is confusing, and I can't see a need
for it except possibly making code more compact, but you could do the
same with minimal extra intrusion, and I would argue far more clarity:

do printf("%d\n", i); while (--i);
do { printf("%d\n", i); } while (--i);

Pascal J. Bourguignon

unread,
Oct 31, 2016, 9:16:15 AM10/31/16
to
In C, basically a pointer is equivalent to array. Here we have two
pointers, so 4 different combinations of pointers and arrays.

* b can hold a pointer that points to a pointer that points to a
character:

+---+ +---+ +---+
b:| *----->| *----->|'a'|
+---+ +---+ +---+

(**b)=='a'

* b can hold a pointer that points to a pointer that points to an
array of characters:

+---+ +---+ +---+---+---+
b:| *----->| *----->|'a'|'b'|'c'|
+---+ +---+ +---+---+---+

strncmp((*b),"abc",3)

* b can hold a pointer that points to an array of pointers that
point to a character:

+---+ +---+---+---+
b:| *----->| * | * | * |
+---+ +-|-+-|-+-|-+
| | +-----+
| +--+ |
| | |
v v v
+---+ +---+ +---+
|'a'| |'b'| |'c'|
+---+ +---+ +---+

(*(b[0]))=='a'
(*(b[1]))=='b'
(*(b[2]))=='c'

b[0][0]=='a'
b[1][0]=='b'
b[2][0]=='c'

* b can hold a pointer that points to an array of pointers that
point to arrays of character:

+---+ +---+---+---+
b:| *----->| * | * | * |
+---+ +-|-+-|-+-|-+
| | +-----------------+
| +----------+ |
| | |
v v v
+---+---+---+ +---+---+ +---+---+---+---+
|'a'|'b'|'c'| |'a'|'b'| |'c'|'c'|'c'|'c'|
+---+---+---+ +---+---+ +---+---+---+---+

strncmp(b[0],"abc",3)==0
strncmp(b[1],"ab",2)==0
strncmp(b[2],"cccc",4)==0

b[0][1]=='b'
b[1][0]=='a'
b[2][2]=='c'


Of course, since a pointer to a character is equivalent to a pointer to
an array of 1 character, in the last case you could also have single
characters referenced.


Also, since there is only one mode of argument passing in C, namely pass
by value, when we want to have output or in-out parameters, we use
pointers to the argument l-value, so b could also be an output or in-out
parameter holding a pointer to a character, or a pointer to an array of
characters (same as first two diagrams), but with a different purpose.

Notice that the C expressions walking the structure are walking on eggs,
since they can work only if the pointers are valid, and the arrays are
allocated (if the indices are below the array size).

The later case can also be considered as b holding a 2D array, or matrix
of characters, as long as all the vectors have the same length.

Notice that all the meta data required to know the length of the arrays
is implicit, or needs to be passed separately. This is why it is a very
bad idea to use such parameter types, even for simple C strings, char*
is ambiguous, and are you sure there's a terminating null in the
string???

Much better is to pass a structure containing both the meta data and the
data, and ensure that you are manipulating those structure only thru
functions that will ensure their invariants (that the meta data
represents correctly the allocated data).

Write:

typedef struct string {
unsigned int character_length;
unsigned int byte_length;
unsigned int bytes_allocated;
char* encoding;
char* bytes;
} string_t;

string_t* string_new_from_c_string(const char* cstring,const char* encoding);
string_t* string_concatenate(string_t* a,string_t* b);
void string_printf(string_t* string,FILE* stream);

string_printf(string_concatenate(string_new_from_c_string("Hello ","ASCII"),
string_new_from_c_string("world!","ASCII")),
stdout);

instead of

printf("%s",strcat("Hello ","world!"));




--
__Pascal Bourguignon__

Croire en l'histoire officielle, c'est croire des criminels sur parole.
-- Simone WEIL (1909-1943) philosophe Française.

BartC

unread,
Oct 31, 2016, 9:40:35 AM10/31/16
to
C has the if-statement, and the do-while statement. Either can be
written with a single-statement body without braces:

if (a==b) c=d;
do c=d; while (e==f);

or with braces, here shown with the same single statement:

if (a==b) {c=d};
do {c=d;} while (e==f);

I suppose it can get confusing if you mix non-brace versions of both:

if (a==b) do c=d; while (e==f);
do if (a==b) c=d; while (e==f);

There is also the while-statement with a one-statement body:

while (g==h) i=j;

What /I/ can't understand is how C distinguishes from a while-statement
with an empty body, from the while-part of do-while:

do while (g==h); while (k==l);

Is this a do-while loop controlled by (k==l), with a single statement in
its body consisting of the loop 'while (g==h)'? Or a do-loop with an
empty body controlled by (g==h), followed by a separate while-loop with
an empty body?

I suggest following David Brown's advice and always using braces!

--
Bartc

Richard Heathfield

unread,
Oct 31, 2016, 10:20:30 AM10/31/16
to
On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> fir <profes...@gmail.com> writes:
>
>> this case when your argument of function is of a type of char**
>> has couple of usecases, im not sure if people ale fully and clearly
>> aware of them (personaly even in regard to me who rather knows c this days
>> im not sure if i totally clear see those usacases)
>>
>> is someone able to mention it all and maybe note some remarks on it?
>
>
> In C, basically a pointer is equivalent to array.

Except when it isn't. For example:

unsigned long arra[32] = {0};
unsigned long arrb[16] = {0};
unsigned long *ptra = arra;
unsigned long *ptrb = arrb;

If pointers were equivalent to arrays, we could expect sizeof ptra to be
equivalent to sizeof arra and sizeof ptrb to be equivalent to sizeof
arrb, but even if one of those equivalences held (which is vanishingly
unlikely), they cannot /both/ hold, because ptra has to be equal in size
to ptrb whereas arra must differ in size from arrb.

If pointers were equivalent to arrays, we could note that ++ptra is
legal and assume that ++arra is legal too, but it isn't.

Confusing pointers with arrays is a leading cause of misunderstandings
about pointers, and should be avoided at all times.

The most important relationship between pointers and arrays is that,
when an array name is used in an evaluation context, it evaluates to a
pointer to its first element. That is a far cry from saying that
pointers and arrays are equivalent, basically or otherwise.

> Here we have two
> pointers, so 4 different combinations of pointers and arrays.

Since the original article has been filtered out of my newsfeed, I can't
comment on the specific example, so I won't.

<snip>

> Of course, since a pointer to a character is equivalent to a pointer to
> an array of 1 character,

But it isn't. In the following code:

char arronechar[1] = 'X';
char *p = &arronechar[0]; /* pointer to a character */
char (*parr)[1] = &arronechar; /* pointer to an array of one character */

we can clearly see that p and parr of of different types. p has type
"pointer to char", whereas parr has type "pointer to array 1 of char".

> Also, since there is only one mode of argument passing in C, namely pass
> by value,

Right!

--
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within

BartC

unread,
Oct 31, 2016, 10:55:17 AM10/31/16
to
On 31/10/2016 14:20, Richard Heathfield wrote:
> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
>> fir <profes...@gmail.com> writes:
>>
>>> this case when your argument of function is of a type of char**
>>> has couple of usecases, im not sure if people ale fully and clearly
>>> aware of them (personaly even in regard to me who rather knows c this
>>> days
>>> im not sure if i totally clear see those usacases)
>>>
>>> is someone able to mention it all and maybe note some remarks on it?
>>
>>
>> In C, basically a pointer is equivalent to array.
>
> Except when it isn't. For example:

Here we go again...

Take the char** parameter type in the subject; each "*" might denote a
pointer to a single object, or a pointer to multiple objects, the common
idiom to pass an array (or possibly a string when char*) to a function.

Hence char** can be interpreted in four different ways, some of which
could involve arrays. The 'b' parameter can always be legally indexed by
non-zero values of i and j as follows:

b[i][j];

But the first and/or second index will be wrong depending on what actual
combination of arrays, if any, were intended.

But if arrays /are/ involved, then they can be handled by this single
pointer to pointer parameter. It's magic.

This is what that poster was pointing out; that's all.

>> Here we have two
>> pointers, so 4 different combinations of pointers and arrays.

Exactly.

--
bartc

John Bode

unread,
Oct 31, 2016, 10:59:09 AM10/31/16
to
There are two basic use cases:

- Your function is writing to a parameter of pointer type;
- Your function is dealing with an array of pointers, or a pointer
to a sequence of pointers.

For a function to write to a parameter of type T, it must receive a
pointer to T:

void foo( T *p )
{
*p = new_value; // write a new value to whatever p points to
}

void bar( void )
{
T var;
foo( &var ); // update the value in var
}

If you replace T with the pointer type R *, you get

void foo( R **p )
{
*p = new_value; // write a new value to whatever p points to
}

void bar( void )
{
R *var;
foo( &var ); // update the value in var
}

The semantics are exactly the same - all that's changed is the type.

If you're dealing with an array of pointers, then you're either dealing
with something like

T **a = malloc( sizeof *a * N );
for ( size_t i = 0; i < N; i++ )
a[i] = malloc( sizeof *a[i] * M );

or

T a[N]; // where N != M != O
T b[M];
T c[O];
...
T *x[] = {a, b, c, ...};

In the first case, `a` has type `T **` to begin with. In the second case, `x` "decays" from type `T *[N]` to `T **`.

Note that an actual 2D array parameter would look like

void foo( T (*a)[M] )

or

void foo( T a[][M] )

because T a[N][M] "decays" to T (*a)[M].

Keith Thompson

unread,
Oct 31, 2016, 11:04:34 AM10/31/16
to
"Pascal J. Bourguignon" <p...@informatimago.com> writes:
[...]
> In C, basically a pointer is equivalent to array.

That is completely wrong. Arrays are not pointers. Please do not
spread this dangerous misconception.

Read section 6 of the comp.lang.c FAQ, http://www.c-faq.com

Keith Thompson

unread,
Oct 31, 2016, 11:09:04 AM10/31/16
to
BartC <b...@freeuk.com> writes:
> On 31/10/2016 14:20, Richard Heathfield wrote:
>> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
[...]
>>> In C, basically a pointer is equivalent to array.
>>
>> Except when it isn't. For example:
>
> Here we go again...

Yes, here we go again.

> Take the char** parameter type in the subject; each "*" might denote a
> pointer to a single object, or a pointer to multiple objects, the common
> idiom to pass an array (or possibly a string when char*) to a function.

A (valid non-null) pointer is always a pointer to a single object. That
single object may or may not be the initial element of an array object.

> Hence char** can be interpreted in four different ways, some of which
> could involve arrays.

Yes, some uses of char** may *involve* arrays. That doesn't mean
pointers and arrays are equivalent.

[...]

> But if arrays /are/ involved, then they can be handled by this single
> pointer to pointer parameter. It's magic.

No, it's not magic. It's a consequence of a reasonably straightforward
set of rules laid out in the standard.

Stop trying to make this more confusing than it really is.

[...]

Rick C. Hodgin

unread,
Oct 31, 2016, 11:27:25 AM10/31/16
to
On Monday, October 31, 2016 at 11:09:04 AM UTC-4, Keith Thompson wrote:
> BartC <b...@freeuk.com> writes:
> > On 31/10/2016 14:20, Richard Heathfield wrote:
> >> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> [...]
> >>> In C, basically a pointer is equivalent to array.
> >>
> >> Except when it isn't. For example:
> >
> > Here we go again...
>
> Yes, here we go again.
>
> > Take the char** parameter type in the subject; each "*" might denote a
> > pointer to a single object, or a pointer to multiple objects, the common
> > idiom to pass an array (or possibly a string when char*) to a function.
>
> A (valid non-null) pointer is always a pointer to a single object. That
> single object may or may not be the initial element of an array object.

A pointer (null, or non-null) is always a pointer to an array in memory.
The only time it's not is if it's pointing to the last possible item in
physical memory.

C has a protocol which makes them different logically, and in practice
there may not be any additional elements out there, but C has no way of
knowing that as you can increment beyond the one object which exists
into invalid memory and keep computing. It's the cause of many bugs
because this physical ability exists in C, despite its logical definition
of the separate protocols.

> > Hence char** can be interpreted in four different ways, some of which
> > could involve arrays.
>
> Yes, some uses of char** may *involve* arrays. That doesn't mean
> pointers and arrays are equivalent.
>
> [...]
>
> > But if arrays /are/ involved, then they can be handled by this single
> > pointer to pointer parameter. It's magic.
>
> No, it's not magic. It's a consequence of a reasonably straightforward
> set of rules laid out in the standard.
>
> Stop trying to make this more confusing than it really is.

It's only confusing because C has a protocol which is enforced through C
and C alone, one which does not translate to physical implementations of
the language, nor even to logical implementations of the language when
errant code accesses a pointer beyond its single/multi object boundary.

C is a horrible language in this regard (of being unable to protect its
data apart from an externally enforced protocol by every developer).
It's also one of its greatest strengths.

John Bode

unread,
Oct 31, 2016, 11:27:34 AM10/31/16
to
On Monday, October 31, 2016 at 8:16:15 AM UTC-5, Pascal J. Bourguignon wrote:
> fir <profes...@gmail.com> writes:
>
> > this case when your argument of function is of a type of char**
> > has couple of usecases, im not sure if people ale fully and clearly
> > aware of them (personaly even in regard to me who rather knows c this days
> > im not sure if i totally clear see those usacases)
> >
> > is someone able to mention it all and maybe note some remarks on it?
>
>
> In C, basically a pointer is equivalent to array.

No. No, no, no, no, *no*. No.

Under most circumstances, an *expression* of type "N-element array of T"
will be converted ("decay") to an expression of type "pointer to T", but
that does not make arrays equivalent to pointers, basically or not.

John Bode

unread,
Oct 31, 2016, 12:13:06 PM10/31/16
to
On Monday, October 31, 2016 at 10:27:25 AM UTC-5, Rick C. Hodgin wrote:
> On Monday, October 31, 2016 at 11:09:04 AM UTC-4, Keith Thompson wrote:
> > BartC <b...@freeuk.com> writes:
> > > On 31/10/2016 14:20, Richard Heathfield wrote:
> > >> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> > [...]
> > >>> In C, basically a pointer is equivalent to array.
> > >>
> > >> Except when it isn't. For example:
> > >
> > > Here we go again...
> >
> > Yes, here we go again.
> >
> > > Take the char** parameter type in the subject; each "*" might denote a
> > > pointer to a single object, or a pointer to multiple objects, the common
> > > idiom to pass an array (or possibly a string when char*) to a function.
> >
> > A (valid non-null) pointer is always a pointer to a single object. That
> > single object may or may not be the initial element of an array object.
>
> A pointer (null, or non-null) is always a pointer to an array in memory.

That is an incredibly confused and garbled understanding of pointers.

NULL is a well-defined "nowhere" - it is *guaranteed* to never point to an
object or function.

A pointer value evaluates to the location of a single object. Period.

> The only time it's not is if it's pointing to the last possible item in
> physical memory.
>
> C has a protocol which makes them different logically, and in practice
> there may not be any additional elements out there, but C has no way of
> knowing that as you can increment beyond the one object which exists
> into invalid memory and keep computing.

WHICH IS WHY WE TREAT IT AS A POINTER TO A SINGLE OBJECT, NOT A SEQUENCE
OF OBJECTS. That's the only *safe* interpretation of a pointer value. Yes,
you can use that pointer value in a larger expression to access additional
elements of an array, but the *pointer value itself* denotes the location
of a single object.

Even when we're dealing with pointers to arrays like

int (*p)[10];

The expression *p evaluates to a single object - that object just happens to
be a 10-element array of int.

[snip remainder]

Rick C. Hodgin

unread,
Oct 31, 2016, 12:21:09 PM10/31/16
to
On Monday, October 31, 2016 at 12:13:06 PM UTC-4, John Bode wrote:
> On Monday, October 31, 2016 at 10:27:25 AM UTC-5, Rick C. Hodgin wrote:
> > On Monday, October 31, 2016 at 11:09:04 AM UTC-4, Keith Thompson wrote:
> > > BartC <b...@freeuk.com> writes:
> > > > On 31/10/2016 14:20, Richard Heathfield wrote:
> > > >> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> > > [...]
> > > >>> In C, basically a pointer is equivalent to array.
> > > >>
> > > >> Except when it isn't. For example:
> > > >
> > > > Here we go again...
> > >
> > > Yes, here we go again.
> > >
> > > > Take the char** parameter type in the subject; each "*" might denote a
> > > > pointer to a single object, or a pointer to multiple objects, the common
> > > > idiom to pass an array (or possibly a string when char*) to a function.
> > >
> > > A (valid non-null) pointer is always a pointer to a single object. That
> > > single object may or may not be the initial element of an array object.
> >
> > A pointer (null, or non-null) is always a pointer to an array in memory.
>
> That is an incredibly confused and garbled understanding of pointers.

It's what they resolve to through compilation. A pointer points to
something, but what it ultimately points to is a memory address. Beyond
that address exist more things which, by incrementing, you can access
through that same pointer, or through the array element, which always
then resolves to a pointer location for accessing the physical data.

If it's anything, it's a relation more to assembly language than to the
C protocols and standards.

> NULL is a well-defined "nowhere" - it is *guaranteed* to never point to an
> object or function.

It does though. Compile your valid C program that uses NULL pointers into
a 16-bit DOS application, and then write data to a NULL pointer value. It
will gladly do so. It's only the protection mechanisms of modern operating
systems that give you NULL pointer exception handling. Not every system
has that ability, and errant pointers can overwrite memory which exists at
offset 0 without issue.

> A pointer value evaluates to the location of a single object. Period.

A pointer evaluates to an address which is the location of a single object.
But if you then look at what it's pointing at/to, then you realize it is
always an array of data, except when it's pointing to the last element in
physical memory.

It's only a pointer pointing to a single object when viewed through C,
not when viewed through what it's really doing in assembly.

> > The only time it's not is if it's pointing to the last possible item in
> > physical memory.
> >
> > C has a protocol which makes them different logically, and in practice
> > there may not be any additional elements out there, but C has no way of
> > knowing that as you can increment beyond the one object which exists
> > into invalid memory and keep computing.
>
> WHICH IS WHY WE TREAT IT AS A POINTER TO A SINGLE OBJECT, NOT A SEQUENCE
> OF OBJECTS. That's the only *safe* interpretation of a pointer value. Yes,
> you can use that pointer value in a larger expression to access additional
> elements of an array, but the *pointer value itself* denotes the location
> of a single object.

As I say, it points to a location which is the position of data stored in
memory which is a single object. But those horses are all lined up then
from that pointer's point of view. Incrementing or decrementing the value
gives you another object, even if it's not valid.
>
> Even when we're dealing with pointers to arrays like
> int (*p)[10];
>
> The expression *p evaluates to a single object - that object just happens to
> be a 10-element array of int.

It does compute a single address ... but then you have to realize what
exactly is it that address is referring to? Once you realize that, you
understand what Bart's talking about, that it's just a reference to a
place where data is, right alongside other neighbor data, regardless of
whether or not that data is valid or not. And it's then up to the C
developer to take extra steps to ensure that the N-element array isn't
accessed via the Nth or larger element, or else it's out of range, or
that a pointer is incremented or decremented beyond its valid range.

C requires some pretty advanced control mechanisms to be put into place
to make sure the code is bug free. It's one of its greatest weaknesses,
and also one of its greatest strengths.

Richard Heathfield

unread,
Oct 31, 2016, 12:35:21 PM10/31/16
to
On 31/10/16 16:12, John Bode wrote:
> On Monday, October 31, 2016 at 10:27:25 AM UTC-5, Rick C. Hodgin wrote:
>> On Monday, October 31, 2016 at 11:09:04 AM UTC-4, Keith Thompson wrote:
>>> BartC <b...@freeuk.com> writes:
>>>> On 31/10/2016 14:20, Richard Heathfield wrote:
>>>>> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
>>> [...]
>>>>>> In C, basically a pointer is equivalent to array.
>>>>>
>>>>> Except when it isn't. For example:
>>>>
>>>> Here we go again...
>>>
>>> Yes, here we go again.
>>>
>>>> Take the char** parameter type in the subject; each "*" might denote a
>>>> pointer to a single object, or a pointer to multiple objects, the common
>>>> idiom to pass an array (or possibly a string when char*) to a function.
>>>
>>> A (valid non-null) pointer is always a pointer to a single object. That
>>> single object may or may not be the initial element of an array object.
>>
>> A pointer (null, or non-null) is always a pointer to an array in memory.
>
> That is an incredibly confused and garbled understanding of pointers.

Indeed.

> NULL is a well-defined "nowhere" - it is *guaranteed* to never point to an
> object or function.

Right.

> A pointer value evaluates to the location of a single object. Period.

Uh, not quite. It can evaluate to the location of a function. Functions
are not objects. And, as you say, a pointer value that is a null value
doesn't point to either an object or a function. With those exceptions,
however, and if we accept that an indeterminate value isn't a "valid"
value and so doesn't count in this context, then you are correct.

<snip>

supe...@casperkitty.com

unread,
Oct 31, 2016, 12:59:45 PM10/31/16
to
On Monday, October 31, 2016 at 11:35:21 AM UTC-5, Richard Heathfield wrote:
> Uh, not quite. It can evaluate to the location of a function. Functions
> are not objects. And, as you say, a pointer value that is a null value
> doesn't point to either an object or a function. With those exceptions,
> however, and if we accept that an indeterminate value isn't a "valid"
> value and so doesn't count in this context, then you are correct.

Null pointers are not required to point to objects, and the Standard does
not require that implementations provide any means of determining whether
they point to objects or not, but there are some hardware platforms in
which important objects are located at address zero, and for which quality
implementations should allow those objects to be accessed just like those
at any other address. If a hardware platform puts an I/O port register at
address zero (I think some of the controllers in Motorola/Freescale's
68HCxx line do that), having a pointer to that address compare equal to a
null pointer but otherwise behave normally is apt to be far more convenient
and practical than anything else an implementation might do.

Richard Heathfield

unread,
Oct 31, 2016, 1:04:58 PM10/31/16
to
On 31/10/16 16:59, supe...@casperkitty.com wrote:
> On Monday, October 31, 2016 at 11:35:21 AM UTC-5, Richard Heathfield
> wrote:
>> Uh, not quite. It can evaluate to the location of a function.
>> Functions are not objects. And, as you say, a pointer value that is
>> a null value doesn't point to either an object or a function. With
>> those exceptions, however, and if we accept that an indeterminate
>> value isn't a "valid" value and so doesn't count in this context,
>> then you are correct.
>
> Null pointers are not required to point to objects,

In fact, they are required not to. See 6.3.2.3(3): An integer constant
expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. 66) If a null pointer
constant is converted to a pointer type, the resulting pointer, called a
null pointer, is guaranteed to compare unequal to a pointer to any
object or function.

> and the Standard does
> not require that implementations provide any means of determining
> whether they point to objects or not,

If the implementation is conforming, that is the only indication needed
that null pointers do not point to objects. If the implementation is
non-conforming, well, there are other newsgroups.

fir

unread,
Oct 31, 2016, 1:36:45 PM10/31/16
to
W dniu poniedziałek, 31 października 2016 15:59:09 UTC+1 użytkownik John Bode napisał:
> On Sunday, October 30, 2016 at 7:42:23 AM UTC-5, fir wrote:
> > this case when your argument of function is of a type of char**
> > has couple of usecases, im not sure if people ale fully and clearly
> > aware of them (personaly even in regard to me who rather knows c this days
> > im not sure if i totally clear see those usacases)
> >
> > is someone able to mention it all and maybe note some remarks on it?
>
> There are two basic use cases:
>
> - Your function is writing to a parameter of pointer type;
> - Your function is dealing with an array of pointers, or a pointer
> to a sequence of pointers.
>
thats good note (in a way i asked on/got on my mind)

then some notes

1) are you or anybody sure those two those are all the cases (categories)? (how to convince self on it?)

2) [side note] it is rather bad that those two separate cases are
denoted in one way, (sooo baad.. it is so bad that im not sure if
it wouldnt be better to use in a way when youre supoposed to deal
with pack of pointer as just foo(char_pointers* b) and then recast it
internally like

void foo(char_pointers* b)
{
char** p = (char**)b;

print(p[0]);
print(p[1]);
print(p[2]);
// (if thats ok i dont touched c over half a year recently)

}

this is not rally ideal 9and should be done better) but something is on it coz char** is also bad

30 forgot third point though i got something (maybe will recall yet)

(fir)

BartC

unread,
Oct 31, 2016, 1:38:25 PM10/31/16
to
On 31/10/2016 15:10, Keith Thompson wrote:
> BartC <b...@freeuk.com> writes:
>> On 31/10/2016 14:20, Richard Heathfield wrote:
>>> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> [...]
>>>> In C, basically a pointer is equivalent to array.
>>>
>>> Except when it isn't. For example:
>>
>> Here we go again...
>
> Yes, here we go again.
>
>> Take the char** parameter type in the subject; each "*" might denote a
>> pointer to a single object, or a pointer to multiple objects, the common
>> idiom to pass an array (or possibly a string when char*) to a function.
>
> A (valid non-null) pointer is always a pointer to a single object. That
> single object may or may not be the initial element of an array object.
>
>> Hence char** can be interpreted in four different ways, some of which
>> could involve arrays.
>
> Yes, some uses of char** may *involve* arrays. That doesn't mean
> pointers and arrays are equivalent.

It means you have to use "*" when dealing with a pointer to a standalone
object, but you also have to to use "*" when dealing with a pointer to
the first element of an array that is intended to be used like an array.

In English, "*" is used, in common practice, for passing both pointers
and arrays.

Looking at Pascal JB's post again, he illustrates the possibilities
beautifully.

Four combinations, and yet all encoded as T** ! That's why I said it was
magic.

However it is also VERY confusing. Which of the four possibilities was
intended when you see T** A? If you then see A[i][j], is that
meaningful, or is it nonsensical?

> Stop trying to make this more confusing than it really is.

Better than trying to brush things under the carpet.

--
Bartc

Richard Heathfield

unread,
Oct 31, 2016, 1:44:54 PM10/31/16
to
On 31/10/16 17:38, BartC wrote:
> On 31/10/2016 15:10, Keith Thompson wrote:
>> BartC <b...@freeuk.com> writes:
>>> On 31/10/2016 14:20, Richard Heathfield wrote:
>>>> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
>> [...]
>>>>> In C, basically a pointer is equivalent to array.
>>>>
>>>> Except when it isn't. For example:
>>>
>>> Here we go again...
>>
>> Yes, here we go again.
>>
>>> Take the char** parameter type in the subject; each "*" might denote a
>>> pointer to a single object, or a pointer to multiple objects, the common
>>> idiom to pass an array (or possibly a string when char*) to a function.
>>
>> A (valid non-null) pointer is always a pointer to a single object. That
>> single object may or may not be the initial element of an array object.
>>
>>> Hence char** can be interpreted in four different ways, some of which
>>> could involve arrays.
>>
>> Yes, some uses of char** may *involve* arrays. That doesn't mean
>> pointers and arrays are equivalent.
>
> It means you have to use "*" when dealing with a pointer to a standalone
> object, but you also have to to use "*" when dealing with a pointer to
> the first element of an array that is intended to be used like an array.

Not true.

Consider:

char c = 'A';
char *pc = &c;
char arr[] = "ABCDEFG";
char *pa = arr;

printf("%c\n", *pc); /* as you say */
printf("%c\n", *pa); /* as you say */
printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
printf("%c\n", pa[0]); /* not as you say, but perfectly legal */

> In English, "*" is used, in common practice, for passing both pointers
> and arrays.

In C, you can't pass arrays, because the use of the array name in a
value context (and parameter-passing *is* a value context, because it is
the value that is passed) is treated as a pointer to the first element
of the array.

> Looking at Pascal JB's post again, he illustrates the possibilities
> beautifully.
>
> Four combinations, and yet all encoded as T** ! That's why I said it was
> magic.
>
> However it is also VERY confusing.

Only if you try very hard to be confused.

Rick C. Hodgin

unread,
Oct 31, 2016, 1:53:20 PM10/31/16
to
On Monday, October 31, 2016 at 1:38:25 PM UTC-4, Bart wrote:
> Looking at Pascal JB's post again, he illustrates the possibilities
> beautifully.

I agree. When I saw JB's post, I thought to myself I would use his
illustration for the C instruction video I'm preparing right now.

fir

unread,
Oct 31, 2016, 2:02:18 PM10/31/16
to
W dniu niedziela, 30 października 2016 18:17:24 UTC+1 użytkownik Bart napisał:
> On 30/10/2016 12:42, fir wrote:
> > this case when your argument of function is of a type of char**
> > has couple of usecases, im not sure if people ale fully and clearly
> > aware of them (personaly even in regard to me who rather knows c this days
> > im not sure if i totally clear see those usacases)
> >
> > is someone able to mention it all and maybe note some remarks on it?
>
> Here's one:
>
> void printstringarray(char** A,int n) {
> int i;
> for (i=0; i<n; ++i)
> printf("%d: %s\n", i, A[i]);
> }
>
> int main (void) {
> char* names[]={"Alan","Bill","Charlie","Dick"};
>
> printstringarray(names,4);
> }
>
> And another:
>
>
> char nextchar(char** s){
> char c = **s;
> if (c) ++*s;
> return c;
> }
>
> int main (void) {
> char* name="bartsimpson";
> char* nameptr = name;
> char c;
>
> while (c=nextchar(&nameptr)) printf("%c",c);
>
> }
>
the first is simple but second is a bit convoluted and thus not so clear as an easy example (it would be better to make something clearedr)- but right besides (later bode user wrote on those two categories)

the second case in pure form (is/shoul be) just case whwre you wrote something or pointer upscope (should write more on this )


> --
> bartc

Rick C. Hodgin

unread,
Oct 31, 2016, 2:04:14 PM10/31/16
to
Look at the underlying assembly code:

6: int main(int argc, char* argv[])
7: {
00EA1250 push ebp
00EA1251 mov ebp,esp
00EA1253 sub esp,58h
00EA1256 mov eax,dword ptr [___security_cookie (0EA6000h)]
00EA125B xor eax,ebp
00EA125D mov dword ptr [ebp-4],eax
00EA1260 push ebx
00EA1261 push esi
00EA1262 push edi
8: char c = 'A';
00EA1263 mov byte ptr [ebp-5],41h
9: char *pc = &c;
00EA1267 lea eax,[ebp-5]
00EA126A mov dword ptr [ebp-0Ch],eax
10: char arr[] = "ABCDEFG";
00EA126D mov eax,dword ptr [string "ABCDEFG" (0EA4740h)]
00EA1272 mov dword ptr [ebp-14h],eax
00EA1275 mov ecx,dword ptr ds:[0EA4744h]
00EA127B mov dword ptr [ebp-10h],ecx
11: char *pa = arr;
00EA127E lea eax,[ebp-14h]
00EA1281 mov dword ptr [ebp-18h],eax
12:
13: printf("%c\n", *pc); /* as you say */
00EA1284 mov eax,dword ptr [ebp-0Ch]
00EA1287 movsx ecx,byte ptr [eax]
00EA128A push ecx
00EA128B push offset string "%c\n" (0EA473Ch)
00EA1290 call dword ptr [__imp__printf (0EA7238h)]
00EA1296 add esp,8
14: printf("%c\n", *pa); /* as you say */
00EA1299 mov eax,dword ptr [ebp-18h]
00EA129C movsx ecx,byte ptr [eax]
00EA129F push ecx
00EA12A0 push offset string "%c\n" (0EA473Ch)
00EA12A5 call dword ptr [__imp__printf (0EA7238h)]
00EA12AB add esp,8
15: printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
00EA12AE mov eax,dword ptr [ebp-0Ch]
00EA12B1 movsx ecx,byte ptr [eax]
00EA12B4 push ecx
00EA12B5 push offset string "%c\n" (0EA473Ch)
00EA12BA call dword ptr [__imp__printf (0EA7238h)]
00EA12C0 add esp,8
16: printf("%c\n", pa[0]); /* not as you say, but perfectly legal */
00EA12C3 mov eax,dword ptr [ebp-18h]
00EA12C6 movsx ecx,byte ptr [eax]
00EA12C9 push ecx
00EA12CA push offset string "%c\n" (0EA473Ch)
00EA12CF call dword ptr [__imp__printf (0EA7238h)]
00EA12D5 add esp,8
17: }
00EA12D8 xor eax,eax
00EA12DA pop edi
00EA12DB pop esi
00EA12DC pop ebx
00EA12DD mov ecx,dword ptr [ebp-4]
00EA12E0 xor ecx,ebp
00EA12E2 call @ILT+10(@__security_check_cookie@4) (0EA100Fh)
00EA12E7 mov esp,ebp
00EA12E9 pop ebp
00EA12EA ret

And specifically at these two lines for example:

13:printf("%c\n", *pc); /* as you say */
00EA1284 mov eax,dword ptr [ebp-0Ch]
00EA1287 movsx ecx,byte ptr [eax]
00EA128A push ecx
00EA128B push offset string "%c\n" (0EA473Ch)
00EA1290 call dword ptr [__imp__printf (0EA7238h)]
00EA1296 add esp,8

15:printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
00EA12AE mov eax,dword ptr [ebp-0Ch]
00EA12B1 movsx ecx,byte ptr [eax]
00EA12B4 push ecx
00EA12B5 push offset string "%c\n" (0EA473Ch)
00EA12BA call dword ptr [__imp__printf (0EA7238h)]
00EA12C0 add esp,8

Now consecutively, line-by-line:

13:printf("%c\n", *pc); /* as you say */
15: printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
284 mov eax,dword ptr [ebp-0Ch] // 13
2AE mov eax,dword ptr [ebp-0Ch] // 15
287 movsx ecx,byte ptr [eax] // 13
2B1 movsx ecx,byte ptr [eax] // 15
28A push ecx // 13
2B4 push ecx // 15
28B push offset string "%c\n" (0EA473Ch) // 13
2B5 push offset string "%c\n" (0EA473Ch) // 15
290 call dword ptr [__imp__printf (0EA7238h)] // 13
2BA call dword ptr [__imp__printf (0EA7238h)] // 15
296 add esp,8 // 13
2C0 add esp,8 // 15

They resolve to the exact same fundamental operations on the machine,
despite their being different logically in C.

> In C, you can't pass arrays, because the use of the array name in a
> value context (and parameter-passing *is* a value context, because it is
> the value that is passed) is treated as a pointer to the first element
> of the array.

That's a C protocol. You can perform the exact same operation by using
the address of the array through a union and reconstituting it on the
other side of the call.

Datawise it's no different, only protocol-wise.

fir

unread,
Oct 31, 2016, 2:12:07 PM10/31/16
to
W dniu niedziela, 30 października 2016 21:49:27 UTC+1 użytkownik Rick C. Hodgin napisał:
> On Sunday, October 30, 2016 at 3:14:05 PM UTC-4, supe...@casperkitty.com wrote:
> > [snip]
> > if (size) do
> > {
> > if (!*p) return 1;
> > } while(--size);
> > [snip]
>
> I've never seen this use of syntax before. I like it. I would rewrite
> slightly to move the do keyword down to the next line:
>
> if (test)
> do {
> // Code here
> } while(second_test);
>
> I really like that syntax. It easily exposes the two test conditions. I
> wonder if a dedicated syntax like...:
>
> do if (test)
> {
> // Code here
> } while (second_test);
>
> ...would be desirable?
>
not quite bad idea.. interesting maybe could be used (if ifs and loops combine on upper semantic level in some forms like that, maybe they do)

John Bode

unread,
Oct 31, 2016, 2:21:11 PM10/31/16
to
True, but since Rick was talking about arrays, I subconsciously focused on
object pointers as opposed to function pointers (since you cannot create an
array of functions).

I think part of the disconnect is people thinking in terms of what happens
in the machine code on a particular implementation, rather than the abstract
machine defined by the C language. Yeah, sure, on real-world hardware, almost any address value can be the start of a sequence of bytes/words/whatever, but that's not how the language views it.

An object pointer denotes the location of a single object, not a sequence
of objects, *even if that pointer is the address of the first element of
an array*. If I do something like

int a[10] = {0};
int *p = a;

then the expression *p evaluates to the value of a[0] - that's it. Not the
contents of the array. The *language* does not regard `p` as the first of
a sequence of objects, even though in this case we are treating it as such.
sizeof *p gives us the size of a single integer, not the size of the array
to which p is pointing.

Rick C. Hodgin

unread,
Oct 31, 2016, 2:24:07 PM10/31/16
to
You do know that nobody writes C code on a whiteboard, right?

> Yeah, sure, on real-world hardware, almost any address value can be the start of a sequence of bytes/words/whatever, but that's not how the language views it.

It should be because we live in a world of reality, not abstract theory.

> An object pointer denotes the location of a single object, not a sequence
> of objects, *even if that pointer is the address of the first element of
> an array*. If I do something like
>
> int a[10] = {0};
> int *p = a;
>
> then the expression *p evaluates to the value of a[0] - that's it. Not the
> contents of the array. The *language* does not regard `p` as the first of
> a sequence of objects, even though in this case we are treating it as such.
> sizeof *p gives us the size of a single integer, not the size of the array
> to which p is pointing.

Of course, all of this is just my opinion. But I'm probably right.

Richard Heathfield

unread,
Oct 31, 2016, 2:33:54 PM10/31/16
to
On 31/10/16 18:20, John Bode wrote:
> On Monday, October 31, 2016 at 11:35:21 AM UTC-5, Richard Heathfield
> wrote:
>> On 31/10/16 16:12, John Bode wrote:

<snip>

>>> A pointer value evaluates to the location of a single object.
>>> Period.
>>
>> Uh, not quite. It can evaluate to the location of a function.
>
> True, but since Rick was talking about arrays, I subconsciously
> focused on object pointers as opposed to function pointers (since
> you cannot create an array of functions).

I know, of course, that you know about function pointers. On the other
hand, you will be able to sleep easy tonight without scratching, because
your nits have been well and truly picked.

> I think part of the disconnect is people thinking in terms of what
> happens in the machine code on a particular implementation, rather
> than the abstract machine defined by the C language.

I presume so, yes. By the time I encountered C, I'd already programmed
on an IBM 4341 mainframe, various bitty-boxes, the 68000, and PCs. So I
was rather grateful for the abstractness of the abstract machine!

> Yeah, sure, on real-world hardware, almost any address value can be
> the start of a sequence of bytes/words/whatever, but that's not how
> the language views it.

Precisely so. I have written my fair share of code that peeks around
memory and pokes it in various ways, and that's all well and good, but I
never fooled myself that I was working within the guarantees of the C
language.

> An object pointer denotes the location of a single object, not a
> sequence of objects, *even if that pointer is the address of the
> first element of an array*. If I do something like
>
> int a[10] = {0}; int *p = a;
>
> then the expression *p evaluates to the value of a[0] - that's it.
> Not the contents of the array. The *language* does not regard `p`
> as the first of a sequence of objects, even though in this case we
> are treating it as such.

Um, it sort of does, actually, which is why you can do this:

int single_object;
int *p = &single_object;
int *q = p + 1;
while(p < q)
{
printf("%d\n", *p++);
}

The language explicitly gives us permission to form (but not
dereference) q precisely /because/ p (after being pointed to an object)
is considered to be a pointer to the first of one or more contiguous
objects.

> sizeof *p gives us the size of a single
> integer, not the size of the array to which p is pointing.

Yes, of course, because p isn't pointing to an array. If it were (and it
is perfectly possible to define and initialise such a pointer), sizeof
*p would indeed give the size of the array:

int xarr[10] = {0};
int (*p)[10] = &xarr;

Now sizeof *p == sizeof xarr.

John Bode

unread,
Oct 31, 2016, 2:37:00 PM10/31/16
to
On Monday, October 31, 2016 at 12:36:45 PM UTC-5, fir wrote:
> W dniu poniedziałek, 31 października 2016 15:59:09 UTC+1 użytkownik John Bode napisał:
> > On Sunday, October 30, 2016 at 7:42:23 AM UTC-5, fir wrote:
> > > this case when your argument of function is of a type of char**
> > > has couple of usecases, im not sure if people ale fully and clearly
> > > aware of them (personaly even in regard to me who rather knows c this days
> > > im not sure if i totally clear see those usacases)
> > >
> > > is someone able to mention it all and maybe note some remarks on it?
> >
> > There are two basic use cases:
> >
> > - Your function is writing to a parameter of pointer type;
> > - Your function is dealing with an array of pointers, or a pointer
> > to a sequence of pointers.
> >
> thats good note (in a way i asked on/got on my mind)
>
> then some notes
>
> 1) are you or anybody sure those two those are all the cases (categories)? (how to convince self on it?)

Reasonably sure. People *may* be using pointers to pointers in some other,
more creative ways, but those two cases cover the bulk of it.

There are only so many ways to obtain a pointer to pointer value:

- Use the & operator on an lvalue of pointer type;
- Use an array expression of type "array of pointer" in a value context
- Declare an object as a pointer to a pointer, then construct and assign
a value accordingly (i.e., nested malloc() calls).

>
> 2) [side note] it is rather bad that those two separate cases are
> denoted in one way, (sooo baad.. it is so bad that im not sure if
> it wouldnt be better to use in a way when youre supoposed to deal
> with pack of pointer as just foo(char_pointers* b) and then recast it
> internally like
>
> void foo(char_pointers* b)
> {
> char** p = (char**)b;
>
> print(p[0]);
> print(p[1]);
> print(p[2]);
> // (if thats ok i dont touched c over half a year recently)
>
> }
>
> this is not rally ideal 9and should be done better) but something is on it coz char** is also bad
>
> 30 forgot third point though i got something (maybe will recall yet)
>
> (fir)
>

Yeah. dmr wanted to have his cake (preserve B's array semantics) and eat it
too (not materialize the pointer B's array semantics demanded). Thus we're
left with a notational ambiguity. It would be nice if arrays kept their
"array-ness" in all contexts, but they don't. We can deal with it (griping
about it all the way), or we can use a different language.

I've been using C long enough (and the resulting brain damage is severe
enough) that it doesn't bother me anymore. Usually I can tell what the
intent is immediately.

[snip remainder]

John Bode

unread,
Oct 31, 2016, 2:41:40 PM10/31/16
to
On Monday, October 31, 2016 at 1:33:54 PM UTC-5, Richard Heathfield wrote:
> On 31/10/16 18:20, John Bode wrote:
> > On Monday, October 31, 2016 at 11:35:21 AM UTC-5, Richard Heathfield
> > wrote:
> >> On 31/10/16 16:12, John Bode wrote:
>
> <snip>
>
> >>> A pointer value evaluates to the location of a single object.
> >>> Period.
> >>
> >> Uh, not quite. It can evaluate to the location of a function.
> >
> > True, but since Rick was talking about arrays, I subconsciously
> > focused on object pointers as opposed to function pointers (since
> > you cannot create an array of functions).
>
> I know, of course, that you know about function pointers. On the other
> hand, you will be able to sleep easy tonight without scratching, because
> your nits have been well and truly picked.
>

Ew.

I don't think we know each other well enough for that. :-)
Like I said somewhere above, we can use that pointer as part of a larger
expression that allows us to access other objects in a sequence. But that
pointer value itself always denotes a single object.

BartC

unread,
Oct 31, 2016, 2:51:27 PM10/31/16
to
On 31/10/2016 18:20, John Bode wrote:

> An object pointer denotes the location of a single object, not a sequence
> of objects, *even if that pointer is the address of the first element of
> an array*. If I do something like
>
> int a[10] = {0};
> int *p = a;
>
> then the expression *p evaluates to the value of a[0] - that's it. Not the
> contents of the array. The *language* does not regard `p` as the first of
> a sequence of objects, even though in this case we are treating it as such.
> sizeof *p gives us the size of a single integer, not the size of the array
> to which p is pointing.

But if you wanted to apply a function to the array 'a', would you do
that like this:

void fn1(int (*A)[]) {printf("%d\n",(*A)[4]);}

fn1(&a);

or like this:

void fn2(int *A) {printf("%d\n",A[4]);}

fn2(a); // or fn2(p);

So although 'a' and p are pointers to only the first element of the
error, EVERYBODY uses them to refer to the ENTIRE array.

This is my some of ue can see there's a huge mix-up between pointers and
arrays, while others insist they are quite different.

(And sizeof *p doesn't give the array size because that information is
now missing. The EFFECTIVE array that p points to the first element of
is unbounded. And both fn1 and fn2 really need a length parameter.)

--
bartc

BartC

unread,
Oct 31, 2016, 3:12:22 PM10/31/16
to
On 31/10/2016 17:44, Richard Heathfield wrote:
> On 31/10/16 17:38, BartC wrote:

>> It means you have to use "*" when dealing with a pointer to a standalone
>> object, but you also have to to use "*" when dealing with a pointer to
>> the first element of an array that is intended to be used like an array.
>
> Not true.
>
> Consider:
>
> char c = 'A';
> char *pc = &c;
> char arr[] = "ABCDEFG";
> char *pa = arr;
>
> printf("%c\n", *pc); /* as you say */
> printf("%c\n", *pa); /* as you say */
> printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
> printf("%c\n", pa[0]); /* not as you say, but perfectly legal */

I meant for declaring parameters that are intended to deal indirectly
with arrays. Although you can mix it up a bit by using [] in place of a
*, at the risk of confusion as [] in another context doesn't declare a
pointer.

>> In English, "*" is used, in common practice, for passing both pointers
>> and arrays.
>
> In C, you can't pass arrays, because the use of the array name in a
> value context (and parameter-passing *is* a value context, because it is
> the value that is passed) is treated as a pointer to the first element
> of the array.

In English again: the common way of 'passing an array to a function' is
to pass a pointer to that array's first element. That very often uses
"*" in the parameter type.

Effectively, "*", a pointer designator, is used to 'pass an array'.

>> However it is also VERY confusing.
>
> Only if you try very hard to be confused.

I didn't have to try hard. And I still don't know, when you see T** as
as parameter, exactly what data structure is being passed.

--
Bartc

fir

unread,
Oct 31, 2016, 3:13:06 PM10/31/16
to
i know java python folks who sees normal pointers in c as a kind of blackmagic - not to say a tens of thousands o c intermediates who im sure do understand char* passings but still get lost in char** usecases (i remember i was lost by this in some winapi calls or opencl setup calls not quite many years ago)

anyway this char** 'disambiguition' is a c sin becouse it is contra this same way that made types born - types are to reader know what code do not to pass raws anywhere and do possibly all, here we got just lask of some kind of typing (it occurs already in foo(char*b) bat here it is possiblu even more confusing

(as to those raws btw i think c shpuld probably still support it but as an option for maniacs (im a fan of options it seems) but normal healthy types shpuld be avaliable)

besides im still not sure people can do some weirdest things with this type** passings (as for example many pointers write with mallocks may be involved to make weird things - sadli i dont remember what winapi and opencl setup was doin with this that was so confusing for me)

supe...@casperkitty.com

unread,
Oct 31, 2016, 3:16:44 PM10/31/16
to
On Monday, October 31, 2016 at 12:04:58 PM UTC-5, Richard Heathfield wrote:
> In fact, they are required not to. See 6.3.2.3(3): An integer constant
> expression with the value 0, or such an expression cast to type
> void *, is called a null pointer constant. 66) If a null pointer
> constant is converted to a pointer type, the resulting pointer, called a
> null pointer, is guaranteed to compare unequal to a pointer to any
> object or function.

It compares unequal to any object or function *that would be recognized by
the Standard*. The Standard does not recognize the existence of an I/O
port control register located at address zero, nor does it specify anything
about what happens if code tries to dereference a pointer that compares
equal to null. On the other hand, if a piece of hardware does happen to
have a port at address zero, the simplest and most practical way for a
quality implementation targeting that platform to support access to that
port is to simply treat zero like any other address. The Standard does
not require such behavior, but nothing in the Standard would prohibit it
either. A conforming implementation for that platform could make address
zero inaccessible, but from the point of view of the Standard it could just
as well deny access to all hardware addresses that aren't used to hold
Standard-recognized C objects. A freestanding implementation that did that
wouldn't be very useful, of course, but would be allowable under the
Standard nonetheless.

Richard Heathfield

unread,
Oct 31, 2016, 3:18:49 PM10/31/16
to
On 31/10/16 19:12, BartC wrote:
> And I still don't know, when you see T** as
> as parameter, exactly what data structure is being passed.

If you're writing the function, you /decide/ what data structure is
being passed, and then you document the function so that people who call
it will know what to pass it.

If you're not writing the function, you find the documentation for that
function, and read it.

Either way, it's easy to know what data structure should be passed.

If that turns out to be different from what is actually being passed,
well, you found your bug, so fix it and move on.

Richard Heathfield

unread,
Oct 31, 2016, 3:20:04 PM10/31/16
to
On 31/10/16 19:16, supe...@casperkitty.com wrote:
> On Monday, October 31, 2016 at 12:04:58 PM UTC-5, Richard Heathfield wrote:
>> In fact, they are required not to. See 6.3.2.3(3): An integer constant
>> expression with the value 0, or such an expression cast to type
>> void *, is called a null pointer constant. 66) If a null pointer
>> constant is converted to a pointer type, the resulting pointer, called a
>> null pointer, is guaranteed to compare unequal to a pointer to any
>> object or function.
>
> It compares unequal to any object or function *that would be recognized by
> the Standard*.

Yes. If the Standard doesn't recognise it as an object or function, then
it isn't an object or function.

> The Standard does not recognize the existence of an I/O
> port control register located at address zero,

Agreed. So if that's your issue, you need to raise it in a newsgroup
that discusses your particular platform.

fir

unread,
Oct 31, 2016, 3:59:29 PM10/31/16
to
>
> anyway this char** 'disambiguition'

sorry i wnated to mean 'ambiguity' (probably - i seen this word disambiguition on wiki pages always thought that mean sorta like ambiguity, now checked in dictionary and it means not what i thought)

fir

unread,
Oct 31, 2016, 4:27:14 PM10/31/16
to
W dniu poniedziałek, 31 października 2016 18:36:45 UTC+1 użytkownik fir napisał:
> W dniu poniedziałek, 31 października 2016 15:59:09 UTC+1 użytkownik John Bode napisał:
> > On Sunday, October 30, 2016 at 7:42:23 AM UTC-5, fir wrote:
> > > this case when your argument of function is of a type of char**
> > > has couple of usecases, im not sure if people ale fully and clearly
> > > aware of them (personaly even in regard to me who rather knows c this days
> > > im not sure if i totally clear see those usacases)
> > >
> > > is someone able to mention it all and maybe note some remarks on it?
> >
> > There are two basic use cases:
> >
> > - Your function is writing to a parameter of pointer type;
> > - Your function is dealing with an array of pointers, or a pointer
> > to a sequence of pointers.
> >
> thats good note (in a way i asked on/got on my mind)
>
> then some notes
>
> 1) are you or anybody sure those two those are all the cases (categories)? (how to convince self on it?)
>
> 2) [side note] it is rather bad that those two separate cases are
> denoted in one way, (sooo baad.. it is so bad that im not sure if
> it wouldnt be better to use in a way when youre supoposed to deal
> with pack of pointer as just foo(char_pointers* b) and then recast it
> internally like
>
> void foo(char_pointers* b)
> {


by the way in some posts i was writing some way ago i wa sain about some
way in c you could really do this 'pointer to pointers to strings' usecase more elegantly, one coud use 'chunks' type and pass it by value, chunks is a structure containing two pointers (.begin .end) pointing on first and last entry in some array of chunks (each chunk itself is a pait of .begin .end pointers pointing on chars of some strings located in ram)

void foo(chunks words)
{
// acces is then like
// words.begin[word].begin[letter]

}

this is somewhat surprising effect of some of my deeper thinking on it,
it removes many problems and surprising is it fits in big extent in present c (side note)

fir

unread,
Oct 31, 2016, 4:40:37 PM10/31/16
to
W dniu poniedziałek, 31 października 2016 18:36:45 UTC+1 użytkownik fir napisał:
> W dniu poniedziałek, 31 października 2016 15:59:09 UTC+1 użytkownik John Bode napisał:
> > On Sunday, October 30, 2016 at 7:42:23 AM UTC-5, fir wrote:
> > > this case when your argument of function is of a type of char**
> > > has couple of usecases, im not sure if people ale fully and clearly
> > > aware of them (personaly even in regard to me who rather knows c this days
> > > im not sure if i totally clear see those usacases)
> > >
> > > is someone able to mention it all and maybe note some remarks on it?
> >
> > There are two basic use cases:
> >
> > - Your function is writing to a parameter of pointer type;

this above case also seems to me clearer is to use just as

foo(some* p)

and then assign to *p - for some time i began to think or at least speculate that ** and more 'rank' pointers in c are bad idea (and it seem to fit this examples too)
(i should say more on this maybe but got no specific thing to say right now) ** are overconfusing, (*** probably nobody uses, so it would make ** 'worst evil' here

** seem to be a bad lane in language, not that i dislike c complexity, i like c complexity but this is maybe wrong way to do complexity)

fir

unread,
Oct 31, 2016, 7:05:06 PM10/31/16
to
i do not following (in read) this pointers vs arrays but one could say that if c has a lot of arrays (where most of it just are one element ones like "int x=5;" ;c) then all we got are just arrays and pointers to arrays (all pointers are always pointers to arrays - this is probably conceptuali quite ok, though lack of possibly 'healthy' size typing is again somewhat a glitch) [this dont mean that poiinters and arrays are the same - pointers and arrays are totlly differanyt things, nothing in common]

fir

unread,
Oct 31, 2016, 7:22:41 PM10/31/16
to
underlying semantics are quite defined BTW so some syntax could be easilu used; not saying that this is good looking, but as semantic example:

int - integer
int[2] - table of two integers
int* - pointer to integer
int[7]* - pointer to table of 7 integers
int*[6] - table of 6 pointers to integers
int[3][2] - table of two tables of 3 integers
int*[6]*[4]** - pointer to pointer to table of four pointers to table of six pointers of integers (if im not wrong)

round parenthesis could be also used instead of square, im not quite sure how present c allows to code this information but it seems that full typing like thic could be encoded - it may semm that i contradict to myslelf saying that i consider such things like int**** as wrong and here typing this as okay, but im saying about somewhat different things (as in present c int**** or **** by slef is some kind of weird and wide category (which i found as probably harmfull) and here above this is strictly hardly defined simple type)

BartC

unread,
Oct 31, 2016, 7:45:27 PM10/31/16
to
On 31/10/2016 23:22, fir wrote:

> underlying semantics are quite defined BTW so some syntax could be easilu used; not saying that this is good looking, but as semantic example:
>
> int - integer
> int[2] - table of two integers
> int* - pointer to integer
> int[7]* - pointer to table of 7 integers
> int*[6] - table of 6 pointers to integers
> int[3][2] - table of two tables of 3 integers
> int*[6]*[4]** - pointer to pointer to table of four pointers to table of six pointers of integers (if im not wrong)

There are any number of schemes that could have been used; it would be
difficult for any of them to be worse than what there is now.

It's not clear where the names go in your examples, but it still suffers
from some of the same problems: arrays seem be declared in reverse order
for instance.

I use a scheme which would look like this to declare the above (except I
use "ref" or "pointer to" instead of "*"):

int A;
[2]int B;
*int C;
*[7]int D, E;
[6]*int F;
[2,3]int G;
**[4]*[6]*int H;

Everything reads left to right. The name is always on one end of the
type, not somewhere in the middle. And both D and E here have exactly
the same type.

(Actually it is so simple to come up with something like this, that is
easy, intuitive to write and to read, that you have to wonder what went
wrong. It's not as though C was the first language with type declarations.)


--
Bartc

fir

unread,
Oct 31, 2016, 7:47:59 PM10/31/16
to
how to distinguish those mentioned two usecases in this syntac those with pack od strings would look like

void foo(char[]*[]* args)
{
// (*args)[i] would be i-th pointer to i-th string
}

void foo(char[]** x)
{
*x = "cat";
}

could be used for outerscope writing, both could be use for writing or reading and this is stil ambiguity whats why it is need to distinguish on types for read and tyopes for write.. i must check if one additional symbol for pointer to only-read will suffice

fir

unread,
Oct 31, 2016, 7:58:52 PM10/31/16
to
i decided once that java-like

int[] table;

is somewhat more logical than c ones [i wrote on this in this group maybe someone remember tis] (as it is important to have 'concise' type name like int[] not in two separate parts like int and [] far away), and the above is somewhat coherent to this view

as c defines array as a[] not []a it is better to get it right (a[]) then to not jump pointers to it and more arrays should grow right imo - it all loooks a bit bad but maybe not much more terribly or matbe even a bit less, as it is now - but also im not sure if there shouldnt be a word pointer or ptr instead of *

(syntax is btw a bit side thing to what im sain on - i said mainly on idea of just more concise typesafety as present c has just big holes in it )





>
> --
> Bartc

fir

unread,
Oct 31, 2016, 8:11:59 PM10/31/16
to
to get it on right and not jump it would probably also mean that dereferencing pointer also should be right like

void foo(int* b)
{
b* = 10;
}

other story is if all this pointers are so needed, probably no and normal references with some extensions my suffice

int a = 2;

(by normal reference i mean 'a' label in above)

BartC

unread,
Oct 31, 2016, 9:27:52 PM10/31/16
to
On 01/11/2016 00:11, fir wrote:


> to get it on right and not jump it would probably also mean that dereferencing pointer also should be right like
>
> void foo(int* b)
> {
> b* = 10;
> }

Yes, a postfix dereference op would have solved some problems (although
not with * as 'b* = 10' currently means 'b *= 10').

>> (syntax is btw a bit side thing to what im sain on - i said mainly on idea of just more concise typesafety as present c has just big holes in it )

Syntax can be very important. The poor design of the way C types are
specified must have an adverse affect overall.

And if there was a postfix deref op as above then you wouldn't have
needed "->" for struct pointers.

You'd be able to use pointers to actual arrays, as the syntax would be
simpler: A*[i] instead of (*A)[i], instead of messing around with
pointers to elements and having all these silly rules. (But with
something else in place of *.)

--
Bartc

Ben Bacarisse

unread,
Oct 31, 2016, 9:45:37 PM10/31/16
to
BartC <b...@freeuk.com> writes:
<snip>
> What /I/ can't understand is how C distinguishes from a
> while-statement with an empty body, from the while-part of do-while:
>
> do while (g==h); while (k==l);
>
> Is this a do-while loop controlled by (k==l), with a single statement
> in its body consisting of the loop 'while (g==h)'?

Yes.

> Or a do-loop with
> an empty body controlled by (g==h), followed by a separate while-loop
> with an empty body?

No.

The grammar makes it clear: C's compound control statements (switch, if,
while, for and do) all require a statement to be controlled.

<snip>
--
Ben.

fir

unread,
Nov 1, 2016, 5:31:55 AM11/1/16
to
W dniu wtorek, 1 listopada 2016 02:27:52 UTC+1 użytkownik Bart napisał:
> On 01/11/2016 00:11, fir wrote:
>
>
> > to get it on right and not jump it would probably also mean that dereferencing pointer also should be right like
> >
> > void foo(int* b)
> > {
> > b* = 10;
> > }
>
> Yes, a postfix dereference op would have solved some problems (although
> not with * as 'b* = 10' currently means 'b *= 10').
>

the question is if pointers shouldnt be automatically dereferenced,
like

int a = 1;
int* b = &a;
b = 0; //makes a = 0

code that uses a lot of *b (like *b = (*b)*(*b)+(*b) instead of b=b*b+b)
looks bad and somewhat illogical as *b may be seen as many dereferencings of some adress b whan in assembly you work on *b value and forget an adress ) so probably this dereferencing operator could be skipped (only present in type name not in codes)

int*[10]*[10] tab;

for(int j=0; j<10; j++)
for(int i=0; i<10; i++)
tab[i][j] = i*j;

acces to pointers would be &tab[i][j] and &tab[][j]

fir

unread,
Nov 1, 2016, 5:41:31 AM11/1/16
to
>
> int*[10]*[10] tab;
>
> for(int j=0; j<10; j++)
> for(int i=0; i<10; i++)
> tab[i][j] = i*j;
>
> acces to pointers would be &tab[i][j] and &tab[][j]
>

this abiove should be probably understood as an example that this said typehole in c can be probaby fully 'patched' just this way

this shows the (maybe mentioned) diference betwean

int*[10]*[10] tab; //table of 110 pointers (and tied 100 underlying values)
int[10][10] tab; // normal table of 100 values
int[10][10]* tab; // pointer to table of 100 values and so on other combinations


asetof...@gmail.com

unread,
Nov 1, 2016, 6:15:49 AM11/1/16
to
fir wrote:
the question is if pointers shouldnt be automatically dereferenced,
like

int a = 1;
int* b = &a;
b = 0; //makes a = 0
-------
b=0 doesn't make a=0
*b=0 makes a=0
I agree pointers have not to be
"automatically deferenced"

Ben Bacarisse

unread,
Nov 1, 2016, 7:14:21 AM11/1/16
to
BartC <b...@freeuk.com> writes:
<snip>
> Yes, a postfix dereference op would have solved some problems
> (although not with * as 'b* = 10' currently means 'b *= 10').

I'm not entirely sure what you mean here (since there is a hypothetical
being discussed) but 'b* = 10' is currently a syntax error and, given a
hypothetical postfix * operator, would mean '(b*) = 10' unless some very
dramatic changes were made to how C defines its lexical tokens.

<snip>
--
Ben.

BartC

unread,
Nov 1, 2016, 7:30:22 AM11/1/16
to
You mean that "*=" can't have a space in the middle?

OK, maybe a postfix * might just about be viable after all, with the
right white-space and appropriate use of parentheses. But I'm still not
sure it would be that attractive a proposition:

f*(a+b); // looks like f multiplied by a+b
(f*)(a+b); // call function at f* with argument a+b
b *= 10; // multiply b by 10
b * = 10; // store 10 in b* (ie. the current *b)

It looks too much like another set of quirks in the making.

--
Bartc

fir

unread,
Nov 1, 2016, 8:08:05 AM11/1/16
to
good note (if true, i cant even check it as im not touching compiler over half a year )
btw the context of what im sain (to short some reslults) is that

int*[5]**[6]** a;

is concise type syntax which repairs some big type hole in c

as to dereference ir at rightside though it seems anyway that reversed order need to be used

a**[3]**[0]* = 10

though it seems now that leftside dereference

*[5]**[6]**a = 10
would also work so maybe leftside is more logical

Ben Bacarisse

unread,
Nov 1, 2016, 8:10:43 AM11/1/16
to
BartC <b...@freeuk.com> writes:

> On 01/11/2016 11:14, Ben Bacarisse wrote:
>> BartC <b...@freeuk.com> writes:
>> <snip>
>>> Yes, a postfix dereference op would have solved some problems
>>> (although not with * as 'b* = 10' currently means 'b *= 10').
>>
>> I'm not entirely sure what you mean here (since there is a hypothetical
>> being discussed) but 'b* = 10' is currently a syntax error and, given a
>> hypothetical postfix * operator, would mean '(b*) = 10' unless some very
>> dramatic changes were made to how C defines its lexical tokens.
>
> You mean that "*=" can't have a space in the middle?

Yes. Tokens can't include spaces and every operator is a single token.

<snip>
--
Ben.

fir

unread,
Nov 1, 2016, 8:18:21 AM11/1/16
to
i mean *[0]**[0]**a = 10 (as this abowe would be out of bounds of arrays)

but no becoz if
int*[10] a is table of 10 pointers

dereference would need look like

[3]a - is a 3rd pointer
*[3]a - is a dereferenced int

this is reversed, so a[3] and a[3]* seems more fit, though it
is reversed
seems no way to be not a bit reversed here

fir

unread,
Nov 1, 2016, 8:30:53 AM11/1/16
to
(soprry i split meesages on the same topisc on a couple but this is more convenient)

after all it seems so

I) this one is least 'physically' reversed

int*[320][200] a; //is a table of 320x200 pointers to int

[199]a; // is one inner teble of 320 pointers to int
*[30][20]a; //is some dereferenced int

but it is reversed in regard to c convention of a[] for arrays

II) this one is reversed but is more coherent to c convention

int*[320][200] a; //is a table of 320x200 pointers to int

a[199];
a[20][30]*;

for my ovn stylistic sense probably the second would more fit 9though is more reversed)

fir

unread,
Nov 1, 2016, 8:39:05 AM11/1/16
to
bTw if im not wronk it shows as a side not that c convention for arrays

a[y][x] (like a[200][320] when the most right indexes the smallest)

is WRONG it should be a[x][y] (becouse you should be able to skip outer
(here outer is [y] and left the inner make still sense, in forst case it has not much sense so a[x][y][z] is proper)

fir

unread,
Nov 1, 2016, 8:51:28 AM11/1/16
to
in reality even b*=10 shouldnt rise a conflict becouse b is not int 9nor float nor char etc) its a pointer and you reading the code should know that (compiler knows that so

//int* b
b*=10;
//int b
b*=10;

would really work in both cases 9it was already talked that syntax overloading on types ir rather good in c than bad, it is not hard to chec types yet you really should know types becouse even in normal arithmetic if you dont know the type of b (if it is int char or float it is not normal))

supe...@casperkitty.com

unread,
Nov 1, 2016, 9:27:09 AM11/1/16
to
On Tuesday, November 1, 2016 at 6:30:22 AM UTC-5, Bart wrote:
> OK, maybe a postfix * might just about be viable after all, with the
> right white-space and appropriate use of parentheses. But I'm still not
> sure it would be that attractive a proposition:

Some other languages use words for their bitwise operators and/or use the
at-sign and dollar-sign characters, and thus have space in the character
sets for a dedicated dereferencing character. Given the format of the
compound-assignment operators and the overloading of * for both multiply
and dereferencing, using the operator in prefix form might be better than
using that same token in postfix form, but that doesn't mean that a postfix
form using a different token wouldn't be better yet.

fir

unread,
Nov 1, 2016, 10:33:23 AM11/1/16
to
array [] is on right and if this sign * is on left that would mean
weird jumping like *((*(a[]))[]) where when its on right it is much simpler a[]*[]*

Tim Rentsch

unread,
Nov 1, 2016, 10:56:02 AM11/1/16
to
BartC <b...@freeuk.com> writes:

> What /I/ can't understand is how C distinguishes from a
> while-statement with an empty body, from the while-part of do-while:
>
> do while (g==h); while (k==l);
>
> Is this a do-while loop controlled by (k==l), with a single statement
> in its body consisting of the loop 'while (g==h)'? Or a do-loop with
> an empty body controlled by (g==h), followed by a separate while-loop
> with an empty body?

Do you not know, or not understand, how to read ordinary syntax
rules? The syntax rules for C answer this question fairly
easily.

mark.b...@gmail.com

unread,
Nov 1, 2016, 11:07:22 AM11/1/16
to
It has already been determined that Bart is concerned
with criticising C not understanding it...

(BTW isn't it interesting how "fir" threads tend to end
up with him talking to himself for a lot of the time?)

Jerry Stuckle

unread,
Nov 1, 2016, 11:23:15 AM11/1/16
to
What about the ?: (ternary) operator?

--
==================
Remove the "x" from my email address
Jerry Stuckle
jstu...@attglobal.net
==================

Keith Thompson

unread,
Nov 1, 2016, 11:24:01 AM11/1/16
to
Keith Thompson <ks...@mib.org> writes:
[...]
> A (valid non-null) pointer is always a pointer to a single object. That
> single object may or may not be the initial element of an array object.
[...]

Sorry, I missed a couple of cases. A valid pointer can point just
past the last element of an array object, and a non-null pointer
of pointer-to-function type can point to a function.

And for purposes of pointer arithmetic, a single object is treated
as a element of a single-element array.

So, for example, this sets p to a valid pointer value (which cannot be
derferenced):

int n;
int *p = &n + 1;

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson

unread,
Nov 1, 2016, 11:31:13 AM11/1/16
to
supe...@casperkitty.com writes:
> On Monday, October 31, 2016 at 12:04:58 PM UTC-5, Richard Heathfield wrote:
>> In fact, they are required not to. See 6.3.2.3(3): An integer constant
>> expression with the value 0, or such an expression cast to type
>> void *, is called a null pointer constant. 66) If a null pointer
>> constant is converted to a pointer type, the resulting pointer, called a
>> null pointer, is guaranteed to compare unequal to a pointer to any
>> object or function.
>
> It compares unequal to any object or function *that would be recognized by
> the Standard*. The Standard does not recognize the existence of an I/O
> port control register located at address zero, nor does it specify anything
> about what happens if code tries to dereference a pointer that compares
> equal to null. On the other hand, if a piece of hardware does happen to
> have a port at address zero, the simplest and most practical way for a
> quality implementation targeting that platform to support access to that
> port is to simply treat zero like any other address. The Standard does
> not require such behavior, but nothing in the Standard would prohibit it
> either. A conforming implementation for that platform could make address
> zero inaccessible, but from the point of view of the Standard it could just
> as well deny access to all hardware addresses that aren't used to hold
> Standard-recognized C objects. A freestanding implementation that did that
> wouldn't be very useful, of course, but would be allowable under the
> Standard nonetheless.

All that is covered by the fact that the behavior of dereferencing
a null pointer is undefined.

A null pointer does not point to an object or function, because the
standard defines what a pointer, a null pointer, an object, and
function are. If an implementation chooses to define for itself
the behavior of dereferencing a null pointer, then of course it
can do so.

If it's important to be able to access something at address 0, then
an implementation also has the option of using a representation
other than all-bits-zero for null pointers.

(In practice, I suspect that hardware designers tend to avoid putting
anything useful at address zero, precisely so that C null pointers are
unambiguous.)

BartC

unread,
Nov 1, 2016, 11:37:16 AM11/1/16
to
It would definitely be better. Assuming * is used as postfix deref, and
that P is a struct that has an extra pointer level added to its type on
each successive line:

Prefix * With -> Postfix *

P.m P.m P.m
(*P).m P->m P*.m
(**P).m (*P)->m P**.m
(***P).m (**P)->m P***.m


The right column is more consistent, there are no parentheses, and it's
not necessary to mix up * and ->.

It also means that it becomes easier to use *actual* pointers to arrays,
as shown here, starting with a normal array A and adding an extra
pointer level on each line:

A[i] A[i]
(*A)[i] A*[i]
(**A)[i] A**[i]

With pointers to actual arrays, not to the first elements, everything is
far more transparent too (but not everyone will like that as it will
expose some derefs that stay hidden with the current way of pointing to
array elements).

Everything works from left to right too which might be easier to
understand: with P**, start with P, dereference it, then dereference it
again.

**P might match the type 'pointer to pointer to T' more closely, but it
doesn't match the the deref actions as well when used in an expression
(which * corresponds to the first pointer and the first deref?).

--
Bartc

Keith Thompson

unread,
Nov 1, 2016, 11:43:30 AM11/1/16
to
BartC <b...@freeuk.com> writes:
> On 31/10/2016 18:20, John Bode wrote:
>> An object pointer denotes the location of a single object, not a sequence
>> of objects, *even if that pointer is the address of the first element of
>> an array*. If I do something like
>>
>> int a[10] = {0};
>> int *p = a;
>>
>> then the expression *p evaluates to the value of a[0] - that's it. Not the
>> contents of the array. The *language* does not regard `p` as the first of
>> a sequence of objects, even though in this case we are treating it as such.
>> sizeof *p gives us the size of a single integer, not the size of the array
>> to which p is pointing.
>
> But if you wanted to apply a function to the array 'a', would you do
> that like this:
>
> void fn1(int (*A)[]) {printf("%d\n",(*A)[4]);}
>
> fn1(&a);
>
> or like this:
>
> void fn2(int *A) {printf("%d\n",A[4]);}
>
> fn2(a); // or fn2(p);

As you know, C does not have parameters of array type, so if we want a
function to operate on an array, we have to pass a pointer. I'd
probably pass a pointer to the initial element *and* an extra size_t
argument specifying the number of elements in the array (unless there's
some other way to determine how many elements the function should
operate on, as for strings).

> So although 'a' and p are pointers to only the first element of the
> error, EVERYBODY uses them to refer to the ENTIRE array.

The declarations are:
int a[10] = {0};
int *p = a;

`a` is an array object, not a pointer object. It is of course the
entire array. When used in an expression, the name `a` -- oh,
forget it, you already understand array-to-pointer conversions,
you're just pretending not to.

`p` is a pointer object. Its value can be used, via pointer arithmetic,
to access the elements of the array object `a`. It doesn't refer to the
entire array, only to one element of it -- but via pointer arithmetic,
it can refer to all the elements, one at a time.

> This is my some of ue can see there's a huge mix-up between pointers and
> arrays, while others insist they are quite different.

Yes, it can be confusing. You seem to be determined to make it even
more so. Please stop.

> (And sizeof *p doesn't give the array size because that information is
> now missing. The EFFECTIVE array that p points to the first element of
> is unbounded. And both fn1 and fn2 really need a length parameter.)

The array object whose initial element p points to has exactly 10
elements. That information is lost by the array-to-pointer conversion.

Keith Thompson

unread,
Nov 1, 2016, 11:50:28 AM11/1/16
to
supe...@casperkitty.com writes:
[...]
> Some other languages use words for their bitwise operators and/or use the
> at-sign and dollar-sign characters,

Sure.

> and thus have space in the character
> sets for a dedicated dereferencing character.

What?

John Bode

unread,
Nov 1, 2016, 12:03:02 PM11/1/16
to
On Monday, October 31, 2016 at 1:24:07 PM UTC-5, Rick C. Hodgin wrote:
> On Monday, October 31, 2016 at 2:21:11 PM UTC-4, John Bode wrote:
> > On Monday, October 31, 2016 at 11:35:21 AM UTC-5, Richard Heathfield wrote:
> > > On 31/10/16 16:12, John Bode wrote:
> > > > On Monday, October 31, 2016 at 10:27:25 AM UTC-5, Rick C. Hodgin wrote:
> > > >> On Monday, October 31, 2016 at 11:09:04 AM UTC-4, Keith Thompson wrote:
> > > >>> BartC <b...@freeuk.com> writes:
> > > >>>> On 31/10/2016 14:20, Richard Heathfield wrote:
> > > >>>>> On 31/10/16 13:16, Pascal J. Bourguignon wrote:
> > > >>> [...]
> > > >>>>>> In C, basically a pointer is equivalent to array.
> > > >>>>>
> > > >>>>> Except when it isn't. For example:
> > > >>>>
> > > >>>> Here we go again...
> > > >>>
> > > >>> Yes, here we go again.
> > > >>>
> > > >>>> Take the char** parameter type in the subject; each "*" might denote a
> > > >>>> pointer to a single object, or a pointer to multiple objects, the common
> > > >>>> idiom to pass an array (or possibly a string when char*) to a function.
> > > >>>
> > > >>> A (valid non-null) pointer is always a pointer to a single object. That
> > > >>> single object may or may not be the initial element of an array object.
> > > >>
> > > >> A pointer (null, or non-null) is always a pointer to an array in memory.
> > > >
> > > > That is an incredibly confused and garbled understanding of pointers.
> > >
> > > Indeed.
> > >
> > > > NULL is a well-defined "nowhere" - it is *guaranteed* to never point to an
> > > > object or function.
> > >
> > > Right.
> > >
> > > > A pointer value evaluates to the location of a single object. Period.
> > >
> > > Uh, not quite. It can evaluate to the location of a function.
> >
> > True, but since Rick was talking about arrays, I subconsciously focused on
> > object pointers as opposed to function pointers (since you cannot create an
> > array of functions).
> >
> > I think part of the disconnect is people thinking in terms of what happens
> > in the machine code on a particular implementation, rather than the abstract
> > machine defined by the C language.
>
> You do know that nobody writes C code on a whiteboard, right?

I spent a good chunk of the '90s writing code that had to run on different
hardware/OS combinations concurrently (VAX/VMS, x86/Windows, PPC/MacOS,
SPARC/Unix), with different compilers, object file formats, endianess, etc.
I have massive amounts of scar tissue on my ass as a result of repeatedly
being bitten by assumptions that, because the generated machine code worked
a certain way on platform A, it would work the same way on platform B.

The hardware does not define the language. You cannot use a specific
implementation's behavior on a specific architecture to reason how the
*language* works.

A pointer resolves to the location of a single object (or function).
Period. Said pointer may be used in a larger expression context to access
members of a sequence of objects, such as:

a[i]
*(a + i)
*p++

etc., but even so, each of those expressions also resolves to a single
object (or function) value. Period. That's how pointers work.

6.2.5/20: "A pointer type describes an object whose value provides a
reference TO AN ENTITY OF THE REFERENCED TYPE."

*AN* entity. Singular. One. Not a sequence of entities.

Yes, subscript operations are defined in terms of pointer operations. That
doesn't mean pointers are arrays.

>
> > Yeah, sure, on real-world hardware, almost any address value can be the start of a sequence of bytes/words/whatever, but that's not how the language views it.
>
> It should be because we live in a world of reality, not abstract theory.

The reality on x86 is not the same reality as a VAX which is not the
same reality as MIPS which is not the same reality as SPARC which is
not the same reality as PPC which is not the same reality as PA-RISC
which is not the same reality as 68K. And that's not even getting into
different operating systems.

>
> > An object pointer denotes the location of a single object, not a sequence
> > of objects, *even if that pointer is the address of the first element of
> > an array*. If I do something like
> >
> > int a[10] = {0};
> > int *p = a;
> >
> > then the expression *p evaluates to the value of a[0] - that's it. Not the
> > contents of the array. The *language* does not regard `p` as the first of
> > a sequence of objects, even though in this case we are treating it as such.
> > sizeof *p gives us the size of a single integer, not the size of the array
> > to which p is pointing.
>
> Of course, all of this is just my opinion. But I'm probably right.
>

No, you're probably not.

supe...@casperkitty.com

unread,
Nov 1, 2016, 12:06:09 PM11/1/16
to
On Tuesday, November 1, 2016 at 10:50:28 AM UTC-5, Keith Thompson wrote:
> supercat writes:

> > Some other languages use words for their bitwise operators and/or use the
> > at-sign and dollar-sign characters,
>
> Sure.
>
> > and thus have space in the character
> > sets for a dedicated dereferencing character.
>
> What?

In Pascal, for example (the only such language I've used, though other such
languages exist), the caret operator is not used for xor, but is instead
used for indirection. Thus, what in C would be (*myShapeHandle)->left
would in Pascal be myShapeHandle^^.left (note that double-indirection poses
no problem in Pascal, unlike C).

BartC

unread,
Nov 1, 2016, 12:14:42 PM11/1/16
to
On 01/11/2016 15:43, Keith Thompson wrote:
> BartC <b...@freeuk.com> writes:

>> But if you wanted to apply a function to the array 'a', would you do
>> that like this:
>>
>> void fn1(int (*A)[]) {printf("%d\n",(*A)[4]);}
>>
>> fn1(&a);
>>
>> or like this:
>>
>> void fn2(int *A) {printf("%d\n",A[4]);}
>>
>> fn2(a); // or fn2(p);
>
> As you know, C does not have parameters of array type, so if we want a
> function to operate on an array, we have to pass a pointer. I'd
> probably pass a pointer to the initial element *and* an extra size_t
> argument specifying the number of elements in the array (unless there's
> some other way to determine how many elements the function should
> operate on, as for strings).

Yes, this is my point. For various reasons (mainly the unwieldy syntax
IMO) true pointers-to-arrays are rarely used, in favour of the inferior
method manipulating pointers to elements and depending on this
'decaying' behaviour happening behind the scenes.

>> (And sizeof *p doesn't give the array size because that information is
>> now missing. The EFFECTIVE array that p points to the first element of
>> is unbounded. And both fn1 and fn2 really need a length parameter.)
>
> The array object whose initial element p points to has exactly 10
> elements. That information is lost by the array-to-pointer conversion.

Here's another advantage of using proper pointers-to-arrays:

int A[10];
int (*P)[10] = &A;

printf("sizeof(*P) = %d\n", sizeof(*P)); // or %<whatever>

This correctly reports the size of the array that P points to. (Although
this is of limited use for function calls, as usually you don't want a
function operating on fixed-size array.)

--
Bartc

BartC

unread,
Nov 1, 2016, 12:28:02 PM11/1/16
to
On 01/11/2016 16:02, John Bode wrote:

> The hardware does not define the language.

Up to a point it does. It can't be a coincidence that a language like C
can be implemented reasonably efficiently on a range of hardware.

And independently of the hardware, given these declarations:

enum {A};
int B;
int *C;
int **D:

then you can probably make the following inferences about what happens
in the evaluation of each of these lines, without knowing what hardware
they will run on:

A; // no dereference (immediate load)
B; // one implicit deref (equating to *&B)
*C; // one explicit deref
**D; // two explicit derefs

It could be that in specific code on a specific machine, the deref for
*C might not appear; it might be optimised out, or the required pointer
is already in a register, or it can deduce what is at *D, etc etc.

That doesn't stop you reasoning about the behaviour on a generic machine
in a way similar to the above.

> A pointer resolves to the location of a single object (or function).
> Period. Said pointer may be used in a larger expression context to access
> members of a sequence of objects, such as:
>
> a[i]
> *(a + i)
> *p++
>
> etc., but even so, each of those expressions also resolves to a single
> object (or function) value. Period. That's how pointers work.

What happens here then:

int B[10];
int (*Q)[10] = &B;

Q is a pointer. *Q yields one object, true, but that object is an entire
array, that can then be indexed:

(*Q)[i];

--
Bartc

BartC

unread,
Nov 1, 2016, 12:28:17 PM11/1/16
to
On 01/11/2016 15:50, Keith Thompson wrote:
> supe...@casperkitty.com writes:
> [...]
>> Some other languages use words for their bitwise operators and/or use the
>> at-sign and dollar-sign characters,
>
> Sure.
>
>> and thus have space in the character
>> sets for a dedicated dereferencing character.
>
> What?

It means having the possibility of using something like "^" for a
postfix dereferencing symbol.

But C can't use it because "^" means exclusive-or. Another language
however might use "xor" for that, thus freeing up "^". That is the point
being made.

--
Bartc


james...@verizon.net

unread,
Nov 1, 2016, 12:33:40 PM11/1/16
to
On Tuesday, November 1, 2016 at 11:23:15 AM UTC-4, Jerry Stuckle wrote:
> On 11/1/2016 8:10 AM, Ben Bacarisse wrote:
...
> > Yes. Tokens can't include spaces and every operator is a single token.
>
> What about the ?: (ternary) operator?

6.4.6p2 defines an operator as a punctuator (defined in 6.4.6p1) which specifies an action to be performed. Unlike *=, ?: does not qualify as a single punctuator, but as two separate punctuators; it requires both punctuators to specify the action to be taken. Therefore, that definition doesn't apply. However, the standard is inconsistent on this matter, referring to the "?: operator" in 5.1.2.4p14, and describing ?: as the "conditional operator" in 6.5.15.
Three of the five kinds of "postfix operators" (6.5.2) involve the use of two or more separated punctuators, so the same issue comes up in those cases.
The definition of "operator" should be modified to reflect these facts.

supe...@casperkitty.com

unread,
Nov 1, 2016, 12:45:51 PM11/1/16
to
On Tuesday, November 1, 2016 at 11:03:02 AM UTC-5, John Bode wrote:
> I spent a good chunk of the '90s writing code that had to run on different
> hardware/OS combinations concurrently (VAX/VMS, x86/Windows, PPC/MacOS,
> SPARC/Unix), with different compilers, object file formats, endianess, etc.
> I have massive amounts of scar tissue on my ass as a result of repeatedly
> being bitten by assumptions that, because the generated machine code worked
> a certain way on platform A, it would work the same way on platform B.
>
> The hardware does not define the language. You cannot use a specific
> implementation's behavior on a specific architecture to reason how the
> *language* works.

If one wishes to perform some task on a particular hardware, it is useful
to have a relatively consistent means of mapping hardware behavior into
languages (using the term "language" to define a mapping between source
texts and behaviors).

Although there used to be a rather wide variety of "interesting"
architectures, how much code today will ever need to be run on a hardware
platform whose fundamental behaviors differ from those of the PDP-11 in any
important ways other than:

1. Sizes, endianness, and alignment requirements of integer types
and long double.
2. Size and alignment of pointer types.
3. Layout of the stack and other data structures, and the particular
effects of accessing memory *not owned by the program*.
4. Precise behaviors of floating-point types.

From a hardware standpoint, on how many platforms today would a simple
straightforward compiler similar to those envisioned and programmed by
Dennis Ritchie not behave pretty much like the PDP-11 compiler (subject
to the above adjustments) *even in cases where the Standard imposes no
requirements*?

BartC

unread,
Nov 1, 2016, 12:56:03 PM11/1/16
to
I said I found constructs like this confusing:

do do while (a); while (b); while (c);

which they are. Saying that you can figure them out by delving into the
grammar might tell you what it means but it doesn't make it less
confusing. You don't want to write code so obscure that people need to
refer to a grammar.

The difference between a 'while' ending a 'do' statement and one
starting a separate 'while' statement is subtle, IMO.

The attitude here seems to be that C cannot be criticised under any
circumstances. There is nothing wrong with any aspect of it!

Anyway I wasn't criticising it except saying I couldn't understand it.
In fact this sub-thread started off with someone (not me) being confused
by a similar bit of code.

--
Bartc

Ben Bacarisse

unread,
Nov 1, 2016, 1:37:35 PM11/1/16
to
Jerry Stuckle <jstu...@attglobal.net> writes:

> On 11/1/2016 8:10 AM, Ben Bacarisse wrote:
>> BartC <b...@freeuk.com> writes:
>>
>>> On 01/11/2016 11:14, Ben Bacarisse wrote:
>>>> BartC <b...@freeuk.com> writes:
>>>> <snip>
>>>>> Yes, a postfix dereference op would have solved some problems
>>>>> (although not with * as 'b* = 10' currently means 'b *= 10').
>>>>
>>>> I'm not entirely sure what you mean here (since there is a hypothetical
>>>> being discussed) but 'b* = 10' is currently a syntax error and, given a
>>>> hypothetical postfix * operator, would mean '(b*) = 10' unless some very
>>>> dramatic changes were made to how C defines its lexical tokens.
>>>
>>> You mean that "*=" can't have a space in the middle?
>>
>> Yes. Tokens can't include spaces and every operator is a single token.
>>
>> <snip>
>
> What about the ?: (ternary) operator?

Or, indeed, all the cast operators ('(int)' is three tokens). The
function call operators can have even more. "Every assignment operator
is a single token" would have been a safer remark.

--
Ben.

Ben Bacarisse

unread,
Nov 1, 2016, 1:46:26 PM11/1/16
to
BartC <b...@freeuk.com> writes:
<snip>
>> A pointer resolves to the location of a single object (or function).
>> Period. Said pointer may be used in a larger expression context to access
>> members of a sequence of objects, such as:
>>
>> a[i]
>> *(a + i)
>> *p++
>>
>> etc., but even so, each of those expressions also resolves to a single
>> object (or function) value. Period. That's how pointers work.
>
> What happens here then:
>
> int B[10];
> int (*Q)[10] = &B;
>
> Q is a pointer. *Q yields one object, true,

You've answered your own question, haven't you?

<snip>
--
Ben.

Ben Bacarisse

unread,
Nov 1, 2016, 1:48:10 PM11/1/16
to
Why not write the more direct C equivalent: (**myShapeHandle).left? And
what problem does the double indirection pose in C?

--
Ben.

Keith Thompson

unread,
Nov 1, 2016, 2:45:48 PM11/1/16
to
BartC <b...@freeuk.com> writes:
> On 01/11/2016 16:02, John Bode wrote:
>
>> The hardware does not define the language.
>
> Up to a point it does. It can't be a coincidence that a language like C
> can be implemented reasonably efficiently on a range of hardware.

Hardware clearly *influences* the design of the language. But once that
design is written down as a standard, implementations do whatever they
need to do to implement the written semantics on whatever target
hardware is being used. If that means emulating binary on trinary
hardware, that's what you need to do to have a conforming
implementation. (Admittedly that's unlikely to be necessary in real
life; there have been trinary computers, but I doubt that there's ever
been a conforming C implementation for them.)

> And independently of the hardware, given these declarations:
>
> enum {A};
> int B;
> int *C;
> int **D:
>
> then you can probably make the following inferences about what happens
> in the evaluation of each of these lines, without knowing what hardware
> they will run on:

Assuming everytiing is initialized properly:

> A; // no dereference (immediate load)

In terms of C semantics, it evaluates and discards the value 0, of type
int. Almost certainly no code is generated.

> B; // one implicit deref (equating to *&B)

That evaluates and discards the value of the int object B. Again, any
decent compiler will generate no code. There is no dereference, which
is an operation applied to a pointer value. (Actually the standard
doesn't use the word "dereference" except in passing in one
non-normative footnote.)

> *C; // one explicit deref
> **D; // two explicit derefs

Yes (but again, it's likely no code will be generated). But by your
logic, surely the first would have one implicit dereference to obtain
the value of C, and an explicit dereference to obtain the value of *C.

You're using the word "dereference" in two *very* different ways. One
is the way most of us use it, to determine the value of the object that
a pointer value points to. The other is to determine the value of a
named object, something that the standard calls "lvalue conversion"
(N1570 6.3.2.1).

Even if code is generated, it's entirely possible that any or all of A,
B, C, or D are stored in registers, or that the compiler is able to
determine their values without re-fetching them.

The C semantics of the above statements is quite clear. The generated
code from a given compiler is likely to be clear as well to someone who
understands the target machine. Mixing them together as you do is not
helpful.

As you commonly do, you're rejecting the standard terminology most of us
use and describing things in terms that create more confusion than
necessary.

[...]

> What happens here then:
>
> int B[10];
> int (*Q)[10] = &B;
>
> Q is a pointer. *Q yields one object, true, but that object is an entire
> array, that can then be indexed:
>
> (*Q)[i];

Is that a serious question? Come on, you know this stuff; you just
don't like it.

A pointer points to a single object (and, via pointer arithmetic, may
also be used to access other objects that are elements of the same
array object). An object can be of any object type -- including an
array type. If the pointed-to object happens to be an array object,
then of course it can be indexed -- because it's an array object.

There's nothing magical about pointers to array types. They work
exactly as you'd expect once you understand how pointers and
arrays work.

I expect you'll reply to this with yet another complaint that arrays and
pointers are confusing. I agree, they certainly can be. Stop making it
worse.

Keith Thompson

unread,
Nov 1, 2016, 2:49:26 PM11/1/16
to
Ok, I think I see what you mean.

A few articles directly upthread, BartC wrote:

You mean that "*=" can't have a space in the middle?

When you wrote "... and thus have space ...", I assumed you were
referring to the space character.

Please try to write more clearly.

(I don't know what problem you think C has with double indirection that
Pascal doesn't.)

Keith Thompson

unread,
Nov 1, 2016, 2:57:20 PM11/1/16
to
BartC <b...@freeuk.com> writes:
[...]
> The attitude here seems to be that C cannot be criticised under any
> circumstances. There is nothing wrong with any aspect of it!

I am aware of no such attitude.

My own attitude is that there are a lot of legitimate criticisms of C,
but repeating those criticisms ad nauseam as you do is boring.
Furthermore, expressing those criticisms in terms that seem to be
designed to maximize confusion is annoying.

I've never said I *like* the way C treats arrays and pointer, and in
fact I don't. I'm not sure how I'd fix it while retaining C's
relatively low level. But I *understand* how they work.

> Anyway I wasn't criticising it except saying I couldn't understand it.
> In fact this sub-thread started off with someone (not me) being confused
> by a similar bit of code.

Yes, the code in question was poorly written. It would have been easier
to understand with proper formatting.

Keith Thompson

unread,
Nov 1, 2016, 2:58:28 PM11/1/16
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:
[...]
> Or, indeed, all the cast operators ('(int)' is three tokens).

And a type name can be arbitrarily long, so a cast operator can consist
of as many tokens as you like.

> The
> function call operators can have even more. "Every assignment operator
> is a single token" would have been a safer remark.

--

John Bode

unread,
Nov 1, 2016, 3:34:28 PM11/1/16
to
On Tuesday, November 1, 2016 at 11:56:03 AM UTC-5, Bart wrote:
> On 01/11/2016 15:07, mark.b...@gmail.com wrote:
> > It has already been determined that Bart is concerned
> > with criticising C not understanding it...
> >
> > (BTW isn't it interesting how "fir" threads tend to end
> > up with him talking to himself for a lot of the time?)
> >
>
> I said I found constructs like this confusing:
>
> do do while (a); while (b); while (c);
>
> which they are. Saying that you can figure them out by delving into the
> grammar might tell you what it means but it doesn't make it less
> confusing. You don't want to write code so obscure that people need to
> refer to a grammar.
>

Right. There's no point in making code harder to read than it needs to be.
Just writing it as

do
{
do
while (a);
while (b);
} while (c);

would go a *long* way towards making that clearer, although I'd personally
write

do
{
do
{
while (a);
} while (b);
} while (c);

I've adopted a style where I will only omit curly braces on the innermost
statement, and only if I think the code is *obvious* without it, as in

if ( a )
{
if ( b )
stmt;
}

but if the innermost statement is an empty control structure (such as a for(a;b;c); or while(a);), I'd wrap it in a curly brace as well:

if ( a )
{
if ( b )
{
for ( c; d; e );
}
}

> The difference between a 'while' ending a 'do' statement and one
> starting a separate 'while' statement is subtle, IMO.
>
> The attitude here seems to be that C cannot be criticised under any
> circumstances. There is nothing wrong with any aspect of it!
>

Only speaking for myself, I don't hold that attitude at all - C has more
than its fair share of warts, and I will gripe about them myself. If I
could go back in time to 1968, there's a few things I'd slap dmr around
for. I understand why the array conversion rule exists, I understand what
his motivation was, I understand why it looked like a good idea at the time,
but it has enabled whole classes of bugs and misfeatures and security holes
that *simply don't exist* in languages like Fortran or Pascal or Ada (yes,
I'm old).

But here's the thing - C's something like 45 years old now. WG14 may add
a few new features here and there, deprecate some *obviously* dodgy shit,
but they're not going to fundamentally change the syntax or semantics of
the language. Paradigms come and go, but legacy code is forever. C isn't
going to significantly evolve from here on out.

What gets on *my* nerves are people bitching about what C does wrong and
why it needs to be changed *without taking time to understand what it is
that they are criticizing*. "It doesn't make sense to me, therefore it
is hopelessly broken and should be changed in these ways". fir does
that in this very thread when he bitches about the concept of multiple
indirection:

> for some time i began to think or at least speculate that ** and more
> 'rank' pointers in c are bad idea (and it seem to fit this examples too)

If C isn't doing the job you need it to, use a language that does. You
need structured exception handling, generics, lambdas, etc.? Use C++. You
want to avoid pointers in *any* form? Use Java or C#. That would be
*endlessly* more productive than coming here and complaining that C doesn't
work the way you think it should.

supe...@casperkitty.com

unread,
Nov 1, 2016, 3:39:00 PM11/1/16
to
On Tuesday, November 1, 2016 at 12:48:10 PM UTC-5, Ben Bacarisse wrote:
No parentheses are required in Pascal. As for whether it's better to use
one star and arrow, or two stars and dot, code which uses handles will also
use single-indirect pointers a fair amount. Using "->" for single-indirect
and the dot for both direct and double-indirect seems a little weird.
In any case, though, my main point was that because the indirection operator
is on the same side as the member-access indicator, something like foo^.bar^
clearly applies the member access between the two indirections, while
foo^^.bar applies it after both and foo.bar^^ applies it before both, all
without need for parentheses.


BartC

unread,
Nov 1, 2016, 3:41:26 PM11/1/16
to
On 01/11/2016 18:46, Keith Thompson wrote:
> BartC <b...@freeuk.com> writes:


>> And independently of the hardware, given these declarations:
>>
>> enum {A};
>> int B;
>> int *C;
>> int **D:
>>
>> then you can probably make the following inferences about what happens
>> in the evaluation of each of these lines, without knowing what hardware
>> they will run on:
>
> Assuming everytiing is initialized properly:
>
>> A; // no dereference (immediate load)
>
> In terms of C semantics, it evaluates and discards the value 0, of type
> int. Almost certainly no code is generated.

You're getting bogged down in what likely happens in a real machine. And
course there is no context here as there would be in a real example.

But, to throw in something more concrete, imagine there purpose of all
this is to end up with the value of the expression in a register, while
all objects with storage reside in memory.

>> B; // one implicit deref (equating to *&B)
>
> That evaluates and discards the value of the int object B. Again, any
> decent compiler will generate no code. There is no dereference, which
> is an operation applied to a pointer value. (Actually the standard
> doesn't use the word "dereference" except in passing in one
> non-normative footnote.)
>
>> *C; // one explicit deref
>> **D; // two explicit derefs
>
> Yes (but again, it's likely no code will be generated). But by your
> logic, surely the first would have one implicit dereference to obtain
> the value of C, and an explicit dereference to obtain the value of *C.

Yes, an implicit dereference to get the value of B, C or D, then
whatever extra explicit derefs have been specified.

> You're using the word "dereference" in two *very* different ways. One
> is the way most of us use it, to determine the value of the object that
> a pointer value points to. The other is to determine the value of a
> named object, something that the standard calls "lvalue conversion"
> (N1570 6.3.2.1).
>
> Even if code is generated, it's entirely possible that any or all of A,
> B, C, or D are stored in registers, or that the compiler is able to
> determine their values without re-fetching them.

The point of the example is that it is possible, without knowing exactly
what machine this is to be run on, to see that A, B, C and D each
involve progressively more data memory accesses, from 0 to 3.

(In practice, even if A was an immediate value, that value has to be
loaded from the instruction store. But then the addresses of B, C and D
also have to be loaded, so you might consider that to be 1 to 4 memory
accesses. The progression is still there however.)

In a simplistic machine (and with static storage), the four examples
might be implemented like this:

load R, A # A;
load R, [B] # B;
load R, [C]; load R,[R] # *C;
load R, [D]; load R,[R]; load R,[R] # **D;

This is the sort of execution model I like to have in mind when trying
to understand what a bit of code does. That's why I dislike named
constants implemented with 'const' as you might get A, B or something
in-between. Or array decay as you can't tell whether E[4] gets you
something more like B or more like *C (ie. with an extra deref).

> The C semantics of the above statements is quite clear. The generated
> code from a given compiler is likely to be clear as well to someone who
> understands the target machine. Mixing them together as you do is not
> helpful.
>
> As you commonly do, you're rejecting the standard terminology most of us
> use and describing things in terms that create more confusion than
> necessary.

With the advantage that these common constructs are not tied to a
specific language (and especially not a specific reference manual where
they might have arbitrarily chosen the nomenclature by tossing coins).

>> What happens here then:
>>
>> int B[10];
>> int (*Q)[10] = &B;
>>
>> Q is a pointer. *Q yields one object, true, but that object is an entire
>> array, that can then be indexed:
>>
>> (*Q)[i];
>
> Is that a serious question? Come on, you know this stuff; you just
> don't like it.

John Bode appeared to be making the point that a pointer always refers
to one element of an array, never the whole array. I'm making it clear
that the 'one object' of a pointer target can also refer to an entire
array as well as its first element.

--
Bartc

fir

unread,
Nov 1, 2016, 3:49:41 PM11/1/16
to
W dniu wtorek, 1 listopada 2016 20:34:28 UTC+1 użytkownik John Bode napisał:
>
> What gets on *my* nerves are people bitching about what C does wrong and
> why it needs to be changed *without taking time to understand what it is
> that they are criticizing*. "It doesn't make sense to me, therefore it
> is hopelessly broken and should be changed in these ways". fir does
> that in this very thread when he bitches about the concept of multiple
> indirection:
>

not really, in realily im most probably the first person on this planet to understand c concepts in its 'deeper deepness'
here what i was criticisizing is a big type-hole which is bringed to life by things like int** (and even int*)
i also discussed/presentet relatively easy patch for patching it

Keith Thompson

unread,
Nov 1, 2016, 4:10:34 PM11/1/16
to
BartC <b...@freeuk.com> writes:
> On 01/11/2016 18:46, Keith Thompson wrote:
>> BartC <b...@freeuk.com> writes:
>>> And independently of the hardware, given these declarations:
>>>
>>> enum {A};
>>> int B;
>>> int *C;
>>> int **D:
>>>
>>> then you can probably make the following inferences about what happens
>>> in the evaluation of each of these lines, without knowing what hardware
>>> they will run on:
>>
>> Assuming everytiing is initialized properly:
>>
>>> A; // no dereference (immediate load)
>>
>> In terms of C semantics, it evaluates and discards the value 0, of type
>> int. Almost certainly no code is generated.
>
> You're getting bogged down in what likely happens in a real machine. And
> course there is no context here as there would be in a real example.

I thought it was a real example. `A;` is an expression statement, with
well defined semantics. If you want to talk about evaluating `A` in
some other context, show that context.

I hardly think that mentioning that no code is likely to be generated is
getting "bogged down". It's an obvious hardware-independent
optimization.

> But, to throw in something more concrete, imagine there purpose of all
> this is to end up with the value of the expression in a register, while
> all objects with storage reside in memory.

"You're getting bogged down in what likely happens in a real machine."

[...]

>> You're using the word "dereference" in two *very* different ways. One
>> is the way most of us use it, to determine the value of the object that
>> a pointer value points to. The other is to determine the value of a
>> named object, something that the standard calls "lvalue conversion"
>> (N1570 6.3.2.1).
>>
>> Even if code is generated, it's entirely possible that any or all of A,
>> B, C, or D are stored in registers, or that the compiler is able to
>> determine their values without re-fetching them.
>
> The point of the example is that it is possible, without knowing exactly
> what machine this is to be run on, to see that A, B, C and D each
> involve progressively more data memory accesses, from 0 to 3.

Now you've moved the goalposts from "dereferences" to "data memory
accesses".

I don't see any non-trivial point in what you've said so far.

[...]

>> As you commonly do, you're rejecting the standard terminology most of us
>> use and describing things in terms that create more confusion than
>> necessary.
>
> With the advantage that these common constructs are not tied to a
> specific language (and especially not a specific reference manual where
> they might have arbitrarily chosen the nomenclature by tossing coins).

If you don't want to be tied to a specific language, I suggest you take
your discussion to a forum that isn't tied to a specific language, as
this one is.

You've already confused things by misusing the word "dereference". If
you had used the standard's terminology, which has the advantage of
being consistent, I might have had a better chance of understanding your
point, whatever it is.

>>> What happens here then:
>>>
>>> int B[10];
>>> int (*Q)[10] = &B;
>>>
>>> Q is a pointer. *Q yields one object, true, but that object is an entire
>>> array, that can then be indexed:
>>>
>>> (*Q)[i];
>>
>> Is that a serious question? Come on, you know this stuff; you just
>> don't like it.
>
> John Bode appeared to be making the point that a pointer always refers
> to one element of an array, never the whole array. I'm making it clear
> that the 'one object' of a pointer target can also refer to an entire
> array as well as its first element.

A pointer always points to a single object. Of course that object
can be an array object. You're (perhaps deliberately) conflating
the object, which might happen to be an array object, that a pointer
points to, with the array object that the pointed-to object might
be an element of. I can only assume that you're deliberately trying
to cause confusion.

John Bode

unread,
Nov 1, 2016, 4:12:33 PM11/1/16
to
On Tuesday, November 1, 2016 at 11:28:02 AM UTC-5, Bart wrote:
> On 01/11/2016 16:02, John Bode wrote:
>
> > The hardware does not define the language.
>
> Up to a point it does. It can't be a coincidence that a language like C
> can be implemented reasonably efficiently on a range of hardware.
>

Yes, C's design was informed by real-world hardware. Nobody denies that.

What I am objecting to is Rick's contention that, because memory tends to
be contiguous on real hardware (ignoring the concept of virtual memory for
the moment), that *any* pointer is implicitly the start of an array. That
does not follow from the language definition. It's an improper way of
thinking about pointers that I think will lead you down some nasty rabbit
holes.

Pointers are *abstractions* of memory addresses, with associated type
semantics. There is no implied ordering of addresses between objects.
There's a reason why indexing past the end of an array invokes undefined
behavior.

C is not a "portable assembly" language (blech). It's only "close to the
hardware" in the sense that its abstractions (types, operations, etc.) are
rooted in what most real-world hardware provides. It's a high-level
language that provides low-level abstractions.
It is loading more messages.
0 new messages