alloca() is hard...


  • Banned

    @greybeard said in alloca() is hard...:

    @wharrgarbl It's an issue of scope.

    If you assign the return value to a temp variable, that temp variable has scope until the end of its block. Without a temp, the return value goes out of scope and can be deallocated before the call into somefunction.

    But it's supposed to live at least until the end of statement, amirite?


  • Garbage Person

    @gąska

    Not wanting to pay money to ISO, I'll refer to N1256:

    6.5.2.2(5)

    If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.

    6.5.2.2(10)

    The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

    So no, you are not right. Enjoy your nasal daemons.



  • @greybeard said in alloca() is hard...:

    nasal daemons

    It'd be easier if we just defined the entire language as undefined behavior. That way, you wouldn't even need a specialized C compiler! Absolutely any program would be a valid compiler for the language!



  • @ben_lubar So :doing_it_wrong: was really working on a compiler all this time?


  • Banned

    @greybeard said in alloca() is hard...:

    @gąska

    Not wanting to pay money to ISO, I'll refer to N1256:

    6.5.2.2(5)

    If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.

    6.5.2.2(10)

    The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

    So no, you are not right. Enjoy your nasal daemons.

    Yeah, forgot it only applies to const references. No, wait, I'm pretty sure the const reference thing extends temporary to reference's scope, and end of statement clause was something else. *rereads first 900 pages of N3376*


  • Winner of the 2016 Presidential Election

    @greybeard said in alloca() is hard...:

    If an attempt is made to […] access [the result of a function call] after the next sequence point, the behavior is undefined.

    :wtf:



  • @greybeard TIL.

    They may have fixed that for C11 with N1285 (don't know if that made it into the standard). C++ has been more saneless insane with regard to this for some time.



  • @lb_ said in alloca() is hard...:

    @pleegwat at least on Windows, if you look in the headers it's just macros that do simple pointer arithmetic. I shudder to think what other platforms might require that isn't that...

    FTFY, because pointer arithmetic is never simply "simple". 🍹

    Also:

    ...Windows...headers...macros...pointer arithmetic

    A list of things that a lot of people say are :doing_it_wrong:.



  • @gąska said in alloca() is hard...:

    @greybeard said in alloca() is hard...:

    @wharrgarbl It's an issue of scope.

    If you assign the return value to a temp variable, that temp variable has scope until the end of its block. Without a temp, the return value goes out of scope and can be deallocated before the call into somefunction.

    But it's supposed to live at least until the end of statement, amirite?

    You mean like this?

    int* getNumber(void)
    {
        int number = 4;
        return &number;
    }
    void callStuff(void)
    {
        int value = *(getNumber());
    }
    

    callStuff invokes UB, because something could interrupt the code between the call to getNumber() and the dereference operation and change the value in the location formerly known as number in getNumber().


  • Discourse touched me in a no-no place

    @wharrgarbl said in alloca() is hard...:

    Yes, I did research and tried a few variations on that thing.

    So that was actually "no" then. Thought as much...

    https://linux.die.net/man/3/va_start

    it may be necessary for va_start() to allocate memory, store the arguments there, and also an indication of which argument is next, so that va_arg() can step through the list. Now va_end() can free the allocated memory again.

    @wharrgarbl said in alloca() is hard...:

    this thing wants a pointer to my va_list when I call vsprintf, or wtfdevice just resets.

    Weird - I'd expect invoking undefined behaviour to cause undefined behaviour. Strange that...

    Unless you need to "open" stdin, then you use fopen

    Since you already have a handle to stdin (STDIN_FILENO) when your program starts, why would you want to open() it? If you want another handle you should be using dup()


    @djls45 said in alloca() is hard...:

    @gąska said in alloca() is hard...:

    But it's supposed to live at least until the end of statement, amirite?

    You mean like this?

    int* getNumber(void)
    {
        int number = 4;
        return &number;
    }
    void callStuff(void)
    {
        int value = *(getNumber());
    }
    

    callStuff invokes UB, because something could interrupt the code between the call to getNumber() and the dereference operation and change the value in the location formerly known as number in getNumber().

    You're invoking UB well before then. getNumber() is returning a reference pointer to something that won't exist after the function returns.



  • @pjh said in alloca() is hard...:

    @djls45 said in alloca() is hard...:

    @gąska said in alloca() is hard...:

    But it's supposed to live at least until the end of statement, amirite?

    You mean like this?

    int* getNumber(void)
    {
        int number = 4;
        return &number;
    }
    void callStuff(void)
    {
        int value = *(getNumber());
    }
    

    callStuff invokes UB, because something could interrupt the code between the call to getNumber() and the dereference operation and change the value in the location formerly known as number in getNumber().

    You're invoking UB well before then. getNumber() is returning a reference to something that won't exist after the function returns.

    It isn't UB at that point, though. It's returning a pointer, not a reference. The UB comes in when an attempt is made to dereference that (invalid) pointer.


  • Discourse touched me in a no-no place

    @djls45 said in alloca() is hard...:

    It isn't UB at that point, though. It's returning a pointer, not a reference.

    Braino on my part - I've corrected the OP. My point, though, was that...

    The UB comes in when an attempt is made to dereference that (invalid) pointer.

    and not

    because something could interrupt the code between the call to getNumber() and the dereference operation


  • BINNED

    @tsaukpaetra said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @tsaukpaetra said in alloca() is hard...:

    alpha channel cute bytes

    ❓

    I have no idea where "cute" came from.... 🤷♂

    Stop eyeing coworkers while typing posts!


  • Discourse touched me in a no-no place

    @dreikin said in alloca() is hard...:

    Perhaps I should, but in this case my (limited) understanding of C is that the result of the original call

    somefunction(functionthatreturnastruct().somechararray);
    

    is the same as:

    1. call functionthatreturnastruct()
    2. acquire a reference to the somechararray member of the result from the previous, which being an array is a pointer
    3. pass that pointer into somefunction

    Wait, no, I see the problem you're talking about now. It's not between 2 & 3, it's between 1 & 2, isn't it?

    When returning a struct (that doesn't fit in the accumulator), it's located “somewhere”. It might be located on the stack in a place that is now not safely allocated (so a function call or interrupt will smash it), or it might be located somewhere else, in some global or (hopefully!) thread-local storage. It's a messy hack, and the recommended thing is for the caller to allocate the storage (e.g., using one of its local variables) and pass in the location instead precisely because that's known not to have any weird cases associated with it.

    Now, in the case in question, the array probably is a member of the struct, and that makes all the difference. This means that when you pass the char array to a function, you are pointing into the struct. However, if you instead assign the struct to a variable first, that does a copy (probably looks just like a memcpy under the hood) from the returned struct to the local temporary — this is return-by-value — and that means passing a pointer to the char array will point into the safely-on-the-allocated-stack struct. IOW, if you have a function that directly returns a struct, the only safe things to do are to save it to a local variable or to read a field directly but not take its address, even implicitly. So copy it already, OK?

    The rules are different for inlined functions, as the compiler can trace what's actually going on with the value there. But looks-like-copying is still safe; the compiler can disentangle it for you and do something smarter as required.

    (I never return structs from non-inlined functions. The semantics of what happens under the covers is too damn scary for me…)



  • @pjh said in alloca() is hard...:

    @wharrgarbl said in alloca() is hard...:

    Yes, I did research and tried a few variations on that thing.

    So that was actually "no" then. Thought as much...

    Yes mean yes, what kind of covfefe you're drinking?

    @pjh said in alloca() is hard...:

    Since you already have a handle to stdin (STDIN_FILENO) when your program starts

    No, with wtfdevice stdin is NULL when your program starts.


  • Discourse touched me in a no-no place

    @wharrgarbl said in alloca() is hard...:

    No, with wtfdevice stdin is NULL when your program starts.

    Of course it is. 🤦



  • @dkf Is wtfdevice officially so wtf that people aren't believing me?


  • Discourse touched me in a no-no place

    @wharrgarbl I'm unfortunately believing you. I don't want to believe that stuff can be that bust, but I'm cynical enough that it doesn't surprise.



  • @wharrgarbl said in alloca() is hard...:

    @dkf Is wtfdevice officially so wtf that people aren't believing me?

    What? That there's no stdin/stdout/stderr on wtfdevice? Isn't that on par for microcontrollers? Why would you bother wasting precious memory (RAM|ROM) on that stuff when your finished product can't in any way ever output text to any kind of console ever? I don't really do embedded stuff, and yet I've seen a lot of environments where there's no libc support, period. Or shoddy libc support. (Or shoddy "compiler" support, for that matter.)


  • Discourse touched me in a no-no place

    @cvi said in alloca() is hard...:

    I don't really do embedded stuff, and yet I've seen a lot of environments where there's no libc support, period. Or shoddy libc support.

    You can expect to have pretty much no libc at all; the devices are often so small that there's simply no space for that code.



  • @cvi said in alloca() is hard...:

    What? That there's no stdin/stdout/stderr on wtfdevice?

    There is a stdin that represents the keyboard. But it starts closed, and must be closed at certain system calls, because fuck you, that's why.

    @dkf said in alloca() is hard...:

    You can expect to have pretty much no libc at all; the devices are often so small that there's simply no space for that code.

    wtfdevice has plenty of space to host a complete OS if they wanted, but they wanted to improve security, or compatibility with old wtfstuff or whatever



  • @wharrgarbl said in alloca() is hard...:

    wtfdevice has plenty of space to host a complete OS if they wanted, but they wanted to improve security, or compatibility with old wtfstuff or whatever

    Oh, OK. :wtf:

    Carry on then.

    @wharrgarbl said in alloca() is hard...:

    and must be closed at certain system calls

    Missed that part in my first read. Have another :wtf: for that.



  • @pjh said in alloca() is hard...:

    @djls45 said in alloca() is hard...:

    It isn't UB at that point, though. It's returning a pointer, not a reference.

    Braino on my part - I've corrected the OP. My point, though, was that...

    The UB comes in when an attempt is made to dereference that (invalid) pointer.

    and not

    because something could interrupt the code between the call to getNumber() and the dereference operation

    Agreed, but I think his original point was that unless an interrupt fires, the memory location referenced by the pointer will likely still contain what getNumber put into it... so the code may appear to work, most of the time.

    Although, since it'd be stored on the stack (in the free memory near the top of it), the very next function call will probably trash it (depending on how many arguments the respective functions had; getNumber took no arguments, so it didn't give you much buffer space). So

    int value = *(getNumber());
    doSomething(&value);
    

    will probably work most of the time, but

    doSomething(getNumber());
    

    will almost certainly not work. Calling doSomething will smash the value that getNumber stored there.



  • @dkf said in alloca() is hard...:

    Small structs (i.e., things that will pack into a register) can be safely returned, but they tend to be unusual data structures in real programs (and the bit-stuffing required to work with them gets tedious and irritatingly expensive in inner loops too).

    That's BS.

    Any compiler that produces incorrect code when large arguments are returned is buggy. You can return arbitrarily large structures, and the compiler must hide the horrible details from the programmer and behave exactly the same as teeny tiny (only maybe with worse performance).


  • Garbage Person

    @gąska said in alloca() is hard...:

    rereads first 900 pages of N3376

    You made me trawl through N1256. Serves you right.



  • @dkf said in alloca() is hard...:

    It might be located on the stack in a place that is now not safely allocated

    This is outright wrong, and any compiler that does this is buggy.

    f(function_returning_a_struct());

    is perfectly well defined behaviour in C. It's kind of non-idiomatic, but its perfectly well defined.



  • @cvi I guess they control the ownership of the keyboard this way, because any proccess running can draw to the screen at anytime, and no concept of focus in the base system.

    The problem was certainly what @Greybeard said. When the second function is called with a member of the struct as a parameter, it gets out of scope and gets overwritten.

    That's unintuitive, but in the end we have to always use a temp variable.


  • Notification Spam Recipient

    @onyx said in alloca() is hard...:

    @tsaukpaetra said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @tsaukpaetra said in alloca() is hard...:

    alpha channel cute bytes

    ❓

    I have no idea where "cute" came from.... 🤷♂

    Stop eyeing coworkers while typing posts!

    I'm surrounded by males, only one of which I might describe as "cute"...



  • @wharrgarbl said in alloca() is hard...:

    control the ownership of the keyboard this way, because any proccess running can draw to the screen at anytime

    First I wanted to post that these two (ownership of the keyboard and being able to draw to the screen) aren't related, so :wtf:. Then I realized what you probably were saying, and it made me recoil in horror.

    Yeah, that sounds fairly terrible. I'd say that my thoughts are with you, but, honestly, I'd rather forget about having ever heard of such a system.



  • @djls45 said in alloca() is hard...:

    callStuff invokes UB

    Ummm. You're fucked before that. I'm pretty sure (without reading the standard) that returning the address of a local variable is UB.
    edit: :hanzo:'d


  • Winner of the 2016 Presidential Election

    @dkf said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    Perhaps I should, but in this case my (limited) understanding of C is that the result of the original call

    somefunction(functionthatreturnastruct().somechararray);
    

    is the same as:

    1. call functionthatreturnastruct()
    2. acquire a reference to the somechararray member of the result from the previous, which being an array is a pointer
    3. pass that pointer into somefunction

    Wait, no, I see the problem you're talking about now. It's not between 2 & 3, it's between 1 & 2, isn't it?

    When returning a struct (that doesn't fit in the accumulator), it's located “somewhere”. It might be located on the stack in a place that is now not safely allocated (so a function call or interrupt will smash it), or it might be located somewhere else, in some global or (hopefully!) thread-local storage. It's a messy hack, and the recommended thing is for the caller to allocate the storage (e.g., using one of its local variables) and pass in the location instead precisely because that's known not to have any weird cases associated with it.

    Now, in the case in question, the array probably is a member of the struct, and that makes all the difference. This means that when you pass the char array to a function, you are pointing into the struct. However, if you instead assign the struct to a variable first, that does a copy (probably looks just like a memcpy under the hood) from the returned struct to the local temporary — this is return-by-value — and that means passing a pointer to the char array will point into the safely-on-the-allocated-stack struct. IOW, if you have a function that directly returns a struct, the only safe things to do are to save it to a local variable or to read a field directly but not take its address, even implicitly. So copy it already, OK?

    The rules are different for inlined functions, as the compiler can trace what's actually going on with the value there. But looks-like-copying is still safe; the compiler can disentangle it for you and do something smarter as required.

    (I never return structs from non-inlined functions. The semantics of what happens under the covers is too damn scary for me…)

    TIL C is a little bit more finicky than I thought.



  • @anotherusername said in alloca() is hard...:

    the memory location referenced by the pointer will likely still contain what getNumber put into it..

    And that is the UB you're now relying on. (I can't remember, but I think when running debug mode in VS, it will trounce on that memory - on porpoise.)


  • Winner of the 2016 Presidential Election

    @gwowen said in alloca() is hard...:

    @dkf said in alloca() is hard...:

    It might be located on the stack in a place that is now not safely allocated

    This is outright wrong, and any compiler that does this is buggy.

    f(function_returning_a_struct());

    is perfectly well defined behaviour in C. It's kind of non-idiomatic, but its perfectly well defined.

    That's what I thought, but @dkf et alia seem to be indicating that it's just not so, that C won't create the temp copy for you. Can you show something in the standard or similar documents saying otherwise?



  • @dreikin said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    @dkf said in alloca() is hard...:

    It might be located on the stack in a place that is now not safely allocated

    This is outright wrong, and any compiler that does this is buggy.

    f(function_returning_a_struct());

    is perfectly well defined behaviour in C. It's kind of non-idiomatic, but its perfectly well defined.

    That's what I thought, but @dkf et alia seem to be indicating that it's just not so, that C won't create the temp copy for you. Can you show something in the standard or similar documents saying otherwise?

    @Greybeard mentioned the relevant parts of N1256. Basically, it does not specify the order of evaluation for the function name itself (which could be a function pointer return value or calculation), the arguments sent as parameters (actually the allocation of memory locations for them, probably), and subexpressions used to calculate the parameter values. But it does indicate (1) that a "sequence point" is created before actually calling the function (so all of these do get evaluated) and (2) attempting to use the result of a function call after the next sequence point (in this case, the one before calling the function) is UB.



  • @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.


  • Discourse touched me in a no-no place

    @dreikin said in alloca() is hard...:

    That's what I thought, but @dkf et alia seem to be indicating that it's just not so, that C won't create the temp copy for you.

    Well, creating a temp copy might be very expensive if the structure is large enough, so C doesn't do it for you; if you want a copy, you need to do it yourself. (Heck, that's pretty much the motto for C: “if you want it done right, do it yourself”.) The tricky part is that there's multiple ways it could be done; it actually depends on the architecture. For anything that's actually expensive, you're much better off manually getting the outermost function that cares to allocate the space on the stack and to pass pointers to that struct into everywhere else that needs access; that works and is efficient. OTOH, if the inner functions are inline then you can do anything you want as the compiler doesn't need to follow some irritating ABI rule (or even actually assemble the struct at all in some cases).


  • Winner of the 2016 Presidential Election

    @wharrgarbl said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.

    Eh? Just when I think I've got it...

    So the function's returned struct is preserved and passed only as long as you don't try to use a member? But that seems to conflict with what I undersyood @dkf, @djls45, & @Greybeard to be saying.



  • @dreikin problem is that an array is passed as a pointer, and the struct gets out of scope when the outer function is called. If it was an int member it would be ok.


  • Java Dev

    @dreikin The returned value is only in scope for a very short time. At that point you can either save the value, or copy it (or one of its members, if it's a struct). In case of a normal variable you can't even take the address (not an lvalue), in case of a struct with an array member you can implicitly take its address but it's still UB to use it after the return value goes out of scope.


  • Winner of the 2016 Presidential Election

    @pleegwat said in alloca() is hard...:

    @dreikin The returned value is only in scope for a very short time. At that point you can either save the value, or copy it (or one of its members, if it's a struct). In case of a normal variable you can't even take the address (not an lvalue), in case of a struct with an array member you can implicitly take its address but it's still UB to use it after the return value goes out of scope.

    Okay, that sounds like what I thought I got. So @wharrgarbl's example

    f(function_returning_a_struct());
    

    is UB?



  • @dreikin said in alloca() is hard...:

    @wharrgarbl said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.

    Eh? Just when I think I've got it...

    So the function's returned struct is preserved and passed only as long as you don't try to use a member? But that seems to conflict with what I understood @dkf, @djls45, & @Greybeard to be saying.

    Yes, pretty much. If the parameter is directly filled by another function's return value, then that value cannot be used between that function's returning and the call to the outer function.
    The sequence of events appears to be

    (determine function to call, allocate memory space for parameters, determine value of parameters) in some order -> ("sequence point") -> call function

    and if part of determine value of parameters includes a direct result of a function call, then any attempt to further use that function's return value (including getting a sub-member of a struct) invalidates the parameter.


  • Java Dev

    @dreikin said in alloca() is hard...:

    @pleegwat said in alloca() is hard...:

    @dreikin The returned value is only in scope for a very short time. At that point you can either save the value, or copy it (or one of its members, if it's a struct). In case of a normal variable you can't even take the address (not an lvalue), in case of a struct with an array member you can implicitly take its address but it's still UB to use it after the return value goes out of scope.

    Okay, that sounds like what I thought I got. So @wharrgarbl's example

    f(function_returning_a_struct());
    

    is UB?

    No, because this copies the full return value into the arguments of f(). While f(function_returning_a_struct().array_member) copies a pointer to part of the return value. And f(&(function_returning_simple_value())) does not compile.


  • Java Dev

    @djls45 said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @wharrgarbl said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.

    Eh? Just when I think I've got it...

    So the function's returned struct is preserved and passed only as long as you don't try to use a member? But that seems to conflict with what I understood @dkf, @djls45, & @Greybeard to be saying.

    Yes, pretty much. If the parameter is directly filled by another function's return value, then that value cannot be used between that function's returning and the call to the outer function.
    The sequence of events appears to be

    (determine function to call, allocate memory space for parameters, determine value of parameters) in some order -> ("sequence point") -> call function

    and if part of determine value of parameters includes a direct result of a function call, then any attempt to further use that function's return value (including getting a sub-member of a struct) invalidates the parameter.

    Close, but not quite. Using the value itself is OK, including taking struct members. However if that struct member is an array variable, then taking member (without indexing the array) is an implicit address-of operation. You can copy the address into the arguments of the second function, but the memory that address points to becomes invalid at the sequence point.


  • Winner of the 2016 Presidential Election

    @djls45 said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @wharrgarbl said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.

    Eh? Just when I think I've got it...

    So the function's returned struct is preserved and passed only as long as you don't try to use a member? But that seems to conflict with what I understood @dkf, @djls45, & @Greybeard to be saying.

    Yes, pretty much. If the parameter is directly filled by another function's return value, then that value cannot be used between that function's returning and the call to the outer function.
    The sequence of events appears to be

    (determine function to call, allocate memory space for parameters, determine value of parameters) in some order -> ("sequence point") -> call function

    and if part of determine value of parameters includes a direct result of a function call, then any attempt to further use that function's return value (including getting a sub-member of a struct) invalidates the parameter.

    @pleegwat said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @pleegwat said in alloca() is hard...:

    @dreikin The returned value is only in scope for a very short time. At that point you can either save the value, or copy it (or one of its members, if it's a struct). In case of a normal variable you can't even take the address (not an lvalue), in case of a struct with an array member you can implicitly take its address but it's still UB to use it after the return value goes out of scope.

    Okay, that sounds like what I thought I got. So @wharrgarbl's example

    f(function_returning_a_struct());
    

    is UB?

    No, because this copies the full return value into the arguments of f(). While f(function_returning_a_struct().array_member) copies a pointer to part of the return value. And f(&(function_returning_simple_value())) does not compile.

    @pleegwat said in alloca() is hard...:

    @djls45 said in alloca() is hard...:

    @dreikin said in alloca() is hard...:

    @wharrgarbl said in alloca() is hard...:

    @gwowen said in alloca() is hard...:

    f(function_returning_a_struct());
    is perfectly well defined behaviour in C.

    But

    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.

    Eh? Just when I think I've got it...

    So the function's returned struct is preserved and passed only as long as you don't try to use a member? But that seems to conflict with what I understood @dkf, @djls45, & @Greybeard to be saying.

    Yes, pretty much. If the parameter is directly filled by another function's return value, then that value cannot be used between that function's returning and the call to the outer function.
    The sequence of events appears to be

    (determine function to call, allocate memory space for parameters, determine value of parameters) in some order -> ("sequence point") -> call function

    and if part of determine value of parameters includes a direct result of a function call, then any attempt to further use that function's return value (including getting a sub-member of a struct) invalidates the parameter.

    Close, but not quite. Using the value itself is OK, including taking struct members. However if that struct member is an array variable, then taking member (without indexing the array) is an implicit address-of operation. You can copy the address into the arguments of the second function, but the memory that address points to becomes invalid at the sequence point.

    Okay, so the distinction is that it will copy something, but in the case of an array that something is a pointer, which in this case is of course UB to use in the outer function because it's gone out of stack scope. But non-pointer things like structs or ints are copied as expected. IOW, the problem is trying to use a ref-like struct member because that only copies the reference, not the member itself.

    Right?



  • @dreikin that's it


  • Garbage Person

    @cvi said in alloca() is hard...:

    C++ has been more saneless insane with regard to this for some time.

    Not in my experience. We had a bunch of

    somemethod(someobject.methodreturningstring.c_str())
    

    we had to fix.





  • @greybeard That should work in C++ (regardless of whether methodreturningstring is a member variable or actually a call to a member function). The returned string is destroyed first when the full expression has been evaluated (i.e., after the call to somemethod() returns).



  • @wharrgarbl said in alloca() is hard...:

    mine f() receive a pointer to a member of the struct, that is destroyed when f() is called.

    struct foo {
    int member;
    };
    struct foo struct_func();

    f1(int arg);
    f2(int* arg);

    f1(struct_func().member); // This is fine
    f2(&struct_func().member); // Bad bad bad

    Is this what you're saying? If so, you're absolutely right, and I missed where you were passing the pointer to the member, rather than the member, so sorry.



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


Log in to reply