2014-02-10 2 views
2

Я очень новичок в C, поэтому извините заранее, если это действительно базовый. Это связано с домашним заданием.Распределение памяти и изменяющиеся значения

У меня есть несколько вспомогательных функций, и каждый изменяет значение данной переменной (бинарные операции в основном), т.е .:

void helper1(unsigned short *x, arg1, arg2) --> x = &some_new_x 

Основная функция вызывает другие аргументы arg3, arg4, Arg5. Предполагается, что x начнется с 0 (16-бит 0), а затем будет изменен вспомогательными функциями, и после всех модификаций должен быть в конечном итоге возвращен mainFunction.

Где я могу объявить начальный x и как/где я могу выделить/освободить память? Если я объявлю его в пределах mainFunc, он будет сбрасываться до 0 при каждом вызове помощников. Если я освобожу и перераспределяю память внутри вспомогательных функций, я получаю ошибку «указатель, освобожденный не был», хотя я освободил и выделил все, или так я думал. Глобальная переменная тоже не работает.

Я бы сказал, что я действительно не совсем понимаю распределение памяти, поэтому я предполагаю, что моя проблема связана с этим, но вполне возможно, что я просто не понимаю, как изменять значения переменных в C на более базовом уровне ...

ответ

0

Функции получают двоичную копию своих входов для локально ограниченных переменных. Таким образом, вспомогательная функция не может изменить значение, с которым она была вызвана, только ее локальная копия.

void f(int n) 
{ 
    n = 2; 
} 

int main() 
{ 
    int n = 1; 
    f(n); 
    return 0; 
} 

Несмотря на то же имя, n в f является локальным для вызова f. Таким образом, n в main никогда не меняется.

Путь обойти это пройти мимо указателя:

int f(int *n) 
{ 
    *n = 2; 
} 

int main() 
{ 
    int n = 1; 
    f(&n); 
    // now we also see n == 2. 
    return 0; 
} 

Обратите внимание, что, опять-таки, n в f является локальным, так что если мы изменили указатель n в f, это не будет иметь никакого влияния на main Перспектива. Если бы мы хотели изменить адрес n в main, нам пришлось бы передать адрес указателя.

void f1(int* nPtr) 
{ 
    nPtr = malloc(sizeof int); 
    *nPtr = 2; 
} 

void f2(int** nPtr) 
{ 
    // since nPtr is a pointer-to-a-pointer, 
    // we have to dereference it once to 
    // reach the "pointer-to-int" 
    // typeof nPtr = (int*)* 
    // typeof *nPtr = int* 

    *nPtr = malloc(sizeof int); 

    // deref once to get to int*, deref that for int 
    **nPtr = 2; 
} 

int main() 
{ 
    int *nPtr = NULL; 

    f1(nPtr); // passes 'NULL' to param 1 of f1. 

    // after the call, our 'nPtr' is still NULL 

    f2(&nPtr); // passes the *address* of our nPtr variable 
    // nPtr here should no-longer be null. 

    return 0; 
} 

---- EDIT: Что касается собственности распределений ----

Принадлежность указателей грязный может червей; стандартная библиотека C имеет функцию strdup, которая возвращает указатель на копию строки. Программисту остается понять, что указатель выделяется malloc и, как ожидается, будет выпущен диспетчеру памяти по вызову free.

Этот подход становится более обременительным, поскольку указываемая вещь становится более сложной. Например, если вы получаете структуру каталогов, вы можете понять, что каждая запись является выделенным указателем, за который вы отвечаете за освобождение.

dir = getDirectory(dirName); 
for (i = 0; i < numEntries; i++) { 
    printf("%d: %s\n", i, dir[i]->de_name); 
    free(dir[i]); 
} 
free(dir); 

Если это была операция файл, который вы бы немного удивлены, если библиотека не обеспечивает close функцию и сделали вас рушить дескриптор файла по своему усмотрению.

Многие современные библиотеки склонны брать на себя ответственность за свои ресурсы и обеспечивать соответствующие функции получения и освобождения, например. для открытия и закрытия соединения MySQL:

// allocate a MySQL descriptor and initialize it. 
MYSQL* conn = mysql_init(NULL); 

DoStuffWithDBConnection(conn); 

// release everything. 
mysql_close(conn); 

LibEvent имеет, например,

bufferevent_new(); 

выделить буфер событий и

bufferevent_free(); 

, чтобы освободить его, несмотря на то, что она делает это немного больше, чем таНос() и свободный(), но при наличии у вас вызывать эти функции, они обеспечивают четко определенный и понятный API, который берет на себя ответственность за знание таких вещей.

Это является основой для концепции, известной как "RAII" в C++

+0

О, так точно противоположно тому, что я делал. Думаю, я все время указывал на 'n', объявленный в основной функции, а не наоборот. Благодаря! Это все фиксировало. – user3290954

+0

Я отредактировал ответ, чтобы включить объяснение от указателя к указателю, надеюсь, что это поможет. Что касается права собственности на указатели, это целая червь червей, и я добавлю редактирование, отвечая на эту часть вопроса. – kfsone

1

Переменная x будет существовать, в то время как блок, в котором он был объявлен, выполняется, даже при выполнении помощника, и указав на помощники, позволяет им изменять его значение. Если я правильно понимаю вашу проблему, вам не нужно динамическое распределение памяти. Следующий код возвращает 4 из mainFunction:

void plus_one(unsigned short* x) 
{ 
    *x = *x + 1; 
} 

unsigned short mainFunction(void) 
{ 
    unsigned short x = 0; 
    plus_one(&x); 
    plus_one(&x); 
    plus_one(&x); 
    plus_one(&x); 
    return x; 
} 
+0

или '* х + = 1;' 'или (* х) ++;' где скобки необходимы. –

1

По Вашему описанию я бы предложил объявить й в своей основной функции в качестве локального переменного (выделенной из стека), который вы затем передать ссылку на ваши вспомогательные функции и возврат это от вашей основной функции по стоимости.

int main() 
{ 
    int x;  //local variable 
    helper(&x); //passed by reference 
    return x; //returned by value 
} 

Внутри вашего помощника вы можете изменить переменные разыменовывая его и назначая любое значение, необходимое:

void helper(int * x) 
{ 
    *x = ...; //change value of x 
} 

Альтернатива объявляет указатель х (который получает выделенный из кучи), пропустив ее в ваш помощник выполняет функции и освобождает его, когда вы больше не используете его. Но этот маршрут требует более тщательного рассмотрения и подвержен ошибкам.

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