Использует для нескольких уровней разыменования указателя? - PullRequest
42 голосов
/ 17 апреля 2009

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

Например:

char  * * *ptr;

вместо

char *ptr;

Ответы [ 17 ]

57 голосов
/ 17 апреля 2009

каждая звезда должна читаться как "на которую указывает указатель", поэтому

char *foo;

- это символ, на который указывает указатель foo. Тем не менее

char *** foo;

- это "char, на который указывает указатель, который указывает на указатель, который указывает на указатель foo". Таким образом, foo является указателем. По этому адресу находится второй указатель. По указанному адресу указан третий указатель. Разыменование третьего указателя приводит к символу. Если это все, что нужно, трудно сделать много для этого.

Тем не менее, можно сделать полезную работу. Представьте, что мы пишем замену bash или какой-либо другой программы управления процессами. Мы хотим управлять вызовами наших процессов объектно-ориентированным способом ...

struct invocation {
    char* command; // command to invoke the subprocess
    char* path; // path to executable
    char** env; // environment variables passed to the subprocess
    ...
}

Но мы хотим сделать что-то необычное. Мы хотим иметь возможность просматривать все различные наборы переменных среды, которые видны для каждого подпроцесса. для этого мы собираем каждый набор env членов из экземпляров вызова в массив env_list и передаем его в функцию, которая занимается этим:

void browse_env(size_t envc, char*** env_list);
11 голосов
/ 27 декабря 2012

Если вы работаете с «объектами» в C, у вас, вероятно, есть это:

struct customer {
    char *name;
    char *address;
    int id;
} typedef Customer;

Если вы хотите создать объект, вы должны сделать что-то вроде этого:

Customer *customer = malloc(sizeof Customer);
// Initialise state.

Мы используем указатель на struct здесь, потому что struct аргументы передаются по значению, и нам нужно работать с одним объектом. (Также: Objective-C, объектно-ориентированный язык-обертка для C, использует внутренние, но визуально указатели на struct s.)

Если мне нужно сохранить несколько объектов, я использую массив:

Customer **customers = malloc(sizeof(Customer *) * 10);
int customerCount = 0;

Поскольку переменная массива в C указывает на первый элемент, я снова использую указатель ... Теперь у меня есть двойные указатели.

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

int filterRegisteredCustomers(Customer **unfilteredCustomers, Customer ***filteredCustomers, int unfilteredCount, int *filteredCount);

Функция принимает массив клиентов и возвращает ссылку на массив клиентов (которые являются указателями на struct). Он также принимает количество клиентов и возвращает количество отфильтрованных клиентов (опять же, аргумент по ссылке).

Я могу назвать это так:

Customer **result, int n = 0;
int errorCode = filterRegisteredCustomers(customers, &result, customerCount, &n);

Я мог бы продолжать воображать больше ситуаций ... Это без typedef:

int fetchCustomerMatrix(struct customer ****outMatrix, int *rows, int *columns);

Очевидно, я был бы ужасным и / или садистским разработчиком, если бы оставил это так. Итак, используя:

typedef Customer *CustomerArray;
typedef CustomerArray *CustomerMatrix;

Я могу просто сделать это:

int fetchCustomerMatrix(CustomerMatrix *outMatrix, int *rows, int *columns);

Если ваше приложение используется в отеле, где вы используете матрицу для уровня, вам, вероятно, понадобится массив для матрицы:

int fetchHotel(struct customer *****hotel, int *rows, int *columns, int *levels);

Или просто так:

typedef CustomerMatrix *Hotel;
int fetchHotel(Hotel *hotel, int *rows, int *columns, int *levels);

Не заставляйте меня даже начинать на множестве отелей:

int fetchHotels(struct customer ******hotels, int *rows, int *columns, int *levels, int *hotels);

… в виде матрицы (какая-то крупная гостиничная корпорация?):

int fetchHotelMatrix(struct customer *******hotelMatrix, int *rows, int *columns, int *levels, int *hotelRows, int *hotelColumns);

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

(Этот пост считается приложением для SevenStarDeveloper ?)

5 голосов
/ 17 февраля 2011

Жезл ImageMagicks имеет функцию, которая объявлена ​​как

WandExport char* * * * * * DrawGetVectorGraphics    (  const DrawingWand  *) 

Я не придумываю это .

5 голосов
/ 17 апреля 2009

Указатель - это просто переменная, которая содержит адрес памяти.

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

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

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

Вот пример возврата значения указателя через параметр:

//Not a very useful example, but shows what I mean...
bool getOffsetBy3Pointer(const char *pInput, char **pOutput)
{
  *pOutput = pInput + 3;
  return true;
}

И вы вызываете эту функцию так:

const char *p = "hi you";
char *pYou;
bool bSuccess = getOffsetBy3Pointer(p, &pYou);
assert(!stricmp(pYou, "you"));
4 голосов
/ 17 апреля 2009

N-мерные динамически распределяемые массивы, где N> 3, требуют трех или более уровней косвенности в C.

3 голосов
/ 23 октября 2010

Char *** foo можно интерпретировать как указатель на двумерный массив строк.

3 голосов
/ 17 апреля 2009

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

2 голосов
/ 17 апреля 2009

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

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

1 голос
/ 17 апреля 2009
 int main( int argc, char** argv );
1 голос
/ 17 апреля 2009

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

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