Понимание указателей и адреса памяти - PullRequest
3 голосов
/ 22 марта 2020
#include<stdio.h>

int g(int *a, int *b);

int main()
{
    int a = 2;
    int b = 7;

    b = g(&b , &a);

    printf("a = %d\n", a);
    printf("b = %d\n", b);

    return 0;
}

int g(int *a, int *b)
{
    (*a) = (*a) + 3;
    (*b) = 2*(*a) - (*b)+5;

    printf("a = %d, b = %d\n", *a, *b);

    return (*a)+(*b);
}

Вывод:

a = 10, b = 23
a = 23
b = 33

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

Спасибо за помощь!

Ответы [ 4 ]

4 голосов
/ 22 марта 2020

Последовательность событий, представленных в вопросе:

int main()
{  

Объявление a и b и присвоение значения:

    int a = 2;
    int b = 7;

Вот хитрость, адрес, передаваемый параметру int* a, фактически равен b, и наоборот для второго параметра:

    b = g(&b , &a);

Здесь просто выводятся значения a и b:

    printf("a = %d\n", a);
    printf("b = %d\n", b);

    return 0;
}

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

int g(int *a, int *b) {

Здесь разыменование указателя (*a, скобки в этих случаях не нужны) означает, что вы теперь работают со значением, сохраненным в адресе, указанном a, поэтому 7 + 3 = 10, теперь значение, сохраненное в адресе, указанном a, равно = 10:

    (*a) = (*a) + 3;

Здесь то же самое, разыменование указателей, поэтому 2 * 10 - 2 + 5 = 23, значение хранится в адресе, указанном b будет 23:

    (*b) = 2*(*a) - (*b)+5;

Здесь печать a = 10 и b = 23, опять же, разыменование указателей означает, что вы работаете со значениями, хранящимися в адресах на них указывают:

    printf("a = %d, b = %d\n", *a, *b);

Возвращаемое значение равно 10 + 23 = 33, поэтому для b = g(&b, &a), b будет присвоено значение 33, a уже 23, поэтому он остается таким:

    return (*a)+(*b); 
}
3 голосов
/ 22 марта 2020

С помощью & вы даете адрес переменной функции вместо значения. С помощью * вы можете получить доступ к значению адреса.

С помощью b = g(&b , &a); вы дадите адрес переменной b и a функции. Но вы можете получить доступ к адресу b с помощью * a, потому что вы объявили функцию следующим образом: int g (int * a, int * b):

  • *a указывает на адрес вашей переменной b.
  • *b указывает на адрес вашей переменной a.

Я думаю, что разные имена переменных вас смущают.

Чтобы было проще самостоятельно вы можете изменить объявление на int g (int * b, int * a) В случае, если вы хотите изменить его:

  • *b будет указывать на адрес вашей переменной b и
  • *a будет указывать на адрес вашей переменной a.
2 голосов
/ 22 марта 2020

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

Чтобы любая функция могла изменить значение параметра, вы должны передать этому параметру указатель :

void foo( T *ptr )  // for any type T
{
  *ptr = new_T_value(); // write a new value to the thing ptr points to
}

void bar( void )
{
  T var;
  foo( &var ); // write a new value to var
}

В приведенном выше коде все следующие true:

 ptr == &var
*ptr ==  var

Таким образом, когда вы записываете новое значение в выражение *ptr, это то же самое, что записывать новое значение в var.

Я думаю, что отчасти это сбивает вас с толку, так как имена ваших формальных параметров (a и b) и ваших указателей (a и b) переворачиваются - g:a указывает на main:b и наоборот.

 g:a == &main:b  // I'm using g: and main: here strictly to disambiguate
*g:a ==  main:b  // which a and b I'm talking about - this is not based on
                 // any real C syntax.  
 g:b == &main:a
*g:b ==  main:a
0 голосов
/ 22 марта 2020

с помощью * вы получаете доступ к объекту, на который ссылается указатель. Поскольку указатели ссылаются на int переменные a & b, вы выполняете операции над этими переменными. Я думаю, что те же имена переменных сбивают вас с толку

int g(int *p1, int *p2)
{
    (*p1) = (*p1) + 3;
    (*p2) = 2*(*p1) - (*p2)+5;

    printf("*p1 = %d, *p2 = %d\n", *p1, *p2);

    return (*p1)+(*p2);
}
...