Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   [TinyCC] sizeof of element of struct returned by function (http://www.velocityreviews.com/forums/t957829-tinycc-sizeof-of-element-of-struct-returned-by-function.html)

Maciej Labanowicz 02-20-2013 11:54 AM

[TinyCC] sizeof of element of struct returned by function
 
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

Noob 02-20-2013 12:18 PM

Re: [TinyCC] sizeof of element of struct returned by function
 
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.


James Kuyper 02-20-2013 01:18 PM

Re: [TinyCC] sizeof of element of struct returned by function
 
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

Maciej Labanowicz 02-20-2013 02:23 PM

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

Shao Miller 02-20-2013 07:10 PM

Re: [TinyCC] sizeof of element of struct returned by function
 
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

Barry Schwarz 02-21-2013 02:27 AM

Re: sizeof of element of struct returned by function
 
On Wed, 20 Feb 2013 06:23:44 -0800 (PST), Maciej Labanowicz
<m.labanowicz@gmail.com> 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

Shao Miller 02-21-2013 02:50 AM

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
> <m.labanowicz@gmail.com> 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

James Kuyper 02-21-2013 02:55 AM

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
> <m.labanowicz@gmail.com> 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

Shao Miller 02-21-2013 03:02 AM

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
>> <m.labanowicz@gmail.com> 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

Maciej Labanowicz 02-21-2013 11:41 AM

Re: sizeof of element of struct returned by function
 
On 20 Feb, 20:10, Shao Miller <sha0.mil...@gmail.com> 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


All times are GMT. The time now is 08:18 PM.

Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57