Указатели на функции и указатели в списках - PullRequest
0 голосов
/ 03 февраля 2019

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

void copy(int *p, int *s);  

int main(){
  int a = 4, b = 10, *p = &a, *s = &b;
  copy(p, s);
}

void copy(int *p, int *s){
  *s = *p;
  *p = 0;
}

Таким образом, в конце этого «b» равно «a», а «a» равно 0, хотя «a» и «b» не будутобычно может быть изменяемым.

Говоря о списках и, в частности, добавляя элемент в список, я могу использовать такую ​​функцию:

struct list{
  int n;
  struct list *next;
}

struct list *first = NULL;

int main(){
   int n = 3;
   first = add_to_list(first, n);
}

struct list *add_to_list(struct list *first, int n){
  struct list *new_node;
  new_node = malloc(sizeof(struct list));
  if(new_node == NULL) exit(EXIT_FAILURE);
  new_node->value = n;
  new_node->next = first;
  return new_node;
}

Что меня особенно беспокоит, так это почему функцияя не могу просто вернуть тип void, и вместо записи "return new_node" я не могу просто написать "first = new_node" .Поскольку сначала указатель, если я изменю его где-нибудь в моей программе, исходный указатель тоже должен быть изменен, точно так же, как это произошло в первом примере, который я сделал, верно?

Кроме того, немного не связанный вопрос, ноесли у меня есть такая функция:

void first_to_n(int a[], int n){
  a[0] = n;
}

Первый элемент исходного вектора a, который, скажем, объявлен в main, тоже модифицируется, верно?Потому что векторы можно рассматривать как указатели

Ответы [ 4 ]

0 голосов
/ 03 февраля 2019

При этом используется указатель на литеральную строку.
Указатель передается на modifypointer(), где он изменяется для указания на другую литеральную строку.Новая литеральная строка печатается в функции, но main() все еще печатает оригинал.
Указатель передается как указатель на себя в modifypointertopointer(), где он разыменовывается, чтобы указывать на другую литеральную строку.Теперь функция печатает новую литеральную строку, а main() также печатает новую литеральную строку.
В вашем последнем примере можно использовать first = new_node;, за исключением того, что функция объявляет теневую переменную first, а глобальный first равенбольше не входит в область действия функции.

#include <stdio.h>

void modifypointer( char *mod) {
    mod = "modify pointer";
    printf ( "%s\n", mod);
}

void modifypointertopointer( char **ptrmod) {
    *ptrmod = "modify pointer to pointer";
    printf ( "%s\n", *ptrmod);
}

int main( void) {
    char *text = "original";
    printf ( "%s\n", text);
    modifypointer ( text);
    printf ( "%s\n", text);
    modifypointertopointer ( &text);
    printf ( "%s\n", text);
    return 0;
}
0 голосов
/ 03 февраля 2019

Допустим, у нас есть что-то вроде следующего кода

void funcA(int x)
{
    x = 0;
}

void funcB(int *y)
{
    y = NULL;
}

int main(void)
{
    int a = 10;
    int *b = &a;

    funcA(a);
    funcB(b);
}

Что происходит, когда вызывается funcA, так это то, что значение из a копируется в отдельную переменную x внутри функции.Когда выполняется вызов, две копии значения 10 хранятся в разных местах.Когда присваивание x = 0 выполняется внутри функции, модифицируется только локальная переменная x.

Для funcB происходит то же самое.Значение переменной b копируется в отдельную переменную y в функции.Это означает, что есть две отдельные и разные переменные, указывающие на одно и то же местоположение.Но как только задание y = NULL выполнено, это уже не так.Переменная y больше не указывает на то же место, но в функции main b не изменяется, поскольку в функцию передана только копия значения.


Если мытеперь возьмем немного другой пример

void func(int *x, int **y)
{
    *y = x;
}

int main(void)
{
    int a = 10;
    int b = 20;
    int *c = &a;  // Make c point to the variable a

    func(&b, &c);
}

После вызова функции c больше не указывает на a, а указывает на b.Это потому, что для второго аргумента мы передаем значение &c, которое является указателем на переменную c.Внутри функции мы можем затем использовать оператор разыменования для доступа к тому, на что указывает y (которая будет переменной c в функции main).

0 голосов
/ 03 февраля 2019

Что меня особенно беспокоит, так это то, что функция не может просто возвращать тип void, и вместо записи «return new_node» я не могу просто написать «first = new_node».

Как объясняют другие ответы, вы не можете, потому что first является «копией» указателя вне функции.Однако если вы вместо этого измените функцию так, чтобы вы передавали «указатель на указатель», а не просто «указатель», то вы могли бы изменить внешнее значение, которое происходит в функции «копировать».

0 голосов
/ 03 февраля 2019

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

Среди прочегокак создание нетривиальных структур данных и чтобы избежать копий.

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

first (параметр) - это копия first (глобальная).Следовательно, first = new_node изменит только ваш указатель, а не глобальный.

Это более ясно в вашем первом примере:

void copy(int *p, int *s){
  *s = *p;
  *p = 0;
}

Если вы выполняете, например, p = 0;, вы бы изменили только указатель, а не значение, на которое указывает.

Первый элемент исходного вектора a, который, скажем, объявлен в main, также модифицируется, верно?Поскольку векторы можно рассматривать как указатели

Это не «вектор» (массив), это указатель, даже если он выглядит как массив.Это большое значение C.

Но, действительно, a[0] = 0; изменяет первое значение (в main), указанное параметром.

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