473,698 Members | 2,611 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

gcc: pointer to array

Hi all,

I have a question regarding the gcc behavior (gcc version 3.3.4).

On the following test program it emits a warning:
#include <stdio.h>

int aInt2[6] = {0,1,2,4,9,16};
int aInt3[5] = {0,1,2,4,9};

void print1 (const int* p, size_t cnt)
{
while (cnt--)
printf ("%d\n", *p++);
}

void print2 (const int (*p)[5])
{
size_t cnt;
#if 0
// prohibited:
(*p)[0]=0;
#endif
for (cnt=0; cnt<5; cnt++)
printf ("%d\n", (*p)[cnt]);
}

int main()
{
printf ("test begin\n");

print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
print2 (&aInt3); // <-- warns here

printf ("test end\n");
return 0;
}

The warning is:
MOD.c: In function `main':
MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

Why is that? How is this different from using print1 (aInt2, ...)? All I
want to do is to explicitly show that the pointer is to an array of 5
entries and ensure that it's not modified in anyway inside print2(). However
the #if 0'd assignment operator inside print2() is correctly handled by
gcc -- an error is generated:
MOD.c: In function `print2':
MOD.c:: error: assignment of read-only location
What's wrong with gcc?

There's another thing about the pointer to the array is... The compiler
doesn't generate a warning if I change in print2()
for (cnt=0; cnt<5; cnt++)
to
for (cnt=0; cnt<6; cnt++)
and lets me access (*p)[5], though I think at least a warning should be
generated here.
It doesn't warn me if I put
aInt3[6] = 0;
into main(). But in both cases the compiler "knows" the real size of the
array, still no warning...
Is this OK???

Thanks,
Alex
P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
Nov 15 '05
204 13016
Netocrat wrote:
OK I agree that it should be explicit.
How about this for an alternate
definition of an lvalue:
"Any expression which after removing any possible
register storage-specifier, we can take the address of."


Function identifiers aren't lvalues either,
even though functions have addresses.

--
pete
Nov 15 '05 #121
Netocrat wrote:
After it decays into int*, it's no longer an lvalue.


What would stop it from being an lvalue? It fits the definition of
"[a]n expression with an object type ... other than void".


"A cast does not yield an lvalue"

The result of a type conversion, can't be an lvalue.

char object;

(int)object = INT_MAX; /* no good */

Only one byte of memory is reserved for object.
There isn't really an int type object there.

--
pete
Nov 15 '05 #122
pete wrote:

Netocrat wrote:
After it decays into int*, it's no longer an lvalue.


What would stop it from being an lvalue? It fits the definition of
"[a]n expression with an object type ... other than void".


N869

6.3.2 Other operands
6.3.2.1 Lvalues and function designators

[#3] Except when it is the operand of the sizeof operator or
the unary & operator, or is a string literal used to
initialize an array, an expression that has type ``array of
type'' is converted to an expression with type ``pointer to
type'' that points to the initial element of the array
object

and is not an lvalue.
--
pete
Nov 15 '05 #123
Netocrat <ne******@dodo. com.au> wrote:
On Sat, 16 Jul 2005 00:32:38 +0000, S.Tobias wrote:
Netocrat <ne******@dodo. com.au> wrote:
On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:
Netocrat <ne******@dodo. com.au> wrote:
> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:
How about this for an alternate
definition of an lvalue: "Any expression which after removing any possible
register storage-specifier, we can take the address of."
Perhaps it's close, I don't know. You have to define what it means
"to take the address of" - if it's applying "&" operator, then
how do you define what can be operand to it - something that
"&" can be applied to? As pete indicated, function types
must be taken care of; also void expressions and bit fields.

struct s { int a[1]; };
>> struct s f(void);
>>
>> f().a[0] = 7; //UB
>>
>> The expression on the left is clearly a modifiable lvalue.

This is a minor variation on an example from a previous thread in
c.l.c:
struct s {...} s1, s2;
(s1 = s2).a[0] = 7; //UB

I remember reading that thread - "Undefined behaviour when modifying
the result of an assignment operator".

> Not so clearly according to gcc. I get: "ISO C90 forbids
> subscripting non-lvalue array".

gcc is lying. The expression "f().a[0]" is perfectly valid.
Well... perhaps not then.

It's perfectly valid as a non-modifiable lvalue; but we both agree that
assigning to it as a modifiable lvalue invokes undefined behaviour.


Try it with on-line como - there's not a single warnings. Cut'n'paste
version:
int main()
{
struct s { int a[1]; };
struct s f(void);
f().a[0] = 7; /*UB*/
}


Similarly as for gcc, we don't get warnings in C99 mode, but in C90
mode on-line como gives:

"ComeauTest .c", line 5: error: invalid use of non-lvalue array
f().a[0] = 7; /*UB*/
^


Yes, thanks, I didn't test all possible options. I didn't find
any significant changes in constraints to "[]" and "*", and semantics
of "." operators. Perhaps the difference between C90 and C99
results only from the change in the definition and interpretation
of an lvalue.

Also I have noticed the parts "If an attempt is made to modify
the result of [...] or to access it after the next sequence point,
the behavior is undefined." referring to assignments and function
calls are not there in C90.
[snip]
int a[1];
a[0];
`a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).

Agreed, but I was talking about f(), not f().a. In the case of f(), it
does have to be. If the struct returned by f() is not a modifiable
lvalue, then neither are the elements of its member array.


It doesn't matter, really.


It does though. You ignored my reasoning. f() returns a struct. If
that struct in this context is not a modifiable lvalue, then its elements
aren't modifiable lvalues either. This applies to the elements of its
array member.
`a' is not an lvalue, but `a[0]' is.

ITYM "modifiable lvalue". But yes, in the generic case where a is a
non-const array, I agree.

Thanks, it was late.
Similarly, `f().a' is not an lvalue;

Again, ITYM that it is an lvalue but not a modifiable lvalue.

"Similarly" is not the right word here, but since `f()' is not
an lvalue, neither is `f().a' (before conversion to pointer).
(By accident I was actually right.) This is a cv:
&f().a;

`f().a[0]' is the same as
`*(f().a + 0)', dereferencing a pointer is always an lvalue (in this
case it is even well defined by the Std, because `f().a' presumably
points to some valid temporary storage). Since its type is `int', it's
a modifiable lvalue.


Your reasoning is sound but predicated on the basis that f() itself is a
modifiable lvalue.


No, `f()' is not even an lvalue, and neither is `f().a'.
But `f().a[0]' is.
It falls apart when that is not the case... ie
consider const struct s cs. Now cs.a[0] is not a modifiable lvalue
because cs is not. The same reasoning applies to f().


`cs.a[0]' is not modifiable because members inherit qualifiers
(here: `const') from their containing types. It is not modifiable
not because `cs' isn't, but because its type is `const int'.
This is a modifiable lvalue:
((int*)cs.a)[0] //UB if modified

For another example:
struct s { int i; const int ci; } s;
`s' is a non-modifiable lvalue, but `s.i' is modifiable.

--
Stan Tobias
mailx `echo si***@FamOuS.Be dBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 15 '05 #124
Netocrat <ne******@dodo. com.au> wrote:
On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
Netocrat <ne******@dodo. com.au> wrote:
On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote: N869, 6.3.2.1
"An lvalue is an expression with an object type or an incomplete type
other than void; if an lvalue does not designate an object when it is
evaluated, the behavior is undefined."
The `*ps' is not evaluated in this case.


As I've noted elsewhere, you are correct for C99 but in C90 *ps is
evaluated - at least conceptually i.e. this is a constraint violation:

int *n = NULL.
int *p;
p = &*n;


I don't think so. In C90 6.2.2.1 there's a paragraph which is equivalent
of 6.3.2.1#2 in C99 (in n869.txt) (they seem the same, but I haven't
checked). It means that at least the value of `*n' is not taken (because
it is operand to "&"). I don't see any cv in C90 except that `*n' is
not strictly an lvalue, which cannot (in general) be established at
compile time (due to poor definition of an lvalue).

In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated. What
I think it means (and why it's necessary) is this: To evaluate "*"
is to establish lvalue for the object that `n' points to (it doesn't yet
mean to take a value - it wouldn't occur anyway in presence of "&").
It is UB if an lvalue doesn't designate an object, and it would
be UB if "*" were evaluated in this case; since it isn't, "&*n"
is well defined for all values of `n'.

struct s;
struct s *ps;
& * ps;
The sub-expression `*ps' has an incomplete struct type, and is an lvalue.


But seriously, clearly *ps on its own is nonsensical; surely you agree
with me on that?


I agree. But OTOH the notion of an lvalue is more of a pointer, or
half-dereferenced pointer (which establishes only a position of an
object, but not its range or value). In this context "& * ps" is
not strange.
Apart from that, an lvalue is rather a tag in the grammatical
description of a language, that is, certain expressions are labeled
"lvalues", whether there is an object or not.
What does your compiler say to the simple statement
*ps; (gcc tells me where to get off)? That I haven't yet found a specific
prohibition in the standard merely convinces me that I don't know where to
look.
I haven't found either. I'm prepraring a question on that, too.

I'm not satisfied that your code is an example of an incomplete struct
occurring as an lvalue. In the case where &* is being treated as a no-op,
then semantically *ps (and hence the incomplete struct) never occurs.


But you have to know in advance if "*ps" is an lvalue before you
can put "&" and "*ps" together (whether "&*" is a no-op, or not);
if it weren't an lvalue, then there'd be constraint violation.

And in C99 "&*" is not quite a no-op: the expression after that
is no longer an lvalue (if it was before):
n = 0;
&*n = 0; //CV
--
Stan Tobias
mailx `echo si***@FamOuS.Be dBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 15 '05 #125
Peter Nilsson <ai***@acay.com .au> wrote:
S.Tobias wrote:
In comp.lang.c Peter Nilsson <ai***@acay.com .au> wrote:
> S.Tobias wrote:
>> In comp.lang.c Peter Nilsson <ai***@acay.com .au> wrote:
>>
>> > char *foo();
>> >
>> > void bah(const char *x)
>> > {
>> > char *y = foo(x);
>>
>> You raise UB here: (const char*) and (char*) types are
>> not compatible - you cannot pass incompatible arguments
>> to a function call (cf. 6.5.2.2#6).
>
> C99 has different wording from N869.
>
> [It looks like it's not valid C90 though.]


I'm not sure what you mean. Here's the quote from the C99 Standard
I was referring to:
(6.5.2.2p6)
# If the expression that denotes the called function has a type that
# does not include a prototype, [...]
# [...] If the function is defined with a type that includes a
# prototype, and either the prototype ends with an ellipsis (, ...)
# or the types of the arguments after promotion are not compatible
# with the types of the parameters, the behavior is undefined. [...]


At the time of calling, the function (foo) isn't defined with a
prototype. So the behaviour is determined by the '...' you snipped.


(I snipped only irrelevant parts.)

At the point of call, only the declaration "char *foo()" is visible,
therefore at that point the expression "foo" has a function type
without a prototype. 6.5.2.2p6 is applicable.

The function is defined (elsewhere, no matter where, might be in
a different translation unit) with a type that includes a prototype:
char *foo(char *x) {/*...*/}
That's where the next quoted part applies.

I don't see any ambiguity here.
If the function were defined (again: anywhere) with a type
without a prototype, ie:
char *foo(x) char *x; {/*...*/}
then further (snipped) part would apply.

However, I think the second "dash" exception is ambiguous
(in how "both" and the two "or"s should be interpreted):
const char *pc;
foo(pc); //UB?

[Warning: there're differences between n869 and the Std there.]

--
Stan Tobias
mailx `echo si***@FamOuS.Be dBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 15 '05 #126
On Sat, 16 Jul 2005 10:45:44 +0000, pete wrote:
pete wrote:

Netocrat wrote:
> > After it decays into int*, it's no longer an lvalue.
>
> What would stop it from being an lvalue? It fits the definition of
> "[a]n expression with an object type ... other than void".

N869

6.3.2 Other operands
6.3.2.1 Lvalues and function designators

[#3] Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to
initialize an array, an expression that has type ``array of
type'' is converted to an expression with type ``pointer to
type'' that points to the initial element of the array
object

and is not an lvalue.


Which you seem to be justifying using the logic of your previous post:
"A cast does not yield an lvalue"

The result of a type conversion, can't be an lvalue.
This falsely gives equivalence to the terms "cast" and "type conversion".
See the wording of N869, 6.3#1 for evidence that this is false. The
conversion by which an array decays into a pointer is not a cast and not
subject to such rules.

As further proof consider that if the pointer to which the array decays
were not an lvalue, we would be prohibited from assigning to its elements.
char object;

(int)object = INT_MAX; /* no good */

Only one byte of memory is reserved for object. There isn't really an
int type object there.


Agreed that (int)object is not an lvalue, but its non-modifiability is
not the proof.

Nov 15 '05 #127
On Sat, 16 Jul 2005 17:18:54 +0000, S.Tobias wrote:
Netocrat <ne******@dodo. com.au> wrote:
On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
Netocrat <ne******@dodo. com.au> wrote:
On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote: N869, 6.3.2.1
"An lvalue is an expression with an object type or an incomplete type
other than void; if an lvalue does not designate an object when it is
evaluated, the behavior is undefined."

The `*ps' is not evaluated in this case.
As I've noted elsewhere, you are correct for C99 but in C90 *ps is
evaluated - at least conceptually i.e. this is a constraint violation:

int *n = NULL.
int *p;
p = &*n;


I don't think so.


Perhaps if you read the defect reports that I referenced you would. Those
reports are #012, #076 and #106, the one dealing specifically with this
case being #076.

Note the final words of #012, dealing with a related case of void *p;
"...as long as a diagnostic message is issued, a translator may assign a
meaning to the expression &*p discussed above. Conforming programs shall
not use this expression, however."

Pretty explicit.
In C90 6.2.2.1 there's a paragraph which is equivalent
of 6.3.2.1#2 in C99 (in n869.txt) (they seem the same, but I haven't
checked). It means that at least the value of `*n' is not taken
(because it is operand to "&").
I only have access to n869 and the C89 draft. n869 has no 6.2.2.1 and
the C89 draft has different numbering, so I don't know where to find that
paragraph. Quote it if you can.

Given the defect reports though, the behaviour required under C90 is clear.
I don't see any cv in C90 except that `*n' is
not strictly an lvalue, which cannot (in general) be established at
compile time (due to poor definition of an lvalue).
That's significant enough to prevent the program from being conforming, so
why downplay it?

Defect report #076:
"Subclause 6.3.3.2 requires the operand of &to be an lvalue; NULL is
not an lvalue. [...] [Therefore t]he use of [the construct *n where n ==
NULL] prevents a program from being strictly conforming..."
In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated. What I
think it means (and why it's necessary) is this: To evaluate "*" is to
establish lvalue for the object that `n' points to (it doesn't yet mean
to take a value - it wouldn't occur anyway in presence of "&").
How could evaluating "*" - in the case where "&*" is not a no-op -
not result in taking a value?

Perhaps you would accept this as a clarification/expansion: at compile
time it is established whether the "*" operator results in an lvalue, but
it cannot always be established that it is legal to evaluate that lvalue.
At runtime evaluating that lvalue may in some cases be illegal.

Also the definition of the indirection operator is lacking in the case of
pointer to incomplete struct.

N869, 6.5.3.2#4:
The unary * operator denotes indirection. If the
operand points to a function, the result is a function
designator; if it points to an object, the result is an
lvalue designating the object. If the operand has type
``pointer to type'', the result has type ``type''. If an
invalid value has been assigned to the pointer, the behavior
of the unary * operator is undefined.74)

In this case, the operand points to neither a function nor an object,
since an incomplete type cannot be an object (as implied by the definition
of lvalue). So all that is specified in this case is the type. This is
where I would have expected to find wording to the effect of "if it points
to an incomplete type, the result is that a syntax error occurs except in
cases where the & operator is immediately applied".
It is UB
if an lvalue doesn't designate an object, and it would be UB if "*" were
evaluated in this case;
Agreed.
since it isn't, "&*n" is well defined for all
values of `n'.
Only in C99 - this is not true in C90 as the defect reports clarify.
> struct s;
> struct s *ps;
> & * ps;
> The sub-expression `*ps' has an incomplete struct type, and is an
> lvalue.


But seriously, clearly *ps on its own is nonsensical; surely you agree
with me on that?


I agree. But OTOH the notion of an lvalue is more of a pointer, or
half-dereferenced pointer (which establishes only a position of an
object, but not its range or value). In this context "& * ps" is not
strange.


It's not strange, but without the &* no-op, it's invalid. In 6.3.2.1 as
quoted top of post: "if an lvalue does not designate an object when it is
evaluated, the behavior is undefined." Since *ps represents an incomplete
type, it cannot be an object. Therefore the behaviour is undefined.

So 6.3.2.1 is effectively saying, "an expression with incomplete type is
an lvalue, but cannot be evaluated as one". Which is pretty meaningless
except for the &* case.

<snip>
I'm not satisfied that your code is an example of an incomplete struct
occurring as an lvalue. In the case where &* is being treated as a
no-op, then semantically *ps (and hence the incomplete struct) never
occurs.


But you have to know in advance if "*ps" is an lvalue before you can put
"&" and "*ps" together (whether "&*" is a no-op, or not); if it weren't
an lvalue, then there'd be constraint violation.


So we could say that *ps is known in advance to be an lvalue but its
evaluation as such is prohibited.
And in C99 "&*" is not quite a no-op: the expression after that is no
longer an lvalue (if it was before):
n = 0;
&*n = 0; //CV


Agreed, I missed that.

Nov 15 '05 #128
Netocrat wrote:

On Sat, 16 Jul 2005 10:45:44 +0000, pete wrote:
pete wrote:

Netocrat wrote:

> > After it decays into int*, it's no longer an lvalue.
>
> What would stop it from being an lvalue?
> It fits the definition of
> "[a]n expression with an object type ... other than void".


N869

6.3.2 Other operands
6.3.2.1 Lvalues and function designators

[#3] Except when it is the operand of the sizeof
operator or the
unary & operator, or is a string literal used to
initialize an array, an expression that has type ``array of
type'' is converted to an expression with type ``pointer to
type'' that points to the initial element of the array
object

and is not an lvalue.


Which you seem to be justifying using the logic of your previous post:


No.
I'm quoting the standard for the purpose
of correctly answering your question:
"What would stop it from being an lvalue?"

The "and is not an lvalue." is quoted from the standard.
I separated it from the rest of the text to make it stand out.

"A cast does not yield an lvalue"

The result of a type conversion, can't be an lvalue.


This falsely gives equivalence to the
terms "cast" and "type conversion".


Yes.
I should have supplied the verse from the standard originally.

--
pete
Nov 15 '05 #129
On Sun, 17 Jul 2005 00:14:39 +0000, pete wrote:

<snip>
I'm quoting the standard for the purpose
of correctly answering your question:
"What would stop [an array decayed to a pointer] from being an lvalue?"

The "and is not an lvalue." is quoted from the standard. I separated
it from the rest of the text to make it stand out.


OK, you and Stan are correct.

Nov 15 '05 #130

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
4439
by: pemo | last post by:
I've just been trying out the Watcom compiler from http://www.openwatcom.org, and almost immediately compiled some working source that errored. The code is char buffer; ...
34
3625
by: thibault.langlois | last post by:
Hello, I get a warning when I compile: #include <string.h> int main (int argc, char ** argv) { char * s; s = strdup("a string"); return 0; }
7
2147
by: vippstar | last post by:
Today I got a confusing message from gcc (I'm aware, those don't break conformance ;-) In function 'main': 7: warning: format '%d' expects type 'int', but argument 2 has type 'char (*)' The code is #include <stdio.h>
0
8683
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8610
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9170
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9031
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
8902
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
7740
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5862
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
1
3052
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
2007
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.