4
int main(){
    int i = 0;
    while(i < 2){
        char str[10];
        printf("%p\n", str); //outputs same address both the times
        i++;
    }

    i = 0;

    while(i<2){
        char *str;
        str = (char*)malloc(10*sizeof(char));
        printf("%p\n", str); //outputs two different addresses
        i++;
    }

}

In the above code why declaring same character array in a loop gives the same address although variable is declared two different times. So as per my understanding it should allocate new memory to it. But it returns the same address every time.

In the second where memory is allocated dynamically to it, it returns two different addresses which is understandable as malloc will find new contiguous memory blocks for me everytime.

But why it prints the same address in the first case?

3
  • 2
    In the first case, memory is auto-allocated always at the same position since you're looping. Call it from somewhere else (from another chain of calls of your program you'll get a different address ... several times. Commented Feb 18, 2017 at 18:06
  • 1
    The first case destroys the array between loop iterations, so the next time it is created, the same storage is reused. Commented Feb 18, 2017 at 18:07
  • Thanks for answering. That pretty much explains why this happens Commented Feb 18, 2017 at 18:12

2 Answers 2

7

In your first example, char str[10]; is local variable valid only within scope of { }. After the scope ended, the variable is destroyed and the memory is "freed". The next alocation can be in the same space because it is empty and available.

In the second sample, you use malloc... memory is not auto-released until you call free (or program ends).

Sign up to request clarification or add additional context in comments.

4 Comments

Local variables aren't freed in the same manner as free works. Local variables exist in the program stack.
@AhmedMasud - Care to elaborate? Short of quoting the C standard, this is a colloquial explanation of exactly how it works.
sure thing (see answer below)
@AhmedMasud - So you give an example of a particular implementation as rebuttal to a paraphrasing of the C standard?
3

In the first case the char str[10] is allocated on the stack once, and the pointer to it is maintained while the program stays in the same block {}. It's not allocated twice, even though you're looping. It is NOT that the memory becomes "available" at the end of the mythical } and then gets "reassigned" because it was available again. Look at LLVM ir code that's generated for the first part:

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  %i = alloca i32, align 4

  ; actuall allocation of str as a 10 byte value
  ; occurs OUTSIDE the looping labels
  ; and deallocation also occurs outside the looping
  ; label (see label 10) 
  %str = alloca [10 x i8], align 1


  %str1 = alloca i8*, align 8
  store i32 0, i32* %1
  store i32 0, i32* %i, align 4
  br label %2

; this is the opening {
; <label>:2                                       ; preds = %5, %0
  %3 = load i32* %i, align 4
  %4 = icmp slt i32 %3, 2

; LOOP breaks out on the condition matching i>2
  br i1 %4, label %5, label %10


; <label>:5                                       ; preds = %2
  %6 = getelementptr inbounds [10 x i8]* %str, i32 0, i32 0
  %7 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %6)
  %8 = load i32* %i, align 4
  %9 = add nsw i32 %8, 1
  store i32 %9, i32* %i, align 4

  ; LOOP back to label %2 which does NOT include the
  ; original allocation logic between here and there logic 
  ; this is the closing } 
  br label %2



; DEALLOCATION occurs here

; <label>:10                                      ; preds = %2
  store i32 0, i32* %i, align 4
  br label %11

here is the same code using gcc

use gcc -fdump-tree-all to see different stages of interpretation:

gimple

main ()
{
  int i;

  i = 0;
  goto <D.2181>;
  <D.2180>:
  {
    char str[10];

    try
      {
        printf ("%p\n", &str);
        i = i + 1;
      }
    finally
      {
        str = {CLOBBER};
      }
  }
  <D.2181>:
  if (i <= 1) goto <D.2180>; else goto <D.2182>;
  <D.2182>:
  i = 0;
  goto <D.2186>;
  <D.2185>:
  {
    char * str;
    extern void * malloc (long unsigned int);

    str = malloc (10);
    printf ("%p\n", str);
    i = i + 1;
  }
  <D.2186>:
  if (i <= 1) goto <D.2185>; else goto <D.2187>;
  <D.2187>:
}

original

;; Function main (null)
;; enabled by -tree-original


{
  int i = 0;

    int i = 0;
  goto <D.2181>;
  <D.2180>:;
  {
    char str[10];

        char str[10];
    printf ((const char * restrict) "%p\n", (char *) &str);
    i++ ;
  }
  <D.2181>:;
  if (i <= 1) goto <D.2180>; else goto <D.2182>;
  <D.2182>:;
  i = 0;
  goto <D.2186>;
  <D.2185>:;
  {
    char * str;
    extern void * malloc (long unsigned int);

        char * str;
    str = (char *) malloc (10);
    printf ((const char * restrict) "%p\n", str);
    i++ ;
  }
  <D.2186>:;
  if (i <= 1) goto <D.2185>; else goto <D.2187>;
  <D.2187>:;
}

1 Comment

btw I chose to generate llvm code rather than any particular assembly to demonstrate that this is the same thing that would occur no matter what the target output would be (c -> object code, c->javascript, c-> whatever). Use clang to gen i can show you gcc output too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.