Re: Warning on assigning a function-returning-a-pointer-to-arrays



Wow. I weap with gratitude at your thorough analysis of my problem.
Thank you. :-D

Chris Torek wrote:
I.M. !Knuth <not_knuth@xxxxxxxxxxxx> wrote:

<snip>
int main(void)
{
int (*pd)[5];

(Very good definition of main(), by the way. :-) )

Thanks. I'm trying to go "by the book".

<snip>
/* Should return 20 and 9 */
printf("\nDigits are: %d and %d", *(*(pd + 0) + 3), *(*(pd + 1) + 2) );

This could be written more clearly as:

printf("\nDigits are: %d and %d", pd[0][3], pd[1][2]);


I agree that your recommendation is clearer, and I know array-subscript
notation and pointer notation are interchangeable, but I was under the
impression it was preferred practice (style?) to do as I did as a
reminder that it's a pointer and not merely a locally declared array
that is being dereferenced. Is this not correct?

For strict portability, make sure any output is newline-terminated
(e.g., replace the above printf format "\n..." with "...\n", or
"\n...\n" if you like the extra blank line for some reason).

Sound advice, thanks.

<snip>
return(*arr);
}

Aside: the parentheses here are unnecessary, albeit harmless. The
syntax for "return" statements that return a value is:

return expression;

Yeah, I've been flip-flopping on the style of my returns; (note "return
0;" at the end of main() ). :-/

<snip>
Because C's arrays are always "a sequence of identically-typed
elements with no gaps in between"[%], this sequence of 5 "int"s is
going to be followed by the sequence of 5 "int"s that make up the
object arr[1]. That is, if you can somehow "flatten out" the array,
you get 2 * 5 = 10 "int"s in a row, holding the sequence {5, 10,
15, 20, 25, 3, 6, 9, 12, 15}.
-----
% Or more precisely, if there are gaps, they have to be invisible
as long as you stick to code with defined behavior.
-----

(The C standard does not promise that accessing the array in this
"flattened" manner will always work. There may be some rare
situations in which a clever C compiler tracks array-bounds
information so that it "knows" that, e.g., arr[0][i] can never have
a value of "i" greater than 4, and generates machine code that
actually fails if "i" really is greater than 4. But on real C
compilers on real machines, the flattened access usually does work.
Depending on this is like skating across a frozen pond where someone
has put out a "danger -- thin ice" sign. You will probably be
fine, but if you fall in and freeze to death, you will know who to
blame. :-) )

Thanks. That pretty much resolves part of my crisis; I was waffling
between: "Hmm, this warning makes me uneasy," and "But what the hey,
the program 'seems' to run properly." I'll take the warning with the
seriousness it was intended.


In this case, with some luck -- it is not clear whether this is
"good luck" or "bad luck" -- if the above code compiles, the value
pfunc() returns will "flatten out" the array, and this value will
be "re-folded" by the assignment to "pd". Having folded, spindled,
and perhaps mutilated the value, the code will go on to access
arr[0][3] and arr[1][2] through the pointer value now stored in
"pd". These two elements of "arr" should contain 20 and 9
respectively; and that is what you actually saw:

Yes, well your: "perhaps mutilated the value" gives me pause.

<snip>
I'm stumped. I think my syntax is correct, and there's nothing
in the literature or this group's FAQ that tells me otherwise; yet,
if I combine the declaration and initialisation to:

int (*pd)[5] = pfunc();

. . . the warnings go away. What's up with that?

That would indicate a bug in the compiler, as a diagnostic is
still required.

A compiler bug? <grumble, grumble>

You're right too; now that I've checked again, it's just the one
compiler that doesn't repeat the warning.

The "right" thing to do (for some version of "right" at least) is
to have pfunc() return a value of the correct type. This requires
some ugly syntax, or resorting to C's "typedef" type-alias-creating
facility.
<snip>

Wow. Using typedef in this situation would never have crossed my mind.
I agree: it's ugly.

To eliminate the typedef, we just have to expand it out -- but now
we need parentheses and "[5]"s in awkward places, as with the
original definition for "pd" in main():

int (*pfunc(void))[5];

int main(void) {
int (*pd)[5];

pd = pfunc();
printf("%d and %d\n", pd[0][3], pd[1][2]);
return 0;
}

int (*pfunc(void))[5] {
static int arr[2][5] = {{5, 10, 15, 20, 25}, {3, 6, 9, 12, 15}};

return arr; /* or return &arr[0]; */
}

int (*pfunc(void))[5]? That rocks! I didn't think that was possible.
So, this is an array-of-functions-that-return-pointers-to-int? Or is
it a (single) function-that-returns-an-array-of-pointers-to-int?
Honestly, I'm not sure what I'm looking at. :-O

Note that (alas) in C89, pfunc() can only return a pointer to (the
first of several of) "array 5 of int"s. So we can change "arr" to
"static int arr[123][5]", or "static int arr[42][5]", but never to
"static int arr[2][7]", for instance. C99's "variable-length
arrays" and "variably modified" types solve this particular problem.

That's fine. For my present purposes arr[][5] will do. While I'm
aware of C99's VLAs and I have access to the ISO, I'm certain none of
my freebie compilers are C99 compliant.

Aside: I dislike typedefs in general, and I dislike typedefs for
array types even more because of C's peculiar treatment of arrays.
Unlike every other data type, you *must* know whether some type
is an array type in order to predict the behavior of arguments,
and know whether it is OK to return a value of that type.
<snip>

I have no bias towards typedef one way or another, but I do try to
refrain from using it. Never would I think to "disguise" an array as
something else with typedef; that's new to me.

I fear the rest of your reply is a tad over my head, but that's not to
say all your typing will be wasted. I'll print out a hardcopy and work
through it until I "get it".

Thanks again for all your help.


--

I.M. (definitely) !Knuth
.



Relevant Pages

  • Re: Error handling library
    ... which lets the compiler catch out-of-range usage. ... and assuming that a higher int means "more dangerous error" ... with a comment warning that one is used as an index into the array ... languages while running (an array of languages, ...
    (comp.lang.c)
  • Re: Maximum char array size?
    ... versus declaring an array at compile time (other than the need ... I did not get anywhere before I declared the new type ARRAY2. ... and DMC (Digital Mars Compiler) on WinXp. ... void PrintArray(char *name, int *array, int ncol, int nrow) ...
    (comp.lang.c)
  • Re: On VLAs and incomplete types
    ... I don't understand how a variable is handled by the compiler in ... declaring a VLA. ... an array must be declared with an ... int x; ...
    (comp.lang.c)
  • Re: max size of ptr array
    ... you'll get a compiler error. ... number you are allowed type in the brackets is exactly 536870911. ... int main{ ... And since an int requires 4 "bytes" plus the fact that your array is ...
    (microsoft.public.vc.language)
  • Re: Micro$haft desperate! Buy Windoze 7, get (virtual) XP free!
    ... I have never heard of a compiler that used it any way, and it's hard to imagine what a lexer could even do with the notion. ... I have noticed that Chomsky's supporters seem to like to puff up his resume a bit, pretending he's more significant outside of linguistics than he actually is. ... Well, no, there are a basic set of types not defined in any headers, and int is one of them. ...
    (comp.sys.mac.advocacy)