在C中使用**时的差异
我最近开始学习C语言,而且我在理解指针语法时遇到了一些问题,例如,当我编写以下代码行时:
int ** arr = NULL;
我如何知道:
-
arr是指向整数的指针的指针
-
arr是指向整数指针数组的指针
-
arr是指向整数数组的指针数组的指针
这与int **
不是一回事吗?
同样的问题的另一个问题:
如果我有一个接收char ** s
的函数作为参数,我想把它作为一个pointer
string数组的指针,意味着指向一个char数组指针数组的指针,但它也是一个指针到一个char
的指针?
这与
int **
不是一回事吗?
你刚刚发现了types系统中可能被认为是缺陷的东西。 您指定的每个选项都可以为true。 它实质上是从一个程序存储器的平面图导出的,其中一个地址可以用来引用各种逻辑存储器布局。
自C开始以来,C程序员一直在处理这个问题,就是通过一个约定。 比如接受这些指针的函数需要大小参数,并logging他们对内存布局的假设。 或者要求数组终止一个特殊的值,从而允许指向缓冲区的“锯齿形”缓冲区。
我觉得有一定的解释是为了。 正如你在看到其他非常好的答案时所看到的那样, 数组绝对不是指针 。 然而,他们在有足够的背景下衰退成为一个人,在教导他们(但是我离题)中需要长达数十年的错误。
我原来写的代码如下:
void func(int **p_buff) { } //... int a = 0, *pa = &a; func(&pa); //... int a[3][10]; int *a_pts[3] = { a[0], a[1], a[2] }; func(a_pts); //... int **a = malloc(10 * sizeof *a); for(int i = 0; i < 10; ++i) a[i] = malloc(i * sizeof *a[i]); func(a);
假设func
和每个代码片段被编译在一个单独的翻译单元中。 每个例子(禁止我的拼写错误)是有效的C.当作为parameter passing时,数组将衰变成“指向指针”。 func
的定义是如何知道从它的参数types传递过来的呢? 答案是它不能。 p_buff
的静态types是int**
,但是它仍然允许func
间接地访问(有部分的)有效types差异很大的对象。
声明int **arr
说:“将arr声明为指向整数的指针”。 它(如果有效的话)指向指向(如果有效)单个整数对象的单个指针。 因为可以使用指针运算或者间接级别(即, *arr
与arr[0]
相同,并且**arr
与arr[0][0]
),该对象可以用于访问任何从你的问题(即第二,访问一个指针数组指针数组,第三,访问指针数组的第一个元素的整数数组),只要指针指向数组的第一个元素…
然而, arr
仍然被声明为指向单个整数对象的指针。 也可以声明一个指向定义维数组的指针。 这里的a
被声明为指向10个元素的数组的指针,指向10个整数的数组:
cdecl> declare a as pointer to array 10 of pointer to array 10 of int; int (*(*a)[10])[10]
实际上,数组指针最常用于将多维常量维数组传递到函数中,并用于传递可变长度数组。 将variables声明为数组的指针的语法很less见,因为只要将它们传递给一个函数,使用“array of undefined size”types的参数就可以更容易一些,所以不需要声明
void func(int (*a)[10]);
人们可以使用
void func(int a[][10])
传入一个由10个整数组成的multidimensional array。 或者,可以使用typedef
来减轻头痛。
我如何知道:
- arr是指向整数的指针的指针
它始终是指向整数的指针。
- arr是指向整数指针数组的指针
- arr是指向整数数组的指针数组的指针
它永远不会是这样的。 一个指向整数指针数组的指针可以这样声明:
int* (*arr)[n]
这听起来好像你被欺骗使用int**
由穷人教师/书籍/教程。 这几乎总是不正确的做法,正如这里和这里所解释的(关于数组指针的详细解释) 。
编辑
最后是写一篇详细的文章,解释数组是什么,查找表是什么,为什么后者不好,应该用什么来代替: 正确地分配multidimensional array 。
只有variables的声明,你不能区分这三种情况。 我们仍然可以讨论一下,如果不应该使用int *x[10]
来expression一个10个指向int的指针或其他东西; 但int **x
可以 – 由于指针算术,以三种不同的方式使用,每种方式假设一个不同的内存布局和(错误的)假设的(好)机会。
考虑下面的例子,其中int **
以三种不同的方式被使用,即, p2p2i_v1
作为指向(单个)int的指针的指针, p2p2i_v2
作为指向int的指针数组的指针,并且p2p2i_v3
作为一个指向int数组的指针。 请注意,您无法单独通过types来区分这三种含义,这三种含义都是int**
。 但是,如果初始化方式不同,以错误的方式访问它们中的每一个都会产生一些不可预测的结果,除非是访问第一个元素:
int i1=1,i2=2,i3=3,i4=4; int *p2i = &i1; int **p2p2i_v1 = &p2i; // pointer to a pointer to a single int int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 }; int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int int arrayOfI[4] = { 5,6,7,8 }; int *p2arrayOfi = arrayOfI; int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints // assuming a pointer to a pointer to a single int: int derefi1_v1 = *p2p2i_v1[0]; // correct; yields 1 int derefi1_v2 = *p2p2i_v2[0]; // correct; yields 1 int derefi1_v3 = *p2p2i_v3[0]; // correct; yields 5 // assuming a pointer to an array of pointers to int's int derefi1_v1_at1 = *p2p2i_v1[1]; // incorrect, yields ? or seg fault int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2 int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault // assuming a pointer to an array of pointers to an array of int's int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault; int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault; int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;
我如何知道:
arr是指向整数的指针的指针
arr是指向整数指针数组的指针
arr是指向整数数组的指针数组的指针
你不能。 它可以是任何这些。 它最终取决于你如何分配/使用它。
所以,如果你使用这些代码编写代码,logging你正在做什么,将大小parameter passing给使用它们的函数,并且通常在使用之前确定你分配的是什么。
指针不保留指向单个对象或数组元素的对象的信息。 而且,对于指针运算,单个对象被认为是由一个元素组成的数组。
考虑这些声明
int a; int a1[1]; int a2[10]; int *p; p = &a; //... p = a1; //... p = a2;
在这个例子中,指针p
处理地址。 它不知道它所存储的地址是指向单个对象(如a
还是指向仅有一个元素的数组a1
的第一个元素或指向具有十个元素的数组a2
的第一个元素。
types
int ** arr;
只有一个有效的解释。 它是:
arr is a pointer to a pointer to an integer
如果你没有比上面的声明更多的信息,那么你就可以知道,也就是说,如果arr
可能被初始化了,它就会指向另一个指针,如果可能被初始化的话,它指向一个整数。
假设正确的初始化,唯一有效的使用方法是:
**arr = 42; int a = **arr;
但是,C允许您以多种方式使用它。
•arr可以用作指向整数的指针(即基本情况)
int a = **arr;
arr可以用作指向一个整数数组的指针
int a = (*arr)[4];
•arr可以用作指向整数指针数组的指针
int a = *(arr[4]);
•arr可以用作指向整数数组指针数组的指针
int a = arr[4][4];
在最后三种情况下,它可能看起来好像你有一个数组。 但是,types不是数组。 types总是只是a pointer to a pointer to an integer
– 解引用是指针算术。 这不像是一个二维数组。
要知道哪个程序对手边有效,你需要看看初始化arr
的代码。
更新
对于问题的更新部分:
如果你有:
void foo(char** x) { .... };
你唯一知道的就是**x
会给出一个char, *x
会给你一个char指针(在这两种情况下都假定x
是正确的初始化)。
如果你想以另一种方式使用x
,例如x[2]
来获得第三个字符指针,它要求调用者初始化了x
以便它指向一个至less有3个连续字符指针的存储区域。 这可以被描述为调用foo
的合同。
C语法是合乎逻辑的。 作为声明中标识符之前的星号意味着指向variablestypes的指针,两个星号表示指向variablestypes的指针。
在这种情况下, arr
是a pointer to a pointer to integer
。
有几个双指针的用法。 例如,你可以用一个指向一个指针向量的指针来表示一个matrix。 这个向量中的每个指针指向matrix本身的行。
也可以使用它创build一个二维数组,像这样
int **arr=(int**)malloc(row*(sizeof(int*))); for(i=0;i<row;i++) { *(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. // arr[i]=(int*)malloc(sizeof(int)*col); }
使用指针有一个技巧,从右侧读到左侧:
int** arr = NULL;
你会得到什么: arr
, *
, *
, int
,所以数组是指向一个整数的指针。
和int **arr;
与int** arr;
相同int** arr;
。
int ** arr = NULL;
这是告诉编译器, arr is a double pointer of an integer
并分配NULL
值。
这里已经有了很好的答案,但是我想提一下我的“goto”网站的复杂声明: http : //cdecl.org/
访问网站,粘贴你的声明,并将其翻译成英文。
对于int ** arr;
,它说declare arr as pointer to pointer to int
。
该网站还展示了一些例子。 在他们身上testing自己,然后将鼠标hover在查看答案。
(double (^)(int , long long ))foo
将foo分块(int,long long)返回double
int (*(*foo)(void ))[3]
将foo声明为函数指针(void)返回指向int数组3的指针
它也会将英语翻译成C语言,如果你的描述是正确的,那么这个语句是非常简洁的。