is it good or bad to reuse the variables? [closed]
Asked Answered
B

10

18

I wonder if it is good or bad (or does not matter) if I reuse the variable names as much as possible? for example

int main(void){
  //...
  int x=0;

  //..
  x = atoi(char_var);

  //..

  for (x=0; x<12; x++){
   //...
  }

  //..
  x = socket(...)
  if(x<0){
  //...
  }

  for(x=0;x<100;x++{
  //...
  }

  return 0;
}

Another variables could be used instead of x above (might be better for readability), but I wonder if it would provide me any benefit for the binary size, performance, or anything else?

Berkshire answered 26/7, 2013 at 14:41 Comment(9)
for most cases it's bad. each variable should play its own roleHeadreach
@AndreyChernukha What about a mere counter (e.g. i)Willard
@Willard i said for most cases, not for allHeadreach
Using the same variable as a socket and a loop counter surely doesn't improve the readability of your code. Using meaningful variable names is also considered a good practice.Sanfo
possible duplicate of is it acceptable to recycle or reuse variables?Office
@AndreyChernukha I was not critizing your comment but wanted your opinion about it.Willard
My take on this is that you should minimise the scope of an identifier - i.e. the amount of code wherein that identifier is relevant. (This actually goes for any identifier, like function names. Minimising their scope means reducing coupling.) This implies that reusing a loop counter name is fine when the loops are short, and you can easily see where the scope of the counter variable starts and ends. This also implies that if you have a function with several large loops reusing the counter name, this becomes bad again. (Although there are other reasons why you should avoid this.)Pedersen
You should use a variable for what it MEANS. And the variable name should be chosen to reflect a meaning for the value contained therein. A good compiler will recognize the "scope" of a variable and will reuse the space for multiple variables if their scopes don't overlap, so there's no advantage to reusing variables.Scruggs
To put it another way, regardless of the details, NEVER put a value in a variable that implies something different. Just because there's an "apples" variable there and not being used, do not put oranges into it.Scruggs
S
24

In general it's very poor practice to reuse variable names for different purposes - if someone else needs to maintain your code later on the person will have to find these "context switches" in your code where x now suddenly means something other than what it meant before that line of code.

You may get some memory savings but that's so small compared to the problems it introduces that it's advised against. (Read the edit below, too.)

Typically it's also recommended not to use 1-character variable names for other than loop counters. One could argue that x could also be an X coordinate but I'd use some prefix or a longer name in that case. Single-letter variable names are too short to give meaningful hints about the purpose of a variable.

Edit: as several comments (and some of the other answers) pointed out, the potential memory savings (if any) depend on how good the compiler is. Well-written optimizing compilers may realize that two variables have no overlapping lifetimes so they only allocate one variable slot anyway. The end result would be no run-time gain and still less maintainable source code. This just reinforces the argument: don't reuse variables.

Starfish answered 26/7, 2013 at 14:44 Comment(20)
I don't think i for index is that bad.Frumentaceous
"You do get some memory savings" -- not if the compiler is good enough. Most compilers will optimize away a set of local variables that are each used over a different part of the function.Unsaid
@Jim For a loop counter / index it could be ok, although I usually use something like nIndex even for indices. I only ever use single-letter names for loop counters because of the above reasons. This also depends on personal practice to some extent.Starfish
@Unsaid Yes, you're right. I didn't write it that way because the programmer will perceive it as a saved slot. Also with reused variables it's unlikely that the entire variable could be eliminated. One would have write the code very carefully for that which is - in itself - a source of bugs then.Starfish
Do you re-use variable names the are declared in separate loops? Many are getting around this issue with declaring as local to the operation(s) as possible.Frumentaceous
@Jim Yes, I do reuse loop counters and I always declare them locally in the for loop. As cdhowie pointed out, these are recognized by the compiler as non-overlapping variables so only one slot is allocated for them. I may also - rarely - reuse other names but they're never for different purposes. So if an int is allocated as a counter, that name is only used as a counter. In general I avoid doing this, though - I prefer using long, distinct variable names.Starfish
@Unsaid It's not even if the compiler is "good enough". If you've taken a compiler construction course you know that the IR the compiler uses to actually generate code is programming-language agnostic, and has "variables" for every single subexpression. (Every (lvalue, operator, left operand, right operand) tuple.) Doing this sort of optimisation is a fundamental step of how compilers work and you can bet that unless you disable optimisation, every compiler written for production use will do the right thing here - since they need to save the stack space used by all the code.Pedersen
@Starfish I'd recommend just nuking the "you save memory" sentence since it's patently wrong.Pedersen
@Pedersen I'll add a note but I disagree with your comment. It's easy to construct realistic examples where reusing a variable would end up saving a memory slot. Your comment assumes optimizing compilers (true for C) but the issue is more generic in my opinion.Starfish
@Starfish My comment assumes any compiler written after the Dragon Book was written, and patterned after it. As in, a compiler that does NOT optimize this away would have to written by a CompSci sophomore that was missing in compiler construction class for a third of the year. It would have to be egregiously naively written and would end up ridiculous amounts of stack space.Pedersen
@Starfish My point is that the same "optimisation" that reuses space for local variables handles reusing space for intermediate subexpression values. There are several times more of those than there are of locals. So while you can imagine compilers that do not do this (i.e. gcc with -O0), for all intents and purposes they do not exist in practice. This optimisation is fundamental enough that you can consider it a basic feature, only slightly more advanced than register allocation. (And the implementation technique is similar.)Pedersen
@Pedersen I was talking about compilers like Actionscript, etc. that don't (or unlikely to) do this kind of optimization.Starfish
@Starfish First of, [citation needed]. Remember that Adobe contributed the ActionScript runtime to Mozilla of which the JIT ended up in SpiderMonkey. SpiderMonkey uses SSA for its intermediate representation - with SSA, reassigning an existing variable leads to the same IR as using a new variable, and more likely than not SSA implies the use of linear scan register allocation. Now, it's not conclusive, but it's not implausible that the ActionScript runtime is comparably advanced to other JS runtimes in this regard.Pedersen
@Starfish Basically, it seems you're grossly underestimating the abilities of production-grade compiler writers, especially considering that register allocation based on variable liveness is a problem tackled in third-year college courses, not something you need to pore over academic papers for. It's not a trivial problem, but difficulty-wise it's on par with many other things they absolutely must do. So I stand by my claim that it's not necessary for the compiler to be "good enough", it has to be "not pretty damn terrible enough".Pedersen
I used to make an exception for "i" as a loop index, etc, vs having to have multiple distinct loop index names. But most modern C compilers allow you to declare a loop index in the for statement and limit its scope to that, so you can reuse "i" while still declaring it anew in each loop.Scruggs
And I'd agree with the others that any "production grade" compiler should know how to combine live ranges. If you're using a compiler that doesn't know how to do this then presumably performance is not an important feature for you.Scruggs
@HotLicks That's what I meant when I say reuse it. "Reuse" is not just the simple re-assignment of a value - it's a re-purposing of a symbol name, since this is what may introduce problems to other maintainers. The compiler doesn't care what a variable is used for.Starfish
@Pedersen On the contrary, I think you're looking at a restricted set of languages. Look at languages like Javascript, AS3, PHP, etc. - exactly because these are not very high-performance languages, their interpreters / compilers may skip a number of optimizations and this would be exactly what may lead a programmer to conclude that some memory can be saved. You may be right when it comes to C (I never argued that) but I disagree with your statement in a more general context.Starfish
@Starfish At least two Javascript interpreters provably use compiler techniques whose intent is to enable fast register allocation (which should involve live range combining) in a JIT. Seeing as ActionScript is close enough to JavaScript, it's plausible it's just as capable. I submit that my set of languages is restricted to "any language with an actual compiler or JIT" because this is such a fundamental technique. This probably excludes straightforward interpreters like Python (and maybe PHP but Zend Engine internals aren't well-known), but there the battle for memory use is lost anyway.Pedersen
@Starfish So, yes, in the very, very general case, languages+runtimes where this matters do exist. Except a) you're not using them, and if you are, then b) you don't care about that sort of memory overhead. And honestly, my objection is mostly about the phrasing of "well-writen optimizing compilers", where I'd say "everything you could call a compiler without stretching the definition of the word". To me it seems like it overstates the likelihood of any memory savings occuring in practice.Pedersen
W
8

As with almost everything in programming, it depends on the situation.

If you're reusing the same variable for different purposes, then it makes your code less readable and you shouldn't be doing it. If the purpose is the same (e.g. loop counters), then you can reuse with no problem since this isn't making your code less readable.

Reusing a variable will avoid reserving space in the stack, which results in a faster (you don't waste time reserving space in stack and pushing the value) and less memory consuming (you're not storing it in the stack) program. But this benefits are absolutely negligible in the whole program context, and also relative to architecture, language and compiler. So I would worry more about readability than this tiny benefits.

Willard answered 26/7, 2013 at 14:47 Comment(0)
Y
8

Bad. For simple types like ints, passed by value, the compiler will be able to figure out when they are unneeded and reuse the space.

For example, I compiled the following C++ code in Visual Studio 2010 using 32-bit Release mode:

for (int i = 0; i < 4; ++i)
{
    printf("%d\n", i);
}

for (int j = 0; j < 4; ++j)
{
    printf("%d\n", j);
}

and got the following assembler output:

; 5    :    for (int i = 0; i < 4; ++i)

    mov edi, DWORD PTR __imp__printf
    xor esi, esi
    npad    6
$LL6@main:

; 6    :    {
; 7    :        printf("%d\n", i);

    push    esi
    push    OFFSET ??_C@_03PMGGPEJJ@?$CFd?6?$AA@
    call    edi
    inc esi
    add esp, 8
    cmp esi, 4
    jl  SHORT $LL6@main

; 8    :    }
; 9    : 
; 10   :    for (int j = 0; j < 4; ++j)

    xor esi, esi
$LL3@main:

; 11   :    {
; 12   :        printf("%d\n", j);

    push    esi
    push    OFFSET ??_C@_03PMGGPEJJ@?$CFd?6?$AA@
    call    edi
    inc esi
    add esp, 8
    cmp esi, 4
    jl  SHORT $LL3@main

; 13   :    }

You can see that the compiler is using the esi register for both i and j.

Yakutsk answered 26/7, 2013 at 15:8 Comment(0)
B
3
  int x=0;

  //..
  x = atoi(char_var);

  //..
  int x = 0;

You cannot redeclare x in the same scope. If you are not redeclaring it but using it for different purposes, you are free to do this. But it's a bad practice and should be avoided as it decreases code readability. Also you should find meaningful names for your variables for the same reasons.

Beneficence answered 26/7, 2013 at 14:43 Comment(1)
Thank you for the reply and explanation. Btw, I did not have intention to redecelerate it, it was just a copy pate mistakeBerkshire
M
2

You can re-use it but i don't think it will bring a any significant benefit to your programm and it will make your code less readable.

Mallette answered 26/7, 2013 at 14:45 Comment(0)
A
2

In general for any language, if you reuse variable names, and then you decide to refactor part of your code into another method, you end up having to add or edit declarations.

int i;
for(i = 0; i < 10; ++i) {
    printf("%d\t%d\n", i , i * i);
}
for(i = 0; i < 10; ++i) {
    printf("%d\t%d\n", i , i * i * i);
}

Suppose you take the second loop and move it to a print_cubes method. You will not be able to just cut and paste the for loop, as i will have no declaration there. A good IDE might be able to insert the declaration, but it might worry about the side-effects on i in the code you've typed.

In general, compilers can consolidate used variables by what are called graph-coloring algorithms. Consider this variant:

for(int i = 0; i < 10; ++i) {  // BLOCK 1
    printf("%d\t%d\n", i , i * i);
} // END BLOCK 1
for(int j = 0; j < 10; ++j) { // BLOCK 2
    printf("%d\t%d\n", j , j * j * j);
} // END BLOCK 2

The compiler lists the used variables: i, j. It lists the blocks being used: BLOCK 1, BLOCK 2. The parent function is also a block, but i and j are visible only in BLOCK 1 and BLOCK 2. So, it makes a graph of the variables, and connects them only if they are visible in the same block. It then tries to compute the minimum number of colors needed to color each vertex without giving two adjacent vertices the same color, similar to the Haken-Appel Four Color Theorem. Here; only one color is needed.

Alcyone answered 26/7, 2013 at 15:13 Comment(0)
D
2

Put it this way - how would you like it if I wrote a big pile of undocumented, complex code in such a way and, then, you get the job of maintaining/enhancing it.

Please do not do such a thing, ever :)

Discussion answered 26/7, 2013 at 17:9 Comment(0)
B
1

It is better to reuse variables in terms of memory. But be careful you don't need the value in a variable before reusing it. Other than that, you shouldn't use always the variable. It is important to keep a clean and readable code. So I advice you to choose different variable with names depending on the context so that your code don't become confusing.

You should also take a look at dynamic memory allocation in C, which is very useful to manage memory and variables.

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

Bombay answered 26/7, 2013 at 14:48 Comment(3)
An optimizing compiler will typically optimize away extra variables anyway, so reusing variables won't even save memory most of the time.Unsaid
Just FYI, local variables are not dynamically allocated.Willard
-1: Considering that compilers, whether they're optimising or not, usually work off an IR in single static assignment form where every time you assign a value to a variable, a "new" local variable is synthetised, reusing a name should make exactly no difference to the generated code.Pedersen
K
-1

Re-using is actually great, if you haven't force artificial and unnecessary labels on them in the first place —-

say

function bignum_squaring( bigint_input_str, 
                          input_arr,
                          output_arr,
                          output_str, ...) {
   split(  bigint_input_str
           into input_arr 
           by 7-digit chunks)

   …… iterate over input_arr chunks and
      storing chunked outputs in output_arr …… 
   
   …… zero-pad output_arr cells, 
      sequentially concat them into output_str

   return output_str 
}

If the function were fed gigantic inputs like a few million digits or more, and bigint_input_str were simply left in place after the digits were already split into the arrays for chunked processing,

every single time when the kernel is process switching it has to bring all those bytes back in, even though you absolutely don't need it anymore.

But if you don't force labeling them like that and simply use a placeholder variable name like __, then it's very intuitive to re-use it without having to even write in extra comments detailing how it's recycled (they were only added below for illustrative purposes) :

function bignum_squaring( __, # a bigint
                              #  numeric string
                        input_arr,
                        output_arr,...) {

   split( input str (__)
          into input_arr 
          by   7-digit chunks)
    
   __ = ""  # setting it as zero-length str,
            # thus releasing resources being held up
  
   …… iterate over input_arr chunks and
      storing chunked outputs in output_arr …… 
   
   …… zero-pad output_arr cells, 
      sequentially concat them into 
      a temp placeholder string variable (__)

   return __
}

So now, __ is being repurposed as the output string instead.

For functions that only accept 1 argument as user input, with a function name that's already crystal clear as to what it performs, e.g. abs(), floor(), sqrt() etc, increased verbosity of the input parameter name doesn't lead to any better user experiences.

Similarly, in the example above, since the function itself is already named bignum_squaring(), whether one calls it __ or bigint_input_str or even input_then_output_str doesn't matter as long as the documentation clearly specifies it must be a numeric string type.

Kolodgie answered 29/6, 2024 at 2:22 Comment(0)
U
-2

Only drawback is readability of your code.

Reusing variables you are saving memory.

Speed is not affected (unless you have to use more instructions in order to reuse variable).

Unshod answered 26/7, 2013 at 14:44 Comment(3)
Reusing variables will not save any memory if the compiler is good enough -- it is allowed to optimize away redundant variables as long as it can prove that usage does not overlap.Unsaid
Are you actually sure this isn't optimized out? It seems reasonable to me that a compiler could tell int x isn't referred to again in a function, and store the subsequent int y in that memory.Piero
-1: No you are not saving memory. Read about static single assignment form - the compiler IR makes no difference between reassigning to an existing variable and assigning to a new one. What matters is the overlap in lifetime between versions of a variable. When optimizing, the compiler will use as much memory as is needed for the maximum overlap. When not optimizing, the compiler will actually use a new memory location for every subexpression - you can see this using the -G0 flag to gcc.Pedersen

© 2022 - 2025 — McMap. All rights reserved.