Sign in to follow this  

Literal allocation when used as arguments

This topic is 4111 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm mainly concerned about C++ here, but I'm interested in how this works in other languages as well. This is probably something not oft thought about, but how are literals allocated when used as arguments to a function? Specifically I'm wondering about when literals are passed to a function that takes value arguments. Say for example you have the following:
func1()
{ func2("blah");
}
where func2 takes a string argument by value. Is the string "blah" allocated and initialized in func1's scope and then passed as a normal value argument to func2, resulting in the string being allocated an initialized twice, or is the literal used only to initialize the argument variable in func2's space? Basically I'm curious if something like:
func1()
{ for(int i=1; i<100000; i++)
  { func2("blah");
  }
}
would be grossly less efficient than:
func1()
{ string s = "blah"
  for(int i=1; i<100000; i++)
  { func2(s);
  }
}
Either way we're creating 100000 copies of the string for the value argument, but would we also be creating an additional 100000 in func1's space in the first example?

Share this post


Link to post
Share on other sites
"blah" is a pointer to an array of characters, { 'b', 'l', 'a', 'h', '\0' }, which is stored independently of its usage. Therefore, the literal is in fact a simple pointer to an array allocated at program initialization and deallocated at program shutdown.

Another thing entirely is the operation of creating an std::string from the string literal. If you pass a string literal to a function 10000 times, the conversion from literal to std::string will occur 10000 times as well. It is preferable to create a string constant in a correct scope, especially since code cluttered with literals is bad form.

In other languages where there is not a std::string versus char* duality as in C++, literals are automatically allocated as string constants when the program is loaded in memory (they are, in fact, already present as-is in program code).

Share this post


Link to post
Share on other sites
Perhaps a string was a bad choice for my example (and I've been working in Java a lot and my concept of C++ strings is skewed at the moment).

Everywhere I have "blah" let's just replace that with an integer literal. So func2 takes an integer argument by value and I'm calling it like: func2(5);

So would "5" be allocated and initialized in func1 and then "copied" for the value argument variable in func2 or will the literal only be allocated and initialized as the argument variable in func2?

One the reasons I asked is actually in reference to constants. I was under the (perhaps erroneous) impression that constants are just a signal to the pre-compiler to replace the constant variable with it's literal.

Share this post


Link to post
Share on other sites
The kind of "potential" micro-optimizations you're considering are unavailable. Assembly has registers and memory addresses, not fancy variables. There's multiple ways of doing the same thing too. The bytes that directly represent the literal (or represent assembly instructions which can reconstruct said literals) are only going to be stored once in either scenario (except prehaps in a completely interpreted language).

To approach this kind of micro-optimization on any meaningful level you will need knowledge of assembly - in which case you can look at the disassembly for whatever example source tidbits you want to compare to see whatever little quirks differing syntax may have on your optimizer's output.

Far more useful on the level you're trying to approach it from - that is, from within the higher level language - is mainly going to be:

1) algorithmic (can we halve the number of times we call func2? Or better yet, only do log(N) func2 calls instead of N?)
2) slashing constants
* removing excess copies of expensive-to-copy types (e.g. cloning containers)
* removing excess copies of large (expensive-to-copy) types (e.g. passing a 120 byte triangle data structure by reference rather than by value)
(4 bytes does not count as large, since 0 fewer bytes are passed if you just use a 4 byte reference instead and extra work will have to be done to actually get at the underlying 4 bytes refered to by the 4 byte reference)
* living with approximations (or other suboptimal results)
* living with lookup tables
* disabling features with bookkeeping (std::ios_base::sync_with_stdio(false)) anyone?
3) caching results
4) algorithmic again (see #1)
5) ???
6) profit

Share this post


Link to post
Share on other sites

This topic is 4111 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this