2009-04-17 116 views
37

什么时候使用任何语言的指针都需要有人使用多个指针,比方说一个三重指针。什么时候使用三指针而不是仅使用常规指针有意义?用于多级指针取消引用?

例如:

char * * *ptr; 

代替

char *ptr; 
+19

这是一个非常_indirect_的问题。 – 2010-07-23 20:39:46

+5

要成为http://c2.com/cgi/wiki?ThreeStarProgrammer – 2011-06-14 22:28:10

+0

您可能会喜欢[cdecl](http://cdecl.org/) – bobobobo 2011-08-17 23:05:20

回答

57

每个星应当被理解为这样

char *foo; 

是“字符,其由指针FOO指向”“其由指针指向”。但是,

char *** foo; 

是“指向指向指向foo的指针的指针指向的char”。因此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); 
4

N维动态分配的阵列,其中N> 3,需要在C.

+0

此外,当将二维数组传递给将修改它。如果你有一个double ** ptr,你可以将它作为fn(&ptr)传递,函数fn()将有参数fn(double *** ptr)。 – 2009-04-17 01:29:51

+0

这是一个有点误导,因为myarray [0] [1] [2]通常不是实际上3个指针间接(虽然它可能)。内存通常是连续的,编译器为你做算术。 – 2009-04-17 01:50:10

+0

上述编辑是否解决您的疑虑?也就是说,我们可以同意动态分配的3维数组需要解引用3+指针吗? – 2009-04-17 02:00:05

1
int main(int argc, char** argv); 
三个或更多个间接
3

双指针的标准用法,例如:myStruct ** ptrptr,作为指针的指针。例如,作为函数参数,这允许您更改调用方指向的实际结构,而不是仅能够更改该结构中的值。

0

如果您必须修改函数内的指针,您必须传递对它的引用。

5

指针只是一个保存内存地址的变量。

因此,当你想要保存一个指针变量的地址时,你使用了一个指针指针。

如果你想返回一个指针,并且你已经使用返回变量的东西,你会传入一个指针的地址。然后该函数将该指针取消引用,以便它可以设置指针值。即该函数的参数将是指向指针的指针。

多级间接也用于多维数组。如果你想返回一个二维数组,你可以使用一个三重指针。在使用它们进行多维数组时,尽管要仔细地在每个间接层次上进行适当的转换。

下面是通过参数返回一个指针值的例子:

//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")); 
0

这是有道理的使用指针的指针,每当指针实际上指向一个指针(这个链是无限的,因此“三指针”等是可能的)。

创建此类代码的原因是因为您希望编译器/解释器能够正确检查您正在使用的类型(防止神秘错误)。

您不必使用这种类型 - 只要您需要实际取消引用指针并访问指针指向的数据,您总是可以简单地使用简单的“void *”和类型转换。但这通常是不好的做法,容易出错 - 当然有些情况下使用void *实际上是好的,并且使代码更加优雅。想想它更像你的最后一招。

=>它主要用于帮助编译器确保事物按照它们应该被使用的方式使用。

1

函数,它们封装资源的创作经常使用双指针。也就是说,你传递一个指向资源的地址。该函数然后可以创建有问题的资源,并将指针设置为指向它。这是唯一可能的,如果它有问题的指针的地址,所以它必须是一个双指针。

0

说实话,我很少见过三重指针。

我瞥了一下google代码搜索,还有someexamples,但不是很明显。 (见末尾的链接 - SO不喜欢它们)

正如其他人所提到的,你会不时看到双指针。普通的单指针是有用的,因为它们指向一些分配的资源。双指针很有用,因为您可以将它们传递给一个函数,并使函数为您填充“普通”指针。

这听起来像也许你需要一些关于什么指针和它们是如何工作的解释? 如果你还没有,你首先需要明白。

但是,这是一个separatequestion(:

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

2

您可以使用额外的间接水平 - 或指向 - 在必要的时候,不是因为它会很有趣你很少看到三倍。指针;我不认为我曾经见过一个四重指针(如果我这样做,我的脑海里会浮现)

状态表可以用一个二维数组来表示适当的数据类型(例如指向结构的指针)。当我编写一些几乎泛型的代码来执行状态表时,我记得有一个函数带了一个三重指针 - 它代表了一个指向结构的二维数组。哎哟!

0

C++中很少使用指向指针的指针。它们主要有两种用途。

第一个用途是传递一个数组。例如,char**是指向char的指针,它通常用于传递字符串数组。指向数组的指针不能正常工作,但这是一个不同的主题(如果您想了解更多信息,请参阅comp.lang.c FAQ)。在极少数情况下,您可能会看到第三个*用于阵列数组,但将所有内容存储在一个连续数组中并手动将其索引(例如array[x+y*width]而不是array[x][y])会更有效。然而,在C++中,由于容器类的原因,这种情况非常少见。

第二种用途是通过引用。参数int*允许函数修改调用函数指向的整数,并且通常用于提供多个返回值。这种通过引用传递参数以允许多个返回的模式仍然存在于C++中,但是与其他使用传递引用一样,它通常被引入实际引用所取代。通过引用传递的另一个原因 - 避免复杂的构造 - 也可以用C++引用。

C++有第三个因素减少多个指针的使用:它有string。对字符串的引用可能采用C语言中的char**类型,以便该函数可以更改它传递的字符串变量的地址,但在C++中,我们通常会看到string&

0

当您使用嵌套动态分配(或指针链接)的数据结构。这些东西都是通过指针链接的。

3

Char *** foo可以解释为指向二维字符串数组的指针。

4

ImageMagicks魔杖有被声明为

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

I am not making this up功能。如果你想创建一个对象

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

,你会做这样的事情:

10

如果您在C“对象”的工作,你可能有这样的

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

我们”因为struct参数是按值传递的,所以我们需要使用一个对象。 (另外:Objective-C中,对于C面向对象的包装器语言,使用内部但明显指针struct峰)

如果我需要存储多个对象,我使用的数组:

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

我想说是你可以想象疯狂的应用程序的多个indirections。如果多指针是一个好主意,并且您决定使用它们,请确保您使用typedef

用C不积极地使用基于类型的别名分析的单线程方言特别(这是否交计数作为一个SevenStarDeveloper申请?)

0

,它有时是有用的写内存管理器这可以适应可重新定位的对象。应用程序不是直接将指针指向内存块,而是接收指向处理描述符表的指针,每个指针都包含指向实际内存块的指针以及指示其大小的单词。如果人们需要一种用于struct woozle分配空间,可以说:

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

,然后访问(有点笨拙在C语法 - 语法是 帕斯卡清洁器):(* my_woozle) - > someField = 23 ;重要的是应用程序不是 保持直接指向任何句柄的目标,通过调用 分配内存的函数,但如果只有一个指针指向由句柄标识的每个块 ,则内存管理器将能够在 之间移动内容案件碎片将成为一个问题。

的方法并不在C方言积极 追求型为主的混叠工作近也,由于NewHandle返回的指针不 识别struct woozle*类型的指针,而是标识类型的指针 void*,甚至在那些指针类型将具有 相同表示的平台上,标准并不要求实现 将指针转换为它应该预期可能发生别名 的指示。

0

双重间接简化了许多树平衡算法,通常人们希望能够有效地将子树与其父级“断开连接”。举例来说,一个AVL树的实现可能是:

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

没有“双指针”,我们将不得不做一些更复杂,就像明确地跟踪一个节点的父节点和它是否是一个左或右分支。