2012-06-24 5 views
0

Недавно я наткнулся на исходный код, где программист объявил переменные внутри одного бесконечного цикла в зависимости от , если/else условиях. То, что я нашел странным, - это то, что код работал, и это не подразумевало утечек памяти. Сначала я подумал, что, возможно, это была специфическая архитектура (код для ARM), но я провел несколько тестов, и я узнал, что двоичные файлы на IA32, скомпилированные с GCC, действуют одинаково.Переменные и выделение внутри цикла

Мой подход был таким: Я создал две небольшие программы foo.c и bar.c

Foo.c:

#include <stdio.h> 

int main(void) 
{ 
    int i; 

    for(i=0; i<10; i++) { 
     char buf[10]; 
     buf[i] = 0; 
     printf("buf[%d] (@ %#x) == %d\n", i, &buf, buf[i]); 
    } 

    return(0); 
} 

Bar.c:

#include <stdio.h> 

int main(void) 
{ 
    int i; 

    for(i=0; i<10; i++) { 
     char *ptr; 
     ptr = (char *) malloc(10); 
     ptr[i] = 0; 
     printf("buf[%d] (@ %#x) == %d\n", i, &ptr, ptr[i]); 
    } 

    return(0); 
} 

Причиной явного различия между объявлением массива в Foo.c и выделением памяти в Bar.c было то, что f Сначала я подумал, что, возможно, компилятор автоматически определяет, что это одна и та же переменная, и просто игнорирует декларацию после начального для итерации, которая, конечно же, не должна быть в Bar.c, потому что я явно выделяю память.

Что было действительно странно для меня, что в обоих примерах адрес как массива, так и выделенной памяти остается неизменным после начального для итерации.

Я не совсем понимаю это, и у меня нет моей копии K & R со мной, поэтому я буду благодарен за объяснение. (Также, если я допустил какую-либо ошибку в своих рассуждениях, я был бы рад отметить это.)

+0

C++ - это другой язык от C, не знаете? И 'return' не является функцией ... – Griwes

+0

Tak, wiem. Czy Ty wiesz, ze to zachowanie jest na pewno zwiazane z kompilatorem i jestem prawie pewien, ze G ++ zachowa sie tutaj tak samo. Идентификатор Wiec C++ nie powinien zostac usuniety IMO. – andy

+0

Во-первых, это английский сайт, поэтому пишите только на английском языке; факт, что я из Польши ничего не меняет. Во-вторых, вы пишете на C, а не на C++, поэтому тег C++ здесь неактуальен. – Griwes

ответ

2

Не печатайте адрес вашего указателя ptr (который является константой внутри вашей петли, поскольку ptr является локальной переменной , в текущем кадре вызова), но печатать сам указатель:

printf ("buf[%d] == %d, ptr == %p\n", i, ptr[i], (void*) ptr); 

(помните, что если a массив, вы должны &a == a и их типы совместимы, но если p это указатель, вы обычно дон 't имеют &p == p, и их типы несовместимы)

Конечно, если у вас есть указатель внутри цикла, вы обычно хотите free, указатель в конце тела цикла.

Вы должны научиться компилироваться с помощью gcc -Wall -g (на Linux, возможно, даже с -Wextra) и использовать отладчик gdb (на Linux).

valgrind - полезный инструмент для Linux, чтобы улавливать утечки памяти. Вы можете использовать Boehm's conservative garbage collector, чтобы «избегать» их (используя GC_malloc вместо malloc, и не беспокоиться за free -в явной памяти).

+1

Если честно, это должно быть 'gcc -Wall -Wextra', потому что' -Wall' на самом деле '-Wnot-all' (и иногда это удобно использовать' -Werror'). – Griwes

+0

Неважно, если я делаю & ptr или ptr, потому что это не имеет отношения к вопросу, который я задал. Зачем? потому что даже если я распечатаю ptr вместо & ptr, я все равно получаю базовый адрес. Я спрашиваю, почему это то же самое, когда я явно выделяю память с помощью malloc на каждой итерации. Кроме того, в этом примере освобождение бессмысленно (потому что я * ХОЧУ * сделать утечку памяти). – andy

+0

@Griwes '-Wall' включает в себя большинство * обычно полезных * предупреждений, хотя многое, включенное' -Wextra', субъективно или пытается угадать лучшие практики. – delnan

1
#include <stdio.h> 
#include <stdlib.h> 
int main(void) 
{ 
    int i; 
    for(i=0; i<10; i++) { 
    char *ptr; 
    ptr = malloc(10); 
    ptr[i] = 0; 
    printf("buf[%d] ptr is %#x, &ptr is %#x, ptr[i] is %d\n", i, ptr, &ptr, ptr[i]); 
    } 
    return 0; 
} 

Я добавил ptr к выходу вашего bar.c. Теперь вы можете найти ответ. Вот новый исходный код bar.c. Я исправил все ошибки, о которых говорили другие люди. Вы можете скомпилировать его с помощью «gcc -o bar bar.c» и запустить «./bar».

Выход:

./bar 
    buf[0] ptr is 0x820a008, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[1] ptr is 0x820a018, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[2] ptr is 0x820a028, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[3] ptr is 0x820a038, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[4] ptr is 0x820a048, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[5] ptr is 0x820a058, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[6] ptr is 0x820a068, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[7] ptr is 0x820a078, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[8] ptr is 0x820a088, &ptr is 0xbfb9dc48, ptr[i] is 0 
    buf[9] ptr is 0x820a098, &ptr is 0xbfb9dc48, ptr[i] is 0 

Вы можете найти значение ПТР изменений в каждой строке, но значение & PTR сохраняет то же значение. Заключение: ptr выделяет различную память на каждой итерации «для», но адрес ptr не изменяется.

Смежные вопросы