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

Discussion in 'C Programming' started by I.M. !Knuth, Jun 26, 2006.

  1. I.M. !Knuth

    I.M. !Knuth Guest

    Hi. I'm more-or-less a C newbie. I thought I had pointers under control until
    I started goofing around with this:

    ================================================================================
    /* A function that returns a pointer-of-arrays to the calling function. */

    #include <stdio.h>

    int *pfunc(void);

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

    pd = pfunc();

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

    return 0;
    }

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

    return(*arr);
    }

    ================================================================================
    It compiles and works correctly, but the compilers I've tried it on warn me of
    "illegal conversion of pointer type" or "suspicious pointer conversion"
    regarding:

    pd = pfunc();

    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?

    When I implement what I'm really working towards with this, I won't have the
    option of combining the lines, so how serious is this warning?

    Any elucidation is greatly appreciated.


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 26, 2006
    #1
    1. Advertising

  2. I.M. !Knuth

    Ian Collins Guest

    I.M. !Knuth wrote:
    > Hi. I'm more-or-less a C newbie. I thought I had pointers under control until
    > I started goofing around with this:
    >
    > ================================================================================
    > /* A function that returns a pointer-of-arrays to the calling function. */
    >
    > #include <stdio.h>
    >
    > int *pfunc(void);
    >
    > int main(void)
    > {
    > int (*pd)[5];
    >
    > pd = pfunc();
    >
    > /* Should return 20 and 9 */
    > printf("\nDigits are: %d and %d", *(*(pd + 0) + 3), *(*(pd + 1) + 2) );
    >
    > return 0;
    > }
    >
    > int *pfunc(void)
    > {
    > static int arr[2][5] = {{5, 10, 15, 20, 25}, {3, 6, 9, 12, 15}};
    >
    > return(*arr);
    > }
    >
    > ================================================================================
    > It compiles and works correctly, but the compilers I've tried it on warn me of
    > "illegal conversion of pointer type" or "suspicious pointer conversion"
    > regarding:
    >
    > pd = pfunc();
    >
    > 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:
    >

    They are different types. My compiler gives a more helpful warning
    which says it all:

    warning: assignment type mismatch:
    pointer to array[5] of int "=" pointer to int

    --
    Ian Collins.
     
    Ian Collins, Jun 26, 2006
    #2
    1. Advertising

  3. I.M. !Knuth

    I.M. !Knuth Guest

    Ian Collins wrote:
    >They are different types. My compiler gives a more helpful warning
    >which says it all:
    >
    >warning: assignment type mismatch:
    > pointer to array[5] of int "=" pointer to int
    >


    That makes sense. Do you happen to know of a way I can explicitly tell
    the compiler:

    pd[5] = pfunc();

    .. . . or some such? If I enter the above (and I've tried all sorts of
    colourful combinations, *s included) I get: "only lvalues may take
    assignment" errors.

    Thanks for you attention.


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 26, 2006
    #3
  4. I.M. !Knuth

    Chris Torek Guest

    In article <>
    I.M. !Knuth <> wrote:
    >int *pfunc(void);


    This declares pfunc as a function taking no arguments and returning
    "int *", or "pointer to int". A value of type "pointer to int" can
    (but does not necessarily) point to the first of one or more "int"s
    in sequence, or even into the middle of such a sequence:

    int *p1;
    int x, y[4];

    p1 = NULL; /* p1 does not point to any "int"s */
    p1 = &x; /* p1 points to a single "int" */
    p1 = &y[0]; /* p1 points to the first of 4 ints */
    p1 = &y[1]; /* p1 points to the second of 4 ints, so p1[-1] is y[0] */

    Presumably pfunc() will return a pointer to a single int, or to the
    first of a sequence of "int"s.

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


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

    "pd" has type "pointer to array 5 of int". As with p1 above, it can
    (not necessarily "does") point to the first of one or more "array 5
    of int"s in sequence.

    > pd = pfunc();


    Type mismatch: pfunc() returns a pointer to an int, or some int
    within a sequence of "int"s. "pd" needs to point to an "array 5
    of int", or the first of a sequence of "array 5 of int"s.

    > /* 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]);

    > return 0;


    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).

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


    "arr" has type "array 2 of array 5 of int". That is, it is an array
    of 2 "things", where each of those "things" is an "array 5 of int".
    arr[0] is the first of those things and arr[1] is the second.

    > return(*arr);
    >}


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

    return expression;

    and of course expressions can be parenthesized. In any case, the
    effect is to return a value.

    *arr is one way to write arr[0]. This names the entire "array 5
    of int" containing the sequence {5, 10, 15, 20, 25}. Whenever you
    name an array object in a place where a value is required, C compilers
    will produce, as a value, the address of the first element of that
    array. Hence:

    return arr[0];

    "means" the same thing as:

    return &arr[0][0];

    Hence pfunc() returns a pointer to the "int" holding the value 5
    (namely arr[0][0]), which is of course the first of five "int"s
    in sequence.

    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] 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. :) )

    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:


    >It compiles and works correctly, but the compilers I've tried it
    >on warn me of "illegal conversion of pointer type" or "suspicious
    >pointer conversion" regarding:
    >
    >pd = pfunc();


    The C standard says only that "a diagnostic" is required. A warning
    suffices as a diagnostic, as does having the compiler spin the
    CD-ROM really fast so that it makes a horrible whining sound (as
    long as the compiler's documentation says as much). An "error"
    that aborts compilation entirely is also a valid diagnostic.

    >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.

    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.

    Using a typedef -- which merely exchanges one ugly syntax for a
    different ugly syntax, in my personal opinion :) -- we get something
    like this:

    #include <stdio.h>

    typedef int Zog[5]; /* Zog is now an alias for int[5] */

    Zog *pfunc(void);

    int main(void) {
    Zog *pd;

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

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

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

    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]; */
    }

    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.

    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. That
    is, given the "Zog" typedef above, the following is not valid C:

    Zog f(void) {
    Zog result;

    while (need_more_work())
    fill_it_in(&result);
    return result;
    }

    But if we were to replace the typedef line with, e.g.:

    struct zog { int val[5]; };
    typedef struct zog Zog;

    then the function f() above would suddenly become valid C. Similarly,
    if we have no idea whether "Morgle" is a typedef for an array type,
    we cannot tell whether the following can be simplified:

    void operate(void) {
    Morgle a, b;

    init(&a);
    memcpy(&b, &a, sizeof a); /* ??? do we need this ? */

    while (more_to_do())
    frob(a);

    /* make sure frob() did not modify "a" */
    if (memcmp(&a, &b) != 0)
    printf("alas! alack!\n");
    }

    If Morgle is *not* an array type, frob() will be unable to modify
    "a", because frob() takes the *value* of "a", not the address of
    "a". In this case, the copy in "b" is pointless and the memcmp()
    will never show them as different, so we do not need the copy.
    But if Morgle *is* an array type, frob() receives a pointer to the
    first element of "a", and is able to modify "a".

    (In some cases we can use "const" to promise, weakly, that frob()
    will not modify "a" even if it gets a pointer to the first element;
    but this promise can be violated, and in some cases adding "const"
    is inappropriate anyway. I think it is better to avoid the situation
    entirely.)

    (The heart of the problem is really that C treats arrays "specially".
    Because of this, it is important to know whether some purportedly
    abstract type is in fact an array type. If so, it will not behave
    the way other types behave. C's structure types *do* behave
    "properly", so in the limit, all abstract data types should be
    "struct"s.)

    The one place where even I break down and use "typedef" :) is for
    pointer-to-function types. Consider "signal", which takes two
    parameters:

    - one, an int specifying which signal, and
    - the other, a pointer to a signal-hanlding function

    and returns one value:

    - a pointer to a signal-handling function

    where the repeated type -- "pointer to signal-handling function"
    is itself a pointer-to-function-taking-int-and-returning-void,
    or "void (*)(int)", complete with awkwardly-placed parentheses,
    asterisks, and parameter types. If we write down one typedef for
    this particular type, we can then use it twice and get:

    typedef void (*Sig_func_ptr)(int);
    Sig_func_ptr signal(int sig, Sig_func_ptr func);

    Of course, the standard header <signal.h> is not allowed to use
    names that are in the user's namespace, so most implementors expand
    the types in-line, and omit the parameter names, giving:

    void (*signal(int, void (*)(int)))(int);

    which is confusing at best. If you are the implementor, and
    go to write the function's definition, it gets even worse:

    void (*signal(int sig, void (*func)(int)))(int) {
    void (*old(int, void (*)(int)))(int);

    if (sig < __MIN_SIGNO || sig >= __MAX_SIGNO)
    return SIG_ERR;

    some sort of signal atomicity magic here;

    old = __sigtable[sig - __MIN_SIGNO];
    /*
    * May need additional work depending on sig and/or whether
    * func == SIG_DFL or SIG_IGN. For instance, instead of the
    * crazy top-of-stack "trampoline code" that BSD systems (used
    * to?) use, we might do something like this:
    *
    * if (func == SIG_DFL || func == SIG_IGN)
    * kernel_entry = func;
    * else
    * kernel_entry = __sigtramp;
    * __sig_syscall(sig, kernel_entry, and, any, other, args);
    *
    * Then the local signal table contains the userland handler,
    * while the kernel is told to jump to the trampoline code
    * in the library, no matter where that has been loaded.
    * Now the library always automatically matches itself, even
    * with future version changes, e.g., to save additional state.
    *
    * I believe Sun did something like this way back in SunOS 4.x
    * or 5.0 or so.
    */
    __sigtable[sig - MIN_SIGNO] = func;

    more atomicity here including check for pending signals;

    return old;
    }

    Of course, signals (with their associated atomicity issues, operating
    system interactions, and hardware dependencies) are "relatively deep
    magic" in the first place.
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Jun 26, 2006
    #4
  5. I.M. !Knuth <> writes:
    > Hi. I'm more-or-less a C newbie. I thought I had pointers under
    > control until I started goofing around with this:
    >
    > ========================================================================
    > /* A function that returns a pointer-of-arrays to the calling function. */
    >
    > #include <stdio.h>
    >
    > int *pfunc(void);
    >
    > int main(void)
    > {
    > int (*pd)[5];
    >
    > pd = pfunc();
    >
    > /* Should return 20 and 9 */
    > printf("\nDigits are: %d and %d", *(*(pd + 0) + 3), *(*(pd + 1) + 2) );
    >
    > return 0;
    > }
    >
    > int *pfunc(void)
    > {
    > static int arr[2][5] = {{5, 10, 15, 20, 25}, {3, 6, 9, 12, 15}};
    >
    > return(*arr);
    > }
    >
    > ========================================================================
    > It compiles and works correctly, but the compilers I've tried it on
    > warn me of "illegal conversion of pointer type" or "suspicious
    > pointer conversion" regarding:
    >
    > pd = pfunc();
    >
    > 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?


    I don't know. When I tried it, the warning didn't go away.

    You need to understand the difference between a pointer-to-int and a
    pointer-to-array-of-int. Your function returns a pointer to int. (It
    happens to point to the first element of an array). You assign the
    result to a pointer-to-array-of-int, an incompatible type.

    You can either change the function so it returns a result of type
    int(*)[5] (pointer to array of 5 int), or you can change the caller so
    it assigns the int* result to an int* variable.

    In my opinion, points to arrays are rarely useful. Arrays are
    commonly accessed via a pointer to the element type; this allows the
    length to vary, rather than requiring a fixed size.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jun 26, 2006
    #5
  6. I.M. !Knuth

    I.M. !Knuth Guest

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

    Chris Torek wrote:
    >I.M. !Knuth <> 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] 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
     
    I.M. !Knuth, Jun 26, 2006
    #6
  7. I.M. !Knuth

    I.M. !Knuth Guest

    Keith Thompson wrote:
    >I.M. !Knuth <> writes:

    <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?

    >
    >I don't know. When I tried it, the warning didn't go away.
    >
    >You need to understand the difference between a pointer-to-int and a
    >pointer-to-array-of-int. Your function returns a pointer to int. (It
    >happens to point to the first element of an array). You assign the
    >result to a pointer-to-array-of-int, an incompatible type.
    >
    >You can either change the function so it returns a result of type
    >int(*)[5] (pointer to array of 5 int), or you can change the caller so
    >it assigns the int* result to an int* variable.
    >
    >In my opinion, points to arrays are rarely useful. Arrays are
    >commonly accessed via a pointer to the element type; this allows the
    >length to vary, rather than requiring a fixed size.


    Mr. Torec (above) scrutinised my code quite throroughly and I think
    I've now got a good handle on what was happening. He also suggests a
    compiler bug caused the warning to disappear, an evaluation that
    appears spot on. If I read your third paragraph correctly, a believe
    his recommendation and yours are in full agreement.

    I'm too new (and inexperienced) at C to say how useful (or not)
    pointers to arrays are, but since arrays are essentially pointers to
    themselves, I think I understand where you're going. What messed me up
    in this case was trying to have a function return a single pointer to a
    multi-dimensional array.

    Thanks for your help.

    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 26, 2006
    #7
  8. I.M. !Knuth <> writes:
    [...]
    > 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


    See if you can find a program called "cdecl"; it's very useful for
    unravelling this kind of thing. For example:

    % cdecl
    Type `help' or `?' for help
    cdecl> explain int (*pfunc(void))[5]
    declare pfunc as function (void) returning pointer to array 5 of int

    In fact, I used cdecl myself to figure out how to declare a function
    returning an array of int, the reverse of the above:

    cdecl> declare p as function (void) returning pointer to array 5 of int
    int (*p(void ))[5]

    I actually thought I had found a bug in cdecl because it seemed just
    too ugly to believe, but when I fed it to my compiler (with high
    warning levels), it didn't complain.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jun 26, 2006
    #8
  9. I.M. !Knuth <> writes:
    [...]
    > I'm too new (and inexperienced) at C to say how useful (or not)
    > pointers to arrays are, but since arrays are essentially pointers to
    > themselves, I think I understand where you're going. What messed me up
    > in this case was trying to have a function return a single pointer to a
    > multi-dimensional array.


    No, arrays are not essentially pointers to themselves. An array
    expression is, in most contexts, implicitly *converted* to a pointer
    to its first element.

    Arrays are not pointers. Pointers are not arrays. Arrays are arrays.
    Pointers are pointers.

    Note that the indexing operator, [], is actually defined to work on a
    pointer, not on an array. In the most common case:
    int arr[10];
    ...
    arr[5]
    the prefix "arr" can be used with [] only because it's converted to a
    pointer.

    Read (or re-read) section 6 of the comp.lang.c FAQ at
    <http://www.c-faq.com/>.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jun 26, 2006
    #9
  10. I.M. !Knuth

    I.M. !Knuth Guest

    Keith Thompson wrote:
    <snip>
    >See if you can find a program called "cdecl"; it's very useful for
    >unravelling this kind of thing. For example:
    >
    >% cdecl
    >Type `help' or `?' for help
    >cdecl> explain int (*pfunc(void))[5]
    >declare pfunc as function (void) returning pointer to array 5 of int


    Cool, but I note by the "%" it's a *NIX utility. I'm presently tooling
    around on a WinXP box. Maybe I'll run it on CygWin or something.
    Anyway, I found this link which I gather is the manual way of doing
    what cdecl does: http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

    Now that I know what I'm looking for, I'm amazed at how silent my books
    and the Net is on this subject.

    >In fact, I used cdecl myself to figure out how to declare a function
    >returning an array of int, the reverse of the above:
    >
    >cdecl> declare p as function (void) returning pointer to array 5 of int
    >int (*p(void ))[5]
    >
    >I actually thought I had found a bug in cdecl because it seemed just
    >too ugly to believe, but when I fed it to my compiler (with high
    >warning levels), it didn't complain.


    Since I'm new at this, I can't say that this weird syntax is more ugly
    or surprising to me than any other, and now that I've discovered the
    Left-Right Rule, I think I can grow accustomed to it, though it's all
    still dizzying.

    Unfortunately, regarding *pfunc(), I'm still not out of the woods.
    While . . .

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

    .. . . works handily, the real app I'm working on requires that the
    function take arguments (I might be getting in way over my head; it's
    also meant to be recursive, but that's neither here nor there), so I
    changed my test function to:

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

    .. . . and now it won't compile at all. Two Borland compilers report
    absolutely nothing wrong, but produce no executable, lcc reports "Error
    -1" whatever that means, and the little compiler with the warning bug
    reports "undefined identifier: num (error)" on the line where "num"
    (the int argument passed to *pfunc() ) is used. It seems to me as if
    the formal declaration isn't being passed into the function block.
    <sigh!>

    Thanks for your continued help and advice.


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 27, 2006
    #10
  11. I.M. !Knuth

    I.M. !Knuth Guest

    Keith Thompson wrote:
    <snip>
    >No, arrays are not essentially pointers to themselves. An array
    >expression is, in most contexts, implicitly *converted* to a pointer
    >to its first element.


    Yeah, somehow I knew I'd be stepping in it with that sentence. :-D

    arr == &arr[0];
    *arr == arr[0];

    I believe I've seen the expression "decays to a pointer" a lot in
    regard to array names.

    >Arrays are not pointers. Pointers are not arrays. Arrays are arrays.
    >Pointers are pointers.


    I know you're right, but put this way, I feel I have to re-examine my
    definition of "array".

    >Note that the indexing operator, [], is actually defined to work on a
    >pointer, not on an array. In the most common case:
    > int arr[10];
    > ...
    > arr[5]
    >the prefix "arr" can be used with [] only because it's converted to a
    >pointer.


    Which brings to mind that Mr. Torek criticised my use of . . .

    *(*(arr + 1) + 2)

    .. . . instead of . . .

    arr[1][2]

    Granted the second is more readable, but I thought the first was meant
    to explicitly delineate the indirection of a pointer instead of an
    array, since as you said, pointers are not arrays and vice versa. If
    subscripting is preferred, when is the other notation used, if ever?

    >Read (or re-read) section 6 of the comp.lang.c FAQ at
    ><http://www.c-faq.com/>.


    It's becoming one of my browser's most worn and tattered bookmarks.
    <LOL>


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 27, 2006
    #11
  12. I.M. !Knuth

    Al Balmer Guest

    On Mon, 26 Jun 2006 23:11:14 GMT, I.M. !Knuth <>
    wrote:

    >Keith Thompson wrote:
    ><snip>
    >>See if you can find a program called "cdecl"; it's very useful for
    >>unravelling this kind of thing. For example:
    >>
    >>% cdecl
    >>Type `help' or `?' for help
    >>cdecl> explain int (*pfunc(void))[5]
    >>declare pfunc as function (void) returning pointer to array 5 of int

    >
    >Cool, but I note by the "%" it's a *NIX utility. I'm presently tooling
    >around on a WinXP box. Maybe I'll run it on CygWin or something.
    >Anyway, I found this link which I gather is the manual way of doing
    >what cdecl does: http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html


    Look in your K&R (you *do* have a copy, of course :) at section 5.12,
    "Complicated Declarations". There's an educational implementation of a
    simplified cdecl.

    --
    Al Balmer
    Sun City, AZ
     
    Al Balmer, Jun 27, 2006
    #12
  13. I.M. !Knuth

    I.M. !Knuth Guest

    Al Balmer wrote:
    <snip>
    >Look in your K&R (you *do* have a copy, of course :) at section 5.12,
    >"Complicated Declarations". There's an educational implementation of a
    >simplified cdecl.


    K&R? What's that?

    Just kidding. Got K&R2 of course. Pulling it out right now.

    Thanks.


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 27, 2006
    #13
  14. I.M. !Knuth

    I.M. !Knuth Guest

    Boy do I feel dumb. I was looking at that page just the other day, but
    gave up when my eyes glazed over, so I never connected it to my current
    predicament.

    <hangs head in shame>


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 27, 2006
    #14
  15. I.M. !Knuth <> writes:
    [...]
    > Which brings to mind that Mr. Torek criticised my use of . . .
    >
    > *(*(arr + 1) + 2)
    >
    > . . . instead of . . .
    >
    > arr[1][2]
    >
    > Granted the second is more readable, but I thought the first was meant
    > to explicitly delineate the indirection of a pointer instead of an
    > array, since as you said, pointers are not arrays and vice versa. If
    > subscripting is preferred, when is the other notation used, if ever?


    I suppose you can use "*" and "+" when the prefix is a pointer, and
    "[]" when it's an array (which is going to decay to a pointer anyway),
    but the compiler won't complain either way, and it's not a coding
    convention I've seen before.

    As you know, the expression x[y] is a shorthand for *(x+y). (I'm
    ignoring operator precedence issues). In either case, the pointer
    must be pointing to the first element of an array, so it's really the
    same operation. I'd probably *never* user *(x+y), any more than I'd
    use (*x).y rather than the shorthand x->y.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jun 27, 2006
    #15
  16. I.M. !Knuth

    Guest

    Keith Thompson wrote:


    > % cdecl
    > Type `help' or `?' for help
    > cdecl> explain int (*pfunc(void))[5]
    > declare pfunc as function (void) returning pointer to array 5 of int
    >
    > In fact, I used cdecl myself to figure out how to declare a function
    > returning an array of int, the reverse of the above:
    >
    > cdecl> declare p as function (void) returning pointer to array 5 of int
    > int (*p(void ))[5]


    The reverse of what "above" ? And what do you mean "function
    returning an array..." ? Functions in C don't return arrays.

    >
    > I actually thought I had found a bug in cdecl because it seemed just
    > too ugly to believe, but when I fed it to my compiler (with high
    > warning levels), it didn't complain.


    I don't find it ugly at all.

    Spiros Bousbouras
     
    , Jun 27, 2006
    #16
  17. writes:
    > Keith Thompson wrote:
    >> % cdecl
    >> Type `help' or `?' for help
    >> cdecl> explain int (*pfunc(void))[5]
    >> declare pfunc as function (void) returning pointer to array 5 of int
    >>
    >> In fact, I used cdecl myself to figure out how to declare a function
    >> returning an array of int, the reverse of the above:
    >>
    >> cdecl> declare p as function (void) returning pointer to array 5 of int
    >> int (*p(void ))[5]

    >
    > The reverse of what "above" ?


    The cdecl "declare" command is the reverse (or inverse, if you prefer)
    of the "explain" command.

    > And what do you mean "function
    > returning an array..." ? Functions in C don't return arrays.


    You're right, of course; I meant "function returning a pointer to an
    array", as I wrote in the cdecl "declare" command.

    >> I actually thought I had found a bug in cdecl because it seemed just
    >> too ugly to believe, but when I fed it to my compiler (with high
    >> warning levels), it didn't complain.

    >
    > I don't find it ugly at all.


    Ok.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jun 27, 2006
    #17
  18. I.M. !Knuth

    I.M. !Knuth Guest

    Re: Thanks again! [Was: Warning on assigning...]

    Thought I'd drop in and say thanks again to everyone that replied to my
    post. The little utility I was coding works splendidly, and I learned
    lots.


    --

    I.M. (definitely) !Knuth
     
    I.M. !Knuth, Jun 30, 2006
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alexandra Stehman
    Replies:
    5
    Views:
    30,836
    Chris Smith
    Jun 17, 2004
  2. Prune Tracy
    Replies:
    6
    Views:
    1,844
    Ioannis Vranos
    Oct 14, 2004
  3. Ramesh
    Replies:
    11
    Views:
    4,299
    James Kanze
    Dec 27, 2008
  4. Philipp
    Replies:
    21
    Views:
    1,174
    Philipp
    Jan 20, 2009
  5. weston
    Replies:
    1
    Views:
    274
    Richard Cornford
    Sep 22, 2006
Loading...

Share This Page