Могут ли все функции в потоке иметь доступ к динамически выделяемой памяти (куче) даже без указания указателя или это локально для функции? - PullRequest
1 голос
/ 28 марта 2009

У меня очень простой вопрос, и мне нужна помощь. Я пытаюсь понять, каков объем динамически выделяемой памяти (в куче).

#include <stdio.h>
#include <malloc.h>

//-----Struct def-------
struct node {
        int x;
        int y;
};

//------GLOBAL DATA------

//-----FUNC DEFINITION----
void funct(){
  t->x = 5; //**can I access 't' allocated on heap in main over here ?**
  t->y = 6; //**can I access 't' allocated on heap in main over here ?**
  printf ("int x = %d\n", t->x);
  printf ("int y = %d\n", t->y);
  return;
}

//-----MAIN FUNCTION------
int main(void){
      struct node * t = NULL;// and what difference will it make if I define 
                                 //it outside main() instead- as a global pointer variable
          t = (struct node *) malloc (sizeof(struct node));
      t->x = 7;
      t->y = 12;
      printf ("int x = %d\n", t->x);
      printf ("int y = %d\n", t->y);

    funct(); // FUNCTION CALLED**
    return 0;
}

Здесь можно получить доступ к структуре t в funct(), даже если память выделена в main() без передачи аргумента (указатель на t на function funct) - поскольку куча является общей для потока? Какая разница, если я определю struct node * t = NULL вне main() как глобальную переменную и есть ли что-то не так с ней?

Ответы [ 10 ]

8 голосов
/ 28 марта 2009

Когда вы используете malloc (), память, возвращаемая этим, может быть доступна в любом месте вашего кода, при условии, что вы можете видеть переменную с указателем, возвращаемым malloc ().

Так что в вашем коде, если бы t был глобальным, он был бы виден в main и в funct (), и да, вы могли бы использовать его в обоих.

Таким образом, как уже упоминалось в предыдущих ответах, функция funct () понятия не имеет, что такое t, потому что объявление и определение t находятся в main; это выходит за рамки функциональности. Память, выделенная для t , будет пригодной для использования в funct, , если funct знает, что такое t.

3 голосов
/ 28 марта 2009

Чтобы получить доступ к переменной, функция должна знать адрес этой переменной - значение указателя «t». Поэтому вы должны передать его в функцию, иначе у функции не будет средств, чтобы узнать, к чему получить доступ.

Вы также можете объявить глобальную переменную pinter "t" и использовать ее из любой функции, но опять же явно.

3 голосов
/ 28 марта 2009

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

2 голосов
/ 28 марта 2009

Доступ к выделенной памяти возможен, но это не ваша проблема. Переменная 't' видима только в пределах main , потому что именно там вы ее объявили. Это две разные концепции, которые вам нужно понять. Хранилище данных - это не то же самое, что переменная, которая к нему относится.

1 голос
/ 28 марта 2009

Принятый ответ немного вводит в заблуждение.

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

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

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

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

UPDATE

@ Кевинф говорит:

Использование локального адреса в рамках локального вполне допустимо, но ваше предложение НЕ безопасно для памяти

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

Это отдельное понятие от безопасности памяти или времени жизни объекта в месте хранения. Это концепция времени выполнения. Например:

void g(int *p)
{
    (*p)++;
}

void f()
{
    int n = 1;
    g(&n);
}

Здесь идентификатор n находится только в области видимости в f. Имя хранилища, которое существует во время работы f. Мы получаем адрес &n этого хранилища и передаем его g. Обратите внимание, что g не может использовать имя n напрямую. n здесь не в поле зрения! Тем не менее, хранилище идеально подходит в этом примере , поскольку f еще не завершено (ожидает завершения g).

Большинство реальных программ на C и C ++ часто делают это, и это разрешено стандартом. Это ни вообще безопасно, ни вообще небезопасно. Иногда это безопасно, а иногда нет. Это основная проблема с C и C ++. Неопределенное поведение не всегда обнаруживается при локальном исследовании функции; Вы должны знать, как оно используется в более широком контексте.

Более поздние изменения в языке (в g ++ используйте опцию -std=c++14 для компиляции этого) предлагают другие способы изучения этой серой области:

auto f()
{
    int n = 1;

    auto g = [&] 
    { 
        ++n; 
        cout << "Incremented to " << n << endl;
    };

    g();

    return g;
}

void h()
{
    auto g = f();

    cout << "Returned into h" << endl;

    g();
}

Внутри f, n находится в области видимости. И g содержит экземпляр лямбды, тело которой также является частью области действия n. И звонок на g внутри f в порядке. Но в f, когда мы сохраняем лямбду в другой переменной с именем g, последующий вызов к ней не допускается! Мы, даже не используя &n, неявным образом захватили место хранения, которое нам больше не доступно, поскольку его время жизни было ограничено продолжительностью нашего вызова до f.

Кстати, когда я говорю, что не разрешено, оно будет компилироваться. Но в моей системе это печатает:

Incremented to 2
Returned into h
Incremented to 167772162

Что явно демонстрирует неопределенное поведение.

1 голос
/ 28 марта 2009

Просто измените вашу функцию, чтобы принимать указатель на эту память.

/* changing the variable name to make it clear that p is scoped to funct() */
/* when it's passed, just as t is local to main() */
void funct(struct node *p){
p->x = 5;
p->y = 6;
printf ("int x = %d\n", p->x);
printf ("int y = %d\n", p->y);
return;
}

...

funct(t);

/* now free the memory you allocated */
free(t);
return 0;

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

1 голос
/ 28 марта 2009

Нет, вам нужно сделать t глобальным или передать указатель на funct (), я бы порекомендовал сделать второй. Прямо сейчас, t находится в области действия только main ().

Вы также можете рассмотреть возможность добавления мьютекса в структуру, если вы планируете иметь несколько потоков, работающих с t одновременно. Если вы это сделаете, также рассмотрите mlock (), чтобы гарантировать, что структура не выгружается, или блокировки становятся медленными.

1 голос
/ 28 марта 2009

В вашем коде 't' является локальной переменной main () - к ней нельзя получить доступ откуда-либо еще. Это не имеет ничего общего с потоками. Пожалуйста, исправьте ваш код.

0 голосов
/ 08 февраля 2013

t - это переменная-указатель, локальная для main (), но содержимое внутри 't' существует во всей программе до тех пор, пока / или мы не освободим ее явно. Время жизни адреса, который хранится в t, определяется через программу как он динамически размещается в куче. Так что вы можете использовать этот адрес памяти в любом месте программы, но не переменную локальную переменную 't'. Поскольку t является локальной переменной, она имеет только эту область видимости блока и может использоваться только с в main ()> Таким образом, лучший способ использовать t и его содержимое - объявить t и затем динамически выделить память в глобальной области видимости.

Короче говоря, переменная 't' является локальной, но значение в 't' находится в куче. Единственный способ получить доступ к памяти кучи - через 't'. Таким образом, мы объявляем t в глобальном пространстве, поэтому мы можем получить доступ к памяти кучи в любом месте программы через 't'. Иначе, как вы получите доступ к куче памяти, когда t выходит из области видимости

0 голосов
/ 29 марта 2009

, поскольку t - указатель на структуру, определенную в функции main. вы не можете получить к нему доступ в функции (). Сделайте t глобальным, чтобы к нему можно было получить доступ в funct (), или передайте указатель в качестве аргумента funct (). сделать его глобальным не лучший выбор. В многопоточной среде многопоточные потоки могут пытаться получить к нему доступ, поэтому вам нужно иметь какой-то механизм блокировки, чтобы избежать доступа одновременно. добавление функциональности блокировки it может привести к другим накладным расходам, поэтому лучше всего передать его в качестве аргумента.

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