alloca() is hard...
-
@gwowen That's bad, but the nasty here is that the struct is actually:
struct foo { int member[16]; }
and hence the
&
is implicit.
-
@greybeard said in alloca() is hard...:
someobject.methodreturningstring.c_str()
The problem there is that the temporary variable is just the pointer, not the thing pointed to. So only the pointer variable - not the pointed to char's - gets their life extended.
-
@dkf said in alloca() is hard...:
and hence the & is implicit.
Ugh. That is nasty.
I hate hate hate hate hate that array-args-turn-into-pointers rule of C. Now I have a new reason to. It's the worst. (Well, OK in C there are a lot of good candidates for "worst", but its up there).
-
@gwowen said in alloca() is hard...:
It's the worst.
But is it the @mikeTheLiar?
C is unashamedly a language that sits just above machine code. As such, it is quite annoying to use in places and you need to be very aware of what is going on. (Of course, you can go even lower level than C, but the annoyance level goes up fast. C is a hell of a lot nicer than Assembly.) I'd definitely encourage people to use higher level languages than C where possible, but “where possible” is an important caveat.
-
@dkf said in alloca() is hard...:
C is unashamedly a language that sits just above machine code
I like C. I use it nearly every day. If I didn't find it useful, I wouldn't loathe and detest the bits of it that are annoying, like the stupid rule about pointer decay. I understand why it exists as an historical anomaly (no-one wanted to pass large arrays by value back in the day for efficiency reasons), but it really puts arrays outside the normal type system.
Case in point:typedef char[16] my_array_t;
int main()
{
my_array_t var;
printf("%u\n",(unsigned) sizeof(my_array_t)); // prints 16
printf("%u\n",(unsigned) sizeof(var)); // prints 16
}void fun(my_array_t arg)
{
printf("%u\n",(unsigned) sizeof(my_array_t)); // prints 16
printf("%u\n",(unsigned) sizeof(arg)); // prints size of a pointer
}I'd have like to have seen it slowly deprecated in C99 and removed in C11, giving you 12 years to go and put all those '&' into your codebase.
-
Decided to do some actual tests of the matter under discussion. gcc 4.8.5.
test.c:
struct foo { int member; int array_member[10]; }; struct foo struct_func(); extern f1(int arg); extern f2(int* arg); int main() { f1(struct_func().member); // This is fine f2(&struct_func().member); // Bad bad bad f2(struct_func().array_member); }
make test.o:
cc -c -o test.o test.c test.c: In function ‘main’: test.c:12:8: error: lvalue required as unary ‘&’ operand f2(&struct_func().member); // Bad bad bad ^ test.c:13:5: error: invalid use of non-lvalue array f2(struct_func().array_member); // Also bad ^ make: *** [test.o] Error 1
Both problematic invocations actually fail to compile with reasonably obvious error messages. Of course, embedded compilers are notorious for having old versions and worse behaviour than modern x86 compilers.
-
@gwowen said in alloca() is hard...:
and I missed where you were passing the pointer to the member, rather than the member, so sorry.
It was because the member was an array, so C automatically turns it into a pointer
-
@wharrgarbl said in alloca() is hard...:
f(function_returning_a_struct().arraymember)
Is not. In your example the entire struct is copied to f() parameter, and in mine f() receive a pointer to a member of the struct, that is destroyed when f() is called.I don't see any pointers, to either structs or members of structs, or anything that's destroyed in your f().
@pleegwat said in alloca() is hard...:
While f(function_returning_a_struct().array_member) copies a pointer to part of the return value
Nope - still no pointers.
Ok - new example; one that compiles and behaves more or less as expected:
pjh@hpdesktop:/tmp$ cat -n structs.c 1 #include <stdio.h> 2 3 struct foo { 4 int bar; 5 char baz[5]; 6 char qux[10000]; 7 }; 8 9 10 struct foo foo_as_struct(void){ 11 struct foo f = { 42, "asdf" }; 12 return f; 13 } 14 15 struct foo* foo_as_pointer_to_static(void){ 16 static struct foo f = { 43, "qwer" }; 17 return &f; 18 } 19 20 struct foo* foo_as_pointer_to_auto(void){ 21 struct foo f = { 44, "zxcv" }; 22 return &f; 23 } 24 25 int main(void){ 26 printf("%d, %s\n", foo_as_struct().bar, foo_as_struct().baz); 27 printf("%d, %s\n", foo_as_pointer_to_static()->bar, foo_as_pointer_to_static()->baz); 28 printf("%d, %s\n", foo_as_pointer_to_auto()->bar, foo_as_pointer_to_auto()->baz); // UB 29 30 return 0; 31 } pjh@hpdesktop:/tmp$ make -B structs cc structs.c -o structs structs.c: In function ‘foo_as_pointer_to_auto’: structs.c:22:9: warning: function returns address of local variable [-Wreturn-local-addr] return &f; ^~ pjh@hpdesktop:/tmp$ ./structs 42, asdf 43, qwer Segmentation fault (core dumped) pjh@hpdesktop:/tmp$
-
@gwowen said in alloca() is hard...:
@cvi Correct. It also works if somemethod() takes a const reference, due to the C++ rule extending the lifetime of the const references to temporaries.
Pretty sure you're confused about that. First, in that example
somemethod()
takes a pointer to a (const) char (since that's whatc_str()
normally returns). It can also take a const ref to a char-pointer, that doesn't change anything.In C++, the lifetime of the temporary is guaranteed to be for the full expression, so whatever the type of the argument of
somemethod()
is doesn't matter.The lifetime extension of const references comes into play in the following:
struct X { void f(); ... }; X make_X(); //... X const& xref = make_X(); //... xref.f(); // still valid, the const-ref extended the life of the returned temporary.
-
-
@cvi said in alloca() is hard...:
Pretty sure you're confused about that. First, in that example somemethod() takes a pointer to a (const) char (since that's what c_str() normally returns). It can also take a const ref to a char-pointer, that doesn't change anything.
Nah, I just explained myself poorly. Assuming
fun1(const mytype * t);
fun2(const mytype& t);mytype func();
Now I can do
fun1(&func()); // UB, as in C.
fun2(func()); // OK, because const references are magicwhich both do essentially the same thing - because pointers and references are very similar under the hood.
The big difference is when I call fun2() the value returned from func() is guaranteed to still be around (its lifetime is extended because I took a reference to it).
In fun1() the return value will get destroyed, and the only object that lasts till the end of the full expression is the pointer-to-a-destroyed mytype.
-
#include <stdio.h>
struct mytype
{
int arr[16];
};void fun(const int* t)
{}
struct mytype get()
{
struct mytype retval;
return retval;
}int main()
{
fun(get().arr);
}In gcc this doesn't compile - "Invalid use of non-lvalue array". Which is nice.
-
For me the biggest WTF about
alloca()
is Microsoft's "safe" version:_alloca
: This function is deprecated because a more secure version is available; see_malloca
.Nothing too WTF-y so far...
Unlike
_alloca
, which does not require or permit a call to free to free the memory so allocated,_malloca
requires the use of_freea
to free memory. In debug mode,_malloca
always allocates memory from the heap.Congratulations Microsoft, you've missed the point of
alloca()
entirely!
That said, that point was mostly valid in C (since C++ has destructors) and back then Microsoft might have had already taken the decision of leaving C devs to rot.
-
@gwowen said in alloca() is hard...:
In fun1() the return value will get destroyed, and the only object that lasts till the end of the full expression is the pointer-to-a-destroyed mytype.
No, the return value won't get destroyed immediately. See the link on lifetimes I posted earlier. The return value is guaranteed to be alive for the full expression. You can even try it out:
struct X { ~X() { printf( "X::~X\n" ); } }; X make_X() { return X{}; } void don_t_take_x( int ) { printf( "dont()\n" ); } int main() { don_t_take_x( (make_X(), 0) ); return 0; }
The instance of
X
isn't bound to a reference in the above example, it's in fact completely discarded. Yet, the standard guarantees that it isn't destroyed until afterdont_t_take_x()
returns (and thus the full expression is finished).Now I can do
fun1(&func()); // UB, as in C.GCC/Clang will error out at that:
error: taking address of temporary [-fpermissive]
MSVC has a sneaky non-standard extension which makes the above legal (you need /W4 to get a warning about it).You can however do the following (which is also legal because temporary lifetimes last the full expression):
mytype const* take_addr( mytype const& mt ) { return &mt; } fun1( take_addr(func()) );
-
@medinoc Interesting. Thanks.
-
@pjh said in alloca() is hard...:
alloca
Talking about this, I think I've recently seen a kernel bug regarding libraries like glibc uses alloca() to allocate large block of memory (like 64KB) for it's internal functions, and can easily allocate across stack guard page, so they have in increase the guard page size.
-
-
@gwowen psst -- if you put
```
on its own line above and below your code block, it'll format as a code block with syntax highlighting. You can also tell it what language by providing a hint on the top```
-- e.g.```c++
.
-
@cheong said in alloca() is hard...:
@pjh said in alloca() is hard...:
alloca
Talking about this
I'm in the process of turning off _CRT_SECURE_NO_WARNINGS(etc). And fixing other various security things. And removing
_alloca()
because someone didn't want to deal with memory cleanup in C++.
-
@gwowen also, I should've mentioned that you can click next to the voting buttons on a post and click the "View raw" option; that'll show the markdown that the poster used. If you see anything in a post that you're not sure how they did it, you can use it to find out.