Подтвердить что ты не робот

Используется для нескольких уровней разметки указателя?

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

Например:

char  * * *ptr;

вместо

char *ptr;
4b9b3361

Ответ 1

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

char *foo;

is "char, на который указывает указатель foo". Однако

char *** foo;

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

Тем не менее, по-прежнему можно сделать какую-то полезную работу. Представьте, что мы пишем замену для 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);

Ответ 2

Если вы работаете с "объектами" в 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 указывает на первый элемент, я снова использую указатель.... Теперь у меня есть двойные указатели.

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

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?)

Ответ 3

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

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

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

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

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

//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

ImageMagicks Wand имеет функцию, объявленную как

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

Я не делаю этого.

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

 int main( int argc, char** argv );

Ответ 10

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

Ответ 11

Если вам нужно изменить указатель внутри функции, вы должны передать ссылку на нее.

Ответ 12

Имеет смысл использовать указатель на указатель всякий раз, когда указатель на самом деле указывает на указатель (эта цепочка неограничена, следовательно, возможны "тройные указатели" и т.д.).

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

Вам не нужно использовать такие типы - вы всегда можете просто использовать простые "void *" и typecast всякий раз, когда вам нужно реально разыменовать указатель и получить доступ к данным, на которые указывает указатель. Но это, как правило, плохая практика и склонны к ошибкам - конечно, есть случаи, когда использование void * действительно хорошо и делает код намного более элегантным. Подумайте об этом скорее как о вашем последнем курорте.

= > В основном это помогает компилятору убедиться, что все используется так, как они должны использоваться.

Ответ 13

Честно говоря, я редко видел тройной указатель.

Я взглянул на поиск кода Google, и есть some примеры, но не очень освещаются. (см. ссылки в конце - SO не нравится)

Как уже упоминалось, двойные указатели, которые вы будете видеть время от времени. Обычные одиночные указатели полезны, потому что они указывают на некоторый выделенный ресурс. Двойные указатели полезны, потому что вы можете передать их функции, а функция заполнить "простой" указатель для вас.

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

Но это отдельный question (:

http://www.google.com/codesearch/p?hl=en#e_ObwTAVPyo/security/nss/lib/ckfw/capi/ckcapi.h&q=***%20lang:c&l=301

http://www.google.com/codesearch/p?hl=en#eVvq2YWVpsY/openssl-0.9.8e/crypto/ec/ec_mult.c&q=***%20lang:c&l=344

Ответ 14

Указатели на указатели редко используются в С++. Они в основном имеют два вида использования.

Первое использование - передать массив. char**, например, является указателем на указатель на char, который часто используется для передачи массива строк. Указатели на массивы не работают по уважительным причинам, но это другая тема (см. comp.lang.c FAQ, если вы хотите знать Больше). В некоторых редких случаях вы можете увидеть третий *, используемый для массива массивов, но обычно более эффективно хранить все в одном непрерывном массиве и индексировать его вручную (например, array[x+y*width], а не array[x][y]). Однако в С++ это гораздо менее распространено из-за классов контейнеров.

Второе использование - передать по ссылке. Параметр int* позволяет функции изменять целое число, на которое указывает вызывающая функция, и обычно используется для предоставления нескольких возвращаемых значений. Эта схема передачи параметров по ссылке, позволяющей несколько возвратов, по-прежнему присутствует на С++, но, как и другие применения pass-by-reference, обычно заменяется введением фактических ссылок. Другая причина для передачи по ссылке - избегая копирования сложных конструкций - также возможна с помощью ссылки С++.

С++ имеет третий фактор, который уменьшает использование нескольких указателей: он имеет string. Ссылка на строку может принимать тип char** в C, так что функция может изменить адрес строковой переменной, которую она передала, но на С++ обычно мы видим string&.

Ответ 15

При использовании вложенных динамически распределенных (или связанных с указателем) структур данных. Все эти вещи связаны указателями.

Ответ 16

В частности, в однопоточных диалектах C, которые не настойчиво используют анализ сглаживания на основе типов, иногда бывает полезно написать менеджеров памяти, которые могут размещать перемещаемые объекты. Вместо того чтобы давать приложениям прямые указатели на куски памяти, приложение получает указатели в таблицу дескрипторов дескрипторов, каждая из которых содержит указатель на фактический кусок памяти вместе со словом, обозначающим его размер. Если нужно выделить пространство для struct woozle, можно сказать:

struct woozle **my_woozle = newHandle(sizeof struct woozle);

а затем доступ (несколько неудобно в синтаксисе Си - синтаксис чище в Паскаль): (* my_woozle) → someField = 23; важно, чтобы приложения не сохранять прямые указатели на любой дескриптор цели по вызовам функций, которые выделять память, но если существует только один указатель на каждый блок идентифицированный дескриптором, менеджер памяти сможет перемещать вещи вокруг в случае, если фрагментация станет проблемой.

Подход не работает почти так же на диалектах C, которые агрессивно преследуйте псевдонимы на основе типов, поскольку указатель, возвращаемый NewHandle, не идентифицировать указатель типа struct woozle*, но вместо этого указывает указатель типа void* и даже на платформах, где эти типы указателей имели бы то же представление, что стандарт не требует, чтобы реализации интерпретировать перевод указателя как указание на то, что он должен ожидать, что сглаживание может произойти.

Ответ 17

Двойная косвенность упрощает многие алгоритмы балансировки дерева, где обычно нужно иметь возможность эффективно "отвязывать" поддерево от своего родителя. Например, реализация дерева AVL может использовать:

void rotateLeft(struct tree **tree) {
    struct tree *t = *tree,
                *r = t->right,
                *rl = r->left;
    *tree = r;
    r->left = t;
    t->right = rl;
}

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