[TinyCC] sizeof of element of struct returned by function

Discussion in 'C Programming' started by Maciej Labanowicz, Feb 20, 2013.

  1. Hello,

    Is following code correct (ANSI C89) ?
    ----
    01: #include <stdio.h>
    02: #include <stdlib.h>
    03: struct { char array1 [100]; } * fun1(int x1);
    04: struct { char array2 [200]; } fun2(int x2);
    05: int main(void) {
    06: printf("sizeof(array1) = %lu\n", (unsigned long)(sizeof(fun1(23)-
    >array1)));

    07: printf("sizeof(array2) = %lu\n", (unsigned long)
    (sizeof(fun2(45).array2)));
    08: return EXIT_SUCCESS;
    09: }
    ----

    If NO then rest of this post may be discarded.

    If YES then it seems that I have found an issue
    with compiler (tcc version 0.9.26).

    Following example is compiled without any error/warning,
    but final application generates "Segmentation Fault" in line 31.

    I would like to have confirmation about correctness of this code.

    --[beg]-----------------------------------------------------
    C:\tcc> gawk '{printf("%02u: %s\n", NR, $0);}' main.c
    01: #include <stdio.h>
    02: #include <stdlib.h>
    03: #include <string.h>
    04: typedef struct {
    05: void * ptr;
    06: } bar_t;
    07: typedef struct {
    08: bar_t bar;
    09: int * iptr;
    10: } foo_t;
    11: bar_t * fun(foo_t * foo_ptr) {
    12: bar_t * result = NULL;
    13: printf("++ fun(foo_ptr = %p)\n", (void *)foo_ptr);
    14: result = &(foo_ptr->bar);
    15: printf("-- fun(...) = %p\n", (void *)result);
    16: return result;
    17: }
    18: #define TEST_MACRO(foo_ptr) \
    19: ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    20: ? 12 : 34)
    21: int main(void) {
    22: foo_t foo;
    23: memset(&foo, 0, sizeof(foo));
    24: foo.bar.ptr = &foo;
    25: printf("Hello World\n");
    26: printf("foo.bar.ptr = %p\n", foo.bar.ptr);
    27: printf("foo.iptr = %p\n", (void *)foo.iptr);
    28: printf("fun(&foo) = %p\n", (void *)fun(&foo));
    29: printf("fun(&foo)->ptr = %p\n", fun(&foo)->ptr);
    30: printf("+++++++++++\n");
    31: printf("%d\n", TEST_MACRO(((foo_t *)((fun(&foo))->ptr))));
    32: printf("-----------\n");
    33: return EXIT_SUCCESS;
    34: }
    C:\tcc>tcc -c -o main.o main.c
    C:\tcc>tcc -o main.exe main.o
    C:\tcc>main.exe
    Hello World
    foo.bar.ptr = 0012FF78
    foo.iptr = 00000000
    ++ fun(foo_ptr = 0012FF78)
    -- fun(...) = 0012FF78
    fun(&foo) = 0012FF78
    ++ fun(foo_ptr = 0012FF78)
    -- fun(...) = 0012FF78
    fun(&foo)->ptr = 0012FF78
    +++++++++++
    <:seg-fault:>
    --[eof]-----------------------------------------------------

    NOTE: Simplifying this code for example in line 19:
    - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    + ((sizeof(*(( foo_ptr )->iptr)) == 123) \
    makes that issue disappears.

    Regards

    --
    Maciej Labanowicz
    Maciej Labanowicz, Feb 20, 2013
    #1
    1. Advertising

  2. Maciej Labanowicz

    Noob Guest

    Maciej Labanowicz wrote:

    > Is following code correct (ANSI C89) ?
    > ----
    > 01: #include <stdio.h>
    > 02: #include <stdlib.h>
    > 03: struct { char array1 [100]; } * fun1(int x1);
    > 04: struct { char array2 [200]; } fun2(int x2);
    > 05: int main(void) {
    > 06: printf("sizeof(array1) = %lu\n", (unsigned long)(sizeof(fun1(23)-
    >> array1)));

    > 07: printf("sizeof(array2) = %lu\n", (unsigned long)
    > (sizeof(fun2(45).array2)));
    > 08: return EXIT_SUCCESS;
    > 09: }
    > ----


    NB: Adding line numbers makes it impossible to paste as-is.
    You could either remove them, or provide them as comments.

    > If YES then it seems that I have found an issue
    > with compiler (tcc version 0.9.26).


    Yes, as far as gcc 4.6.3 can tell :)
    $ gcc -std=c89 -pedantic -Wall -Wextra x.c

    However, I don't find it surprising that a compiler whose main goals
    are 1) simplicity and 2) compilation speed would stumble on some
    corner case.

    https://en.wikipedia.org/wiki/Tiny_C_Compiler

    Regards.
    Noob, Feb 20, 2013
    #2
    1. Advertising

  3. Maciej Labanowicz

    James Kuyper Guest

    On 02/20/2013 06:54 AM, Maciej Labanowicz wrote:
    ....
    > If YES then it seems that I have found an issue
    > with compiler (tcc version 0.9.26).
    >
    > Following example is compiled without any error/warning,
    > but final application generates "Segmentation Fault" in line 31.
    >
    > I would like to have confirmation about correctness of this code.
    >
    > --[beg]-----------------------------------------------------
    > C:\tcc> gawk '{printf("%02u: %s\n", NR, $0);}' main.c
    > 01: #include <stdio.h>
    > 02: #include <stdlib.h>
    > 03: #include <string.h>
    > 04: typedef struct {
    > 05: void * ptr;
    > 06: } bar_t;
    > 07: typedef struct {
    > 08: bar_t bar;
    > 09: int * iptr;
    > 10: } foo_t;
    > 11: bar_t * fun(foo_t * foo_ptr) {
    > 12: bar_t * result = NULL;


    In general, I'd recommend checking whether foo_ptr is null before trying
    to dereferencing it. In this particular case, it's trivial to see to
    that it's never called with a null pointer argument, but in more
    realistic programs, it's a useful safety measure.

    Why set 'result' to NULL? You never use 'result' until after it has
    already been changed to a different value. Why not simply set it to the
    correct value from the beginning?

    > 13: printf("++ fun(foo_ptr = %p)\n", (void *)foo_ptr);
    > 14: result = &(foo_ptr->bar);
    > 15: printf("-- fun(...) = %p\n", (void *)result);
    > 16: return result;
    > 17: }
    > 18: #define TEST_MACRO(foo_ptr) \
    > 19: ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    > 20: ? 12 : 34)


    In general, it's a good idea to parenthesize the arguments of a macro
    wherever they appear it it's expansion. You also have some unnecessary
    parenthesis. I'd re-write that as

    #define TEST_MACRO(foo_ptr) \
    (sizeof (*(1 ? (foo_ptr) : (foo_ptr))->iptr) == 123 ? 12 : 34)

    > 21: int main(void) {
    > 22: foo_t foo;
    > 23: memset(&foo, 0, sizeof(foo));
    > 24: foo.bar.ptr = &foo;
    > 25: printf("Hello World\n");
    > 26: printf("foo.bar.ptr = %p\n", foo.bar.ptr);
    > 27: printf("foo.iptr = %p\n", (void *)foo.iptr);


    Your memset() of foo caused every byte of foo.iptr to be 0. This is NOT
    guaranteed to leave it containing a valid pointer representation. In
    particular, it's not guaranteed to be a valid representation of a null
    pointer value. You should not be trying to retrieve the value of
    foo.iptr until it has been set to a valid pointer value; otherwise the
    behavior of your program is undefined. In principle, this issue could
    explain your seg-fault below, even though the seg-fault occurs much
    later - that's because there are no limits on undefined behavior.
    However, in practice, it's unlikely that the representation is invalid,
    and even if it were, it would be extremely unlikely that it would cause
    such a long-delayed failure.

    > 28: printf("fun(&foo) = %p\n", (void *)fun(&foo));
    > 29: printf("fun(&foo)->ptr = %p\n", fun(&foo)->ptr);
    > 30: printf("+++++++++++\n");
    > 31: printf("%d\n", TEST_MACRO(((foo_t *)((fun(&foo))->ptr))));


    Several of the parentheses in that macro invocation are unnecessary. The
    following should be equivalent:

    TEST_MACRO((foo_t*)fun(&foo)->ptr)

    > 32: printf("-----------\n");
    > 33: return EXIT_SUCCESS;
    > 34: }
    > C:\tcc>tcc -c -o main.o main.c
    > C:\tcc>tcc -o main.exe main.o
    > C:\tcc>main.exe
    > Hello World
    > foo.bar.ptr = 0012FF78
    > foo.iptr = 00000000
    > ++ fun(foo_ptr = 0012FF78)
    > -- fun(...) = 0012FF78
    > fun(&foo) = 0012FF78
    > ++ fun(foo_ptr = 0012FF78)
    > -- fun(...) = 0012FF78
    > fun(&foo)->ptr = 0012FF78
    > +++++++++++
    > <:seg-fault:>


    I don't have any good ideas about why that's happening. I don't have
    tcc; the code works under gcc.

    > --[eof]-----------------------------------------------------
    >
    > NOTE: Simplifying this code for example in line 19:
    > - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    > + ((sizeof(*(( foo_ptr )->iptr)) == 123) \
    > makes that issue disappears.


    Nor do I have any idea why that would help. Sorry that I couldn't be
    more helpful.
    --
    James Kuyper
    James Kuyper, Feb 20, 2013
    #3
  4. Re: sizeof of element of struct returned by function

    Oops, this is important issue.

    After applying changes:
    - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    + ((sizeof(*((1 ? (foo_ptr) : (foo_ptr))->iptr)) == 123) \
    - memset(&foo, 0, sizeof(foo));
    + foo.iptr = NULL;
    "segfault" is still present :)

    Thanks for Your comments.

    --
    Maciej Labanowicz
    Maciej Labanowicz, Feb 20, 2013
    #4
  5. Maciej Labanowicz

    Shao Miller Guest

    On 2/20/2013 06:54, Maciej Labanowicz wrote:
    > Hello,
    >


    Hello, are you going to follow up on your last thread?

    > Is following code correct (ANSI C89) ?


    That depends on what you mean. It depends on casts to 'unsigned long'
    from 'size_t', which can be criticized.

    > ----
    > 01: #include <stdio.h>
    > 02: #include <stdlib.h>
    > 03: struct { char array1 [100]; } * fun1(int x1);
    > 04: struct { char array2 [200]; } fun2(int x2);
    > 05: int main(void) {
    > 06: printf("sizeof(array1) = %lu\n", (unsigned long)(sizeof(fun1(23)->array1)));


    You might as well have used 'fun1(0)', since there is no call:

    printf(
    "sizeof(array1) = %lu\n",
    (unsigned long) sizeof fun1(0)->array1
    );

    > 07: printf("sizeof(array2) = %lu\n", (unsigned long)
    > (sizeof(fun2(45).array2)));
    > 08: return EXIT_SUCCESS;
    > 09: }
    > ----
    >
    > If NO then rest of this post may be discarded.
    >


    No. The line numbers do not belong. Everyone's got their own opinion,
    but your use of line numbers causes someone wishing to copy and paste
    your code to perform steps that could be avoided if the line numbers
    were omitted. I also find it harder to read, personally.

    > If YES then it seems that I have found an issue
    > with compiler (tcc version 0.9.26).
    >
    > Following example is compiled without any error/warning,
    > but final application generates "Segmentation Fault" in line 31.
    >
    > I would like to have confirmation about correctness of this code.
    >
    > --[beg]-----------------------------------------------------
    > C:\tcc> gawk '{printf("%02u: %s\n", NR, $0);}' main.c


    I beg you to drop line numbers from your posts. :)

    > 01: #include <stdio.h>
    > 02: #include <stdlib.h>
    > 03: #include <string.h>
    > 04: typedef struct {
    > 05: void * ptr;
    > 06: } bar_t;
    > 07: typedef struct {
    > 08: bar_t bar;
    > 09: int * iptr;
    > 10: } foo_t;
    > 11: bar_t * fun(foo_t * foo_ptr) {
    > 12: bar_t * result = NULL;
    > 13: printf("++ fun(foo_ptr = %p)\n", (void *)foo_ptr);
    > 14: result = &(foo_ptr->bar);
    > 15: printf("-- fun(...) = %p\n", (void *)result);
    > 16: return result;
    > 17: }
    > 18: #define TEST_MACRO(foo_ptr) \
    > 19: ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    > 20: ? 12 : 34)


    Taking this apart, the macro appears to expand to:

    (E1 ? 12 : 34)

    E1 is:

    (E2 == 123)

    E2 is:

    sizeof (E3)

    Unless VLAs are involved, which they do not appear to be, then E3 should
    not involve any side effects.

    > 21: int main(void) {
    > 22: foo_t foo;
    > 23: memset(&foo, 0, sizeof(foo));


    Redundant parentheses for the 'sizeof' operator. Why not use the
    following?:

    ((memset)((&foo), (0), (sizeof (foo))));

    > 24: foo.bar.ptr = &foo;
    > 25: printf("Hello World\n");
    > 26: printf("foo.bar.ptr = %p\n", foo.bar.ptr);
    > 27: printf("foo.iptr = %p\n", (void *)foo.iptr);


    How do you know that 'foo.iptr' has a pointer value stored? Whether or
    not your 'memset' did that is implementation-defined.

    > 28: printf("fun(&foo) = %p\n", (void *)fun(&foo));
    > 29: printf("fun(&foo)->ptr = %p\n", fun(&foo)->ptr);
    > 30: printf("+++++++++++\n");
    > 31: printf("%d\n", TEST_MACRO(((foo_t *)((fun(&foo))->ptr))));
    > 32: printf("-----------\n");
    > 33: return EXIT_SUCCESS;
    > 34: }
    > C:\tcc>tcc -c -o main.o main.c
    > C:\tcc>tcc -o main.exe main.o
    > C:\tcc>main.exe
    > Hello World
    > foo.bar.ptr = 0012FF78
    > foo.iptr = 00000000
    > ++ fun(foo_ptr = 0012FF78)
    > -- fun(...) = 0012FF78
    > fun(&foo) = 0012FF78
    > ++ fun(foo_ptr = 0012FF78)
    > -- fun(...) = 0012FF78
    > fun(&foo)->ptr = 0012FF78
    > +++++++++++
    > <:seg-fault:>
    > --[eof]-----------------------------------------------------
    >
    > NOTE: Simplifying this code for example in line 19:
    > - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    > + ((sizeof(*(( foo_ptr )->iptr)) == 123) \
    > makes that issue disappears.


    As mentioned before, if undefined behaviour is invoked, code changes
    which should not have an apparent change in behaviour, sometimes do.

    In this case though, perhaps there is a bug. C99 VLA support was just
    introduced on Feb. 15th (5 days ago), so it would not be surprising.
    Perhaps you could make a more minimal test case and report it to Thomas
    Preud'homme.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Feb 20, 2013
    #5
  6. Re: sizeof of element of struct returned by function

    On Wed, 20 Feb 2013 06:23:44 -0800 (PST), Maciej Labanowicz
    <> wrote:

    >Oops, this is important issue.
    >
    >After applying changes:
    >- ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    >+ ((sizeof(*((1 ? (foo_ptr) : (foo_ptr))->iptr)) == 123) \
    >- memset(&foo, 0, sizeof(foo));
    >+ foo.iptr = NULL;
    >"segfault" is still present :)


    You really should provide the changes in context. All I can tell from
    this is you don't have enough right parentheses.

    Unless you have hidden a variable length array in there somewhere,
    sizeof does not evaluate its operand. What does your sizeof
    expression really mean? Do you really think the parentheses change
    anything?

    Have you verified the trick works with a simple example such as

    int main(void){
    char c;
    double d;
    int i1, i2;
    i1 = sizeof(1 ? c : d);
    i2 = sizeof(0 ? c : d);
    printf("%d %d\n", i1, i2);
    return 0;
    }

    If you break each + and - term into its own statement, such as
    x = -sizeof(*(1 ? foo...
    x += sizeof(*(1 ? (foo...
    x -= memset(...
    and use a debugger you can determine where the seg fault occurs.

    --
    Remove del for email
    Barry Schwarz, Feb 21, 2013
    #6
  7. Maciej Labanowicz

    Shao Miller Guest

    Re: sizeof of element of struct returned by function

    On 2/20/2013 21:27, Barry Schwarz wrote:
    > On Wed, 20 Feb 2013 06:23:44 -0800 (PST), Maciej Labanowicz
    > <> wrote:
    >
    >> Oops, this is important issue.
    >>
    >> After applying changes:
    >> - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    >> + ((sizeof(*((1 ? (foo_ptr) : (foo_ptr))->iptr)) == 123) \
    >> - memset(&foo, 0, sizeof(foo));
    >> + foo.iptr = NULL;
    >> "segfault" is still present :)

    >
    > You really should provide the changes in context. All I can tell from
    > this is you don't have enough right parentheses.
    >
    > Unless you have hidden a variable length array in there somewhere,
    > sizeof does not evaluate its operand. What does your sizeof
    > expression really mean? Do you really think the parentheses change
    > anything?
    >
    > Have you verified the trick works with a simple example such as
    >
    > int main(void){
    > char c;
    > double d;
    > int i1, i2;
    > i1 = sizeof(1 ? c : d);
    > i2 = sizeof(0 ? c : d);
    > printf("%d %d\n", i1, i2);
    > return 0;
    > }
    >
    > If you break each + and - term into its own statement, such as
    > x = -sizeof(*(1 ? foo...
    > x += sizeof(*(1 ? (foo...
    > x -= memset(...
    > and use a debugger you can determine where the seg fault occurs.
    >


    I'd guess that the TinyCC's 5-day-old VLA support probably caused the
    conditional operator to evaluate its operands even if the conditional
    expression is the operand of the 'sizeof' operator. Just a guess, though.

    #include <stdio.h>

    int (* foo(void))[10] {
    printf("Uh oh\n");
    return 0;
    }

    int main(void) {
    return sizeof *(1 ? foo() : foo());
    }

    If this outputs "Uh oh", then uh oh.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Feb 21, 2013
    #7
  8. Maciej Labanowicz

    James Kuyper Guest

    Re: sizeof of element of struct returned by function

    On 02/20/2013 09:27 PM, Barry Schwarz wrote:
    > On Wed, 20 Feb 2013 06:23:44 -0800 (PST), Maciej Labanowicz
    > <> wrote:
    >
    >> Oops, this is important issue.
    >>
    >> After applying changes:
    >> - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    >> + ((sizeof(*((1 ? (foo_ptr) : (foo_ptr))->iptr)) == 123) \
    >> - memset(&foo, 0, sizeof(foo));
    >> + foo.iptr = NULL;
    >> "segfault" is still present :)

    >
    > You really should provide the changes in context. All I can tell from
    > this is you don't have enough right parentheses.


    In the original context, the first line that she changed was part of the
    definition of a macro, which was followed by this line:

    ? 12 : 34)

    Which brings the parentheses into balance.

    > Unless you have hidden a variable length array in there somewhere,
    > sizeof does not evaluate its operand. What does your sizeof
    > expression really mean? Do you really think the parentheses change
    > anything?


    The additional parentheses were my suggestion, as a safety measure, one
    that doesn't matter in her example program, but which might matter in a
    more complicated one.

    My guess is that the original code had all sorts of complexities that
    are missing from this version, and that the original version of
    TEST_MACRO actually made sense in that environment. Now, after much
    simplification, TEST_MACRO()'s only remaining purpose is to trigger the
    segfault, so she can try to determine why the segfault is occurring.

    > Have you verified the trick works with a simple example such as
    >
    > int main(void){
    > char c;
    > double d;
    > int i1, i2;
    > i1 = sizeof(1 ? c : d);
    > i2 = sizeof(0 ? c : d);
    > printf("%d %d\n", i1, i2);
    > return 0;
    > }
    >
    > If you break each + and - term into its own statement, such as
    > x = -sizeof(*(1 ? foo...
    > x += sizeof(*(1 ? (foo...
    > x -= memset(...
    > and use a debugger you can determine where the seg fault occurs.


    There's really not much choice in the matter. There's only one tricky
    feature that follows the last successfully executed printf() call, and
    that's the expansion of TEXT_MACRO(). The result of that expansion is
    very complicated, but as I understand it, it should all boil down to
    (sizeof(int)==123 ? 12 : 34). Therefore, there's no plausible way for it
    to result in a segfault unless someone's misunderstanding something -
    either me, or the implementors of tinycc.
    --
    James Kuyper
    James Kuyper, Feb 21, 2013
    #8
  9. Maciej Labanowicz

    Shao Miller Guest

    Re: sizeof of element of struct returned by function

    On 2/20/2013 21:50, Shao Miller wrote:
    > On 2/20/2013 21:27, Barry Schwarz wrote:
    >> On Wed, 20 Feb 2013 06:23:44 -0800 (PST), Maciej Labanowicz
    >> <> wrote:
    >>
    >>> Oops, this is important issue.
    >>>
    >>> After applying changes:
    >>> - ((sizeof(*((1 ? foo_ptr : foo_ptr)->iptr)) == 123) \
    >>> + ((sizeof(*((1 ? (foo_ptr) : (foo_ptr))->iptr)) == 123) \
    >>> - memset(&foo, 0, sizeof(foo));
    >>> + foo.iptr = NULL;
    >>> "segfault" is still present :)

    >>
    >> You really should provide the changes in context. All I can tell from
    >> this is you don't have enough right parentheses.
    >>
    >> Unless you have hidden a variable length array in there somewhere,
    >> sizeof does not evaluate its operand. What does your sizeof
    >> expression really mean? Do you really think the parentheses change
    >> anything?
    >>
    >> Have you verified the trick works with a simple example such as
    >>
    >> int main(void){
    >> char c;
    >> double d;
    >> int i1, i2;
    >> i1 = sizeof(1 ? c : d);
    >> i2 = sizeof(0 ? c : d);
    >> printf("%d %d\n", i1, i2);
    >> return 0;
    >> }
    >>
    >> If you break each + and - term into its own statement, such as
    >> x = -sizeof(*(1 ? foo...
    >> x += sizeof(*(1 ? (foo...
    >> x -= memset(...
    >> and use a debugger you can determine where the seg fault occurs.
    >>

    >
    > I'd guess that the TinyCC's 5-day-old VLA support probably caused the
    > conditional operator to evaluate its operands even if the conditional
    > expression is the operand of the 'sizeof' operator. Just a guess, though.
    >
    > #include <stdio.h>
    >
    > int (* foo(void))[10] {
    > printf("Uh oh\n");
    > return 0;
    > }
    >
    > int main(void) {
    > return sizeof *(1 ? foo() : foo());
    > }
    >
    > If this outputs "Uh oh", then uh oh.
    >


    Then again, it gripes that the second operand of the conditional (the
    zero) needs to be a pointer, for the code below:

    int main(void) {
    return sizeof *(1 ? 0 : (int *) 0);
    }

    So the implementation could use some improvements.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Feb 21, 2013
    #9
  10. Re: sizeof of element of struct returned by function

    On 20 Feb, 20:10, Shao Miller <> wrote:
    > Redundant parentheses for the 'sizeof' operator.


    This is a style that I prefer.
    My experience shows that sometimes using of parentheses is a good
    idea.
    It makes that the code review is simpler, example:
    - if (a + b * c)
    + if (a + (b * c))
    BTW: I remember that my university teacher has always complaining
    about the redundant parentheses, he has mentioned that I should
    know perfectly priority of operators.
    After that I have started to work in private company,
    where such practice is forbidden.
    Parentheses are your friend.

    Another example of the redundant code is the
    initialization of 'result' in function 'fun'.
    But this is also a style that I prefer.
    Any variable should be initialized at begining.
    For example, a someone has fixed this code with:
    if (foo_ptr) {
    result = &(foo_ptr->bar);
    }
    then function is still correctly working.
    In case the initialization is redundant then
    an optimizer will remove the code during compilation process.
    It is very hard to diagnose bugs that depends on the uninitialized
    variable, especially
    if it occures only ones per a week and only on client side (taken from
    real life).

    Regards

    --
    Maciej Labanowicz
    Maciej Labanowicz, Feb 21, 2013
    #10
  11. Maciej Labanowicz

    James Kuyper Guest

    Re: sizeof of element of struct returned by function

    On 02/21/2013 06:41 AM, Maciej Labanowicz wrote:
    > On 20 Feb, 20:10, Shao Miller <> wrote:
    >> Redundant parentheses for the 'sizeof' operator.

    >
    > This is a style that I prefer.
    > My experience shows that sometimes using of parentheses is a good
    > idea.
    > It makes that the code review is simpler, example:
    > - if (a + b * c)
    > + if (a + (b * c))
    > BTW: I remember that my university teacher has always complaining
    > about the redundant parentheses, he has mentioned that I should
    > know perfectly priority of operators.
    > After that I have started to work in private company,
    > where such practice is forbidden.
    > Parentheses are your friend.


    Putting in so many unnecessary parentheses made your expressions very
    confusing - it took me a lot longer to figure them out than it should have.

    > Another example of the redundant code is the
    > initialization of 'result' in function 'fun'.
    > But this is also a style that I prefer.
    > Any variable should be initialized at begining.


    In this case, you can trivially give the variable it's final value at
    the point of definition, so why not do so?

    bar_t * fun(foo_t * foo_ptr) {
    bar_t * result = &(foo_ptr->bar);
    printf("++ fun(foo_ptr = %p)\n", (void *)foo_ptr);
    printf("-- fun(...) = %p\n", (void *)result);
    return result;
    }

    I'm opposed to the idea of initializing a variable with anything other
    than a value that is intended to be used. I will delay definition of the
    variable as long as possible in order to make this happen. In C99, which
    allows inter-mixed declarations and statements, it's possible to delay
    definition a great deal. However, if I reach the point where it's no
    longer possible to delay definition of the variable, and I still can't
    give it a value that's actually intended to be used, I leave it
    uninitialized.

    My reason for this policy is that most modern compilers do a very good
    job of checking whether or not a variable has been given a value before
    it's first use. By initializing it with a value never intended to be
    used, you are disabling that check. You might end up unintentionally
    using the value that was never intended to be used.

    > For example, a someone has fixed this code with:
    > if (foo_ptr) {
    > result = &(foo_ptr->bar);
    > }
    > then function is still correctly working.
    > In case the initialization is redundant then
    > an optimizer will remove the code during compilation process.


    Not always - but redundancy isn't the primary reason I'm opposed to this
    practice.

    > It is very hard to diagnose bugs that depends on the uninitialized
    > variable, especially
    > if it occures only ones per a week and only on client side (taken from
    > real life).


    In my experience, modern compilers are very good at diagnosing possible
    use of uninitialized variables. Achieving certainty about whether an
    uninitialized variable will be used is exactly equivalent to solving the
    halting problem, but if you allow for a small number of false positives,
    such problems can be identified quite easily - in case of doubt, flag it
    as dangerous. If the code's so confusing that the compiler can't be
    certain whether the variable will be used while uninitialized, it's too
    complicated for me to be justifiably certain about it, either. In that
    case, I will relent and give it an initial value that's not intended to
    be used - usually one deliberately chosen to produce a spectacular
    failure if it ever does get used. I would never choose a value that's
    intended to allow the routine to keep working normally - if something
    has happened that wasn't supposed to happen, I want to know about it as
    soon as possible.
    --
    James Kuyper
    James Kuyper, Feb 21, 2013
    #11
  12. Maciej Labanowicz

    Shao Miller Guest

    Re: sizeof of element of struct returned by function

    On 2/21/2013 06:41, Maciej Labanowicz wrote:
    > On 20 Feb, 20:10, Shao Miller <> wrote:
    >> Redundant parentheses for the 'sizeof' operator.

    >
    > This is a style that I prefer.


    Ok, I respect that, then. If I could make a tiny suggestion: Since you
    use 'if (', perhaps you could use 'sizeof (' instead of 'sizeof('? This
    tiny change would have some infinitesimally smaller chance of
    perpetuating 'sizeof' confusion for people who try to learn C from
    source code... Assuming that you use 'func(' and 'macro(' rather than
    'func (' and 'macro ('.

    > My experience shows that sometimes using of parentheses is a good
    > idea.
    > It makes that the code review is simpler, example:
    > - if (a + b * c)
    > + if (a + (b * c))


    Yes, sometimes I introduce redundant parentheses with infix operators,
    too, if it seems to make code more readable, as you say. Your example
    is nice.

    > BTW: I remember that my university teacher has always complaining
    > about the redundant parentheses, he has mentioned that I should
    > know perfectly priority of operators.
    > After that I have started to work in private company,
    > where such practice is forbidden.


    Which practice? This doesn't make sense, to me... Adding redundant
    parentheses is a practice, because you have to choose where to put them.
    Choosing not to add them is not a practice, because you simply don't
    make any choices about where to add them. Can you explain this policy a
    bit more? Does the policy explain exactly when and where to add them?

    > Parentheses are your friend.
    >


    Agreed that they can be, but only if they contribute to clarity. In the
    case of the 'sizeof' operator, they've confused people into thinking
    it's a function or a macro. If 'sizeof' had been '_Sizeof' all along,
    then at least it would have a funny look that learners might think twice
    about. As it is, code like:

    x = printf("Hello, world!\n");
    x = sizeof("Hello, world!\n");

    has two lines which look so similar that a lot of novices get 'sizeof'
    and 'strlen' confused. If you can teach something with your code, then
    your code can go beyond functional and readable, towards instructional.
    That's not important to everyone.

    > Another example of the redundant code is the
    > initialization of 'result' in function 'fun'.
    > But this is also a style that I prefer.


    Ok. Do you use special values as a sort of "trap", so that clients can
    report observations and it helps you to debug the problem?

    > Any variable should be initialized at begining.
    > For example, a someone has fixed this code with:
    > if (foo_ptr) {
    > result = &(foo_ptr->bar);
    > }
    > then function is still correctly working.
    > In case the initialization is redundant then
    > an optimizer will remove the code during compilation process.
    > It is very hard to diagnose bugs that depends on the uninitialized
    > variable, especially
    > if it occures only ones per a week and only on client side (taken from
    > real life).


    Maybe that's why you are typing posts involving some of the subtleties
    of C? So that you can fix a compiler that isn't good at warning about
    use of uninitialized objects? I can think of a couple that do a pretty
    good job of it, but obviously that's not universal.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Feb 21, 2013
    #12
  13. Maciej Labanowicz

    Jorgen Grahn Guest

    Re: sizeof of element of struct returned by function

    On Thu, 2013-02-21, Maciej Labanowicz wrote:
    ....
    > Another example of the redundant code is the
    > initialization of 'result' in function 'fun'.
    > But this is also a style that I prefer.
    > Any variable should be initialized at begining.


    Argh! A lot of people seem to do it, but I really dislike that
    practice.

    > For example, a someone has fixed this code with:
    > if (foo_ptr) {
    > result = &(foo_ptr->bar);
    > }
    > then function is still correctly working.


    > In case the initialization is redundant then
    > an optimizer will remove the code during compilation process.
    > It is very hard to diagnose bugs that depends on the uninitialized
    > variable, especially
    > if it occures only ones per a week and only on client side (taken from
    > real life).


    Three things:

    - Like others noted, modern compilers emit warnings about
    uninitialized variables being used. That way you don't have to
    clutter your code with 'int i = 0;', and in a sense squeeze more
    information into your code: when you see 'int i;' but no warning,
    you know[1] i will get a value later, in all relevant code paths.

    'int i=0;' on the other hand may mean that 0 is a true
    initialization value -- or just a dummy.

    - The initializion technique you describe doesn't help avoid bugs;
    it helps hiding bugs by making their symptoms more subtle.

    What actually happens is that person A chooses a 'safe' initial
    value, and person B creates the bug: uses the variable before its
    /real/ initialization has been done. There are no guarantees that A
    made the choice that matches B's assumptions.

    Now you have a bug that neither the compiler nor tools like Valgrind
    can detect: the program doesn't crash, it just produces results
    which don't match the expected ones.

    - All of this is less of a problem in C99, where you can usually
    declare your variable where it's first used, i.e. where you know how
    to initialize it. You should argue for C99 instead.

    /Jorgen

    [1] "Know" is too strong. If you call 'foo(&i)', the compiler cannot
    generally know if foo() initializes i, and has to assume it does.

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Feb 21, 2013
    #13
  14. Re: sizeof of element of struct returned by function

    On 21 Feb, 16:05, Shao Miller <> wrote:

    > Can you explain this policy a bit more?
    > Does the policy explain exactly when and where to add them?

    In general it should be added only for clarification of the code.
    Main requirements are confidential, so I can not share with you.

    > Maybe that's why you are typing posts involving some of the subtleties
    > of C?  So that you can fix a compiler that isn't good at warning about
    > use of uninitialized objects?  I can think of a couple that do a pretty
    > good job of it, but obviously that's not universal.

    I agree that modern compilers are able to detect most of the problems.
    It is great.

    Example of the code that modern compiler
    is silent about uninitialized variable:
    $ gawk '{printf("%02u: %s\n", NR, $0);}' test.c
    01: extern void bar(int *);
    02: void foo(void) {
    03: int x;
    04: bar(&x);
    05: }
    $ gcc --version | head -n1
    gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    $ gcc -W -Wall -ansi -pedantic -Werror -Wextra -Wuninitialized -
    Wmissing-field-initializers -Wjump-misses-init -Winit-self -c test.c
    $

    If there is an -W option to GCC that detects the uninitialized
    variable please give the info.

    I don't want to convince anyone to use that 'style'.
    I know that using it makes the life of programmer is easier in real
    world.

    Regards

    --
    Maciej Labanowicz
    Maciej Labanowicz, Feb 22, 2013
    #14
  15. Re: sizeof of element of struct returned by function

    Maciej Labanowicz <> writes:
    [...]
    > Example of the code that modern compiler
    > is silent about uninitialized variable:
    > $ gawk '{printf("%02u: %s\n", NR, $0);}' test.c
    > 01: extern void bar(int *);
    > 02: void foo(void) {
    > 03: int x;
    > 04: bar(&x);
    > 05: }
    > $ gcc --version | head -n1
    > gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    > $ gcc -W -Wall -ansi -pedantic -Werror -Wextra -Wuninitialized -
    > Wmissing-field-initializers -Wjump-misses-init -Winit-self -c test.c
    > $
    >
    > If there is an -W option to GCC that detects the uninitialized
    > variable please give the info.


    "-O3" helps the compiler find some such issues, because of the
    information collected during optimization, but there's no real
    problem in this case.

    There is no reference to the uninitialized *value* of x. If the
    definition of bar is:

    void bar(int *x) { *x = 42; }

    then the code is perfectly legitimate.

    Of course it's possible that bar could attempt to read the value of *x
    before writing to it, and I don't know a way to make gcc warn about
    that. Cross-function optimization is difficult; optimization across
    translation units is even more so.

    [...]

    --
    Keith Thompson (The_Other_Keith) <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, Feb 22, 2013
    #15
  16. Maciej Labanowicz

    Noob Guest

    Re: sizeof of element of struct returned by function

    Maciej Labanowicz wrote:

    > Example of the code that modern compiler is silent about uninitialized variable:
    > $ gawk '{printf("%02u: %s\n", NR, $0);}' test.c


    For the sake of ZEUS! Please change %02u: to /*%02u:*/

    > 01: extern void bar(int *);
    > 02: void foo(void) {
    > 03: int x;
    > 04: bar(&x);
    > 05: }
    >
    > If there is an option to GCC that detects the uninitialized
    > variable please give the info.


    NB: There is no error, at this point.
    x must be initialized ONLY IF 'bar' expects it to be...

    $ cat f1.c
    extern void bar(int *p);
    int main(void) { int x; bar(&x); return x; }
    $ cat f2.c
    void bar(int *p) { *p += 42; }
    $ gcc -Wall -Wextra -O2 -flto f1.c f2.c
    In file included from :0:0:
    f1.c: In function ‘main’:
    f2.c:1:23: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
    f1.c:2:22: note: ‘x’ was declared here

    http://gcc.gnu.org/wiki/LinkTimeOptimization

    > --
    > Maciej Labanowicz


    NB: the "standard" signature delimiter is DASH DASH SPACE NEWLINE
    Noob, Feb 22, 2013
    #16
  17. Re: sizeof of element of struct returned by function

    On 22 Feb, 17:46, Noob <r...@127.0.0.1> wrote:
    > $ gcc -Wall -Wextra -O2 -flto f1.c f2.c


    Nice to hear about that option.

    Full example (more complicated):
    --------------------------------
    $ cat main.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <string.h>
    typedef struct {
    int * p;
    } strct;
    void bar(strct * s) {
    strct copy;
    memcpy(&copy, s, sizeof (strct));
    if (*copy.p < 1) {
    *copy.p = 200;
    }
    else {
    *copy.p = rand() % 200;
    }
    }
    int foo(void) {
    int x;
    strct s;
    s.p = &x;
    bar(&s);
    return (x > 50) && (x < 100);
    }
    int fill(void) {
    int x = 10;
    strct s;
    s.p = &x;
    bar(&s);
    return 0;
    }
    int main(void) {
    srand((unsigned)time(NULL));
    fill();
    if (foo()) {
    printf("OK, HDD format has started.\n");
    }
    return EXIT_SUCCESS;
    }
    $ gcc -Wall -Wextra -O2 -flto main.c -o a.out
    $ ./a.out
    $
    --------------------------------

    I'm lucky guy, my HDD has been not formatted :).

    Regards

    --
    Maciej Labanowicz
    "Project-Manager: (9 women) == (1 baby per month)"
    Maciej Labanowicz, Feb 22, 2013
    #17
  18. Maciej Labanowicz

    Noob Guest

    Re: sizeof of element of struct returned by function

    Maciej Labanowicz wrote:

    > "Project-Manager: (9 women) == (1 baby per month)"


    WTF is that supposed to mean?
    Noob, Feb 22, 2013
    #18
  19. Re: sizeof of element of struct returned by function

    Noob <root@localhost> writes:

    > Maciej Labanowicz wrote:
    >
    > > "Project-Manager: (9 women) == (1 baby per month)"

    >
    > WTF is that supposed to mean?


    If 2 mean can dig one ditch in a week, how many men does it take
    to dig the same ditch in an hour?

    That question is really about the same, ie. applying purely
    mathematical models to real-world problems, where practical
    constraints must be observed. Google "Chinese horde debugging" and/or
    read "The mythical man-month" for further enlightenment.

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
    Anders Wegge Keller, Feb 22, 2013
    #19
    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. Derek
    Replies:
    7
    Views:
    24,304
    Ron Natalie
    Oct 14, 2004
  2. Trevor

    sizeof(str) or sizeof(str) - 1 ?

    Trevor, Apr 3, 2004, in forum: C Programming
    Replies:
    9
    Views:
    615
    CBFalconer
    Apr 10, 2004
  3. Chris Fogelklou
    Replies:
    36
    Views:
    1,356
    Chris Fogelklou
    Apr 20, 2004
  4. Mark A. Odell

    Obtain sizeof struct element using offsetof()?

    Mark A. Odell, Sep 27, 2004, in forum: C Programming
    Replies:
    10
    Views:
    2,152
    Peter Shaggy Haywood
    Oct 1, 2004
  5. Vinu
    Replies:
    13
    Views:
    1,386
    Lawrence Kirby
    May 12, 2005
Loading...

Share This Page