Что означает наличие двух звездочек ** в Objective-C? - PullRequest
14 голосов
/ 07 мая 2009

Я понимаю, что одна звездочка * является указателем, что означает наличие двух **?

Я наткнулся на это из документации:

- (NSAppleEventDescriptor *)executeAndReturnError:(NSDictionary **)errorInfo

Ответы [ 5 ]

33 голосов
/ 07 мая 2009

Это указатель на указатель, как в C (на котором, несмотря на странный синтаксис в квадратных скобках, Objective-C основан):

char c;
char *pc = &c;
char **ppc = &pc;
char ***pppc = &ppc;

и т. Д. До бесконечности (или до тех пор, пока не закончится переменное пространство).

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

=====

Следуя вашему запросу на образец, который показывает, как его использовать, вот код, который я написал для другого поста, который иллюстрирует это. Это appendStr() функция, которая управляет своими собственными выделениями (вам все еще нужно освободить финальную версию). Первоначально вы устанавливаете строку (char *) в NULL, и сама функция будет выделять пространство по мере необходимости.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void appendToStr (int *sz, char **str, char *app) {
    char *newstr;
    int reqsz;

    /* If no string yet, create it with a bit of space. */

    if (*str == NULL) {
        *sz = strlen (app) + 10;
        if ((*str = malloc (*sz)) == NULL) {
            *sz = 0;
            return;
        }
        strcpy (*str, app);
        return;
    }

    /* If not enough room in string, expand it. We could use realloc
       but I've kept it as malloc/cpy/free to ensure the address
       changes (for the program output). */

    reqsz = strlen (*str) + strlen (app) + 1;
    if (reqsz > *sz) {
        *sz = reqsz + 10;
        if ((newstr = malloc (*sz)) == NULL) {
            free (*str);
            *str = NULL;
            *sz = 0;
            return;
        }
        strcpy (newstr, *str);
        free (*str);
        *str = newstr;
    }

    /* Append the desired string to the (now) long-enough buffer. */

    strcat (*str, app);
}

static void dump(int sz, char *x) {
    if (x == NULL)
        printf ("%8p   [%2d]   %3d   [%s]\n", x, sz, 0, "");
    else
        printf ("%8p   [%2d]   %3d   [%s]\n", x, sz, strlen (x), x);
}

static char *arr[] = {"Hello.", " My", " name", " is", " Pax",
                      " and"," I", " am", " old."};

int main (void) {
    int i;
    char *x = NULL;
    int sz = 0;

    printf (" Pointer   Size   Len   Value\n");
    printf (" -------   ----   ---   -----\n");
    dump (sz, x);
    for (i = 0; i < sizeof (arr) / sizeof (arr[0]); i++) {
        appendToStr (&sz, &x, arr[i]);
        dump (sz, x);
    }
}

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

 Pointer   Size   Len   Value
 -------   ----   ---   -----
# NULL pointer here since we've not yet put anything in.
     0x0   [ 0]     0   []

# The first time we put in something, we allocate space (+10 chars).
0x6701b8   [16]     6   [Hello.]
0x6701b8   [16]     9   [Hello. My]
0x6701b8   [16]    14   [Hello. My name]

# Adding " is" takes length to 17 so we need more space.
0x6701d0   [28]    17   [Hello. My name is]
0x6701d0   [28]    21   [Hello. My name is Pax]
0x6701d0   [28]    25   [Hello. My name is Pax and]
0x6701d0   [28]    27   [Hello. My name is Pax and I]

# Ditto for adding " am".
0x6701f0   [41]    30   [Hello. My name is Pax and I am]
0x6701f0   [41]    35   [Hello. My name is Pax and I am old.]

В этом случае вы передаете **str, так как вам нужно иметь возможность изменить значение *str.

=====

Или следующее, которое выполняет развернутую пузырьковую сортировку (о, позор!) Для строк, которых нет в массиве. Это происходит путем прямого обмена адресами строк.

#include <stdio.h>

static void sort (char **s1, char **s2, char **s3, char **s4, char **s5) {
    char *t;

    if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; }
    if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; }
    if (strcmp (*s3, *s4) > 0) { t = *s3; *s3 = *s4; *s4 = t; }
    if (strcmp (*s4, *s5) > 0) { t = *s4; *s4 = *s5; *s5 = t; }

    if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; }
    if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; }
    if (strcmp (*s3, *s4) > 0) { t = *s3; *s3 = *s4; *s4 = t; }

    if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; }
    if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; }

    if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; }
}

int main (int argCount, char *argVar[]) {
    char *a = "77";
    char *b = "55";
    char *c = "99";
    char *d = "88";
    char *e = "66";

    printf ("Unsorted: [%s] [%s] [%s] [%s] [%s]\n", a, b, c, d, e);
    sort (&a,&b,&c,&d,&e);
    printf ("  Sorted: [%s] [%s] [%s] [%s] [%s]\n", a, b, c, d, e);
    return 0;
}

, который производит:

Unsorted: [77] [55] [99] [88] [66]
  Sorted: [55] [66] [77] [88] [99]

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

1 голос
/ 14 апреля 2016

Указатель на указатель

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

Предположим, у нас есть указатель p1, который указывает на еще один указатель p2, который указывает на символ c. В памяти три переменные могут быть визуализированы как:

enter image description here

Итак, мы видим, что в памяти указатель p1 содержит адрес указателя p2. Указатель p2 содержит адрес символа c.

То есть p2 - указатель на символ c, а p1 - указатель на p2, или мы также можем сказать, что p2 - указатель на указатель на символ c.

Теперь в коде p2 можно объявить как:

char * p2 = & c;

Но p1 объявляется как:

char ** p1 = & p2;

Итак, мы видим, что p1 - это двойной указатель (то есть указатель на указатель на символ) и, следовательно, две * s в объявлении.

Теперь

  • p1 - это адрес p2, то есть 5000
  • *p1 - это значение, удерживаемое p2, т.е. 8000
  • **p1 - это значение в 8000, т.е. c Я думаю, что это должно прояснить концепцию, давайте возьмем небольшой пример:

Источник: http://www.thegeekstuff.com/2012/01/advanced-c-pointers/

Для некоторых случаев его использования:

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

  • Например, обработка ошибок, позволяет приемному методу контролировать, на что ссылается указатель. См этот вопрос
  • Для создания непрозрачной структуры, т. Е. Чтобы другие не могли выделять место. См этот вопрос
  • В случае расширения памяти, упомянутого в других ответах на этот вопрос.

не стесняйтесь редактировать / улучшать этот ответ, пока я учусь:]

1 голос
/ 29 октября 2013

(ссылка: больше iOS 6 разработка)

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

1 голос
/ 07 мая 2009

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

1 голос
/ 07 мая 2009

Указатель на указатель.

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