我最近开始学习C语言,但理解指针语法有问题,例如当我写下以下代码行:
int ** arr = NULL;
如何判断:
arr 是一个整数指针的指针
arr 是一个指向整数指针数组的指针
arr 是一个指向整数数组指针数组的指针
这些不都是 int **
吗?
同一问题的另一个问题:
如果我有一个函数接收 char ** s
作为参数,我想把它作为字符串数组的指针引用,意思是指向字符指针数组的数组指针,但它也是指向 char
的指针吗?
不是都是
int **
吗?
你刚刚发现了类型系统中可能存在的缺陷。你所提到的每个选项都有可能是正确的。这实际上源自程序内存的扁平视图,其中单个地址可以用于引用各种逻辑内存布局。
C程序员自C诞生以来一直采用的方法是制定公约。例如,对于接受此类指针的函数要求size参数,并记录有关内存布局的假设。或者要求使用特殊值终止数组,从而允许指向缓冲区的指针的“锯齿形”缓冲区。
我觉得需要进行一定的澄清。当您查阅其他很好的答案时,您会发现数组绝对不是指针。但在足够的上下文中,它们确实会衰减为指针,这已经导致教学方面存在几十年的错误(但我跑题了)。
我最初写的是以下代码:
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语言。当作为参数传递时,这些数组将衰减为“指向指针”的形式。从其参数类型的定义上来看, func
如何知道它究竟传递了什么?答案是它不知道。 p_buff
的静态类型是 int **
,但它仍然允许 func
间接访问具有极不同的有效类型(部分)对象。pointer to pointer to int
)。 - alkint **p,*a [10];
, sizeof(p) == sizeof(a)
将保证为真。但事实并非如此!2)int *a[10]
是指针数组,而不是指向数组的指针(请注意,在英语中通常省略“-”)。 - too honest for this site声明 int **arr
表示:"将 arr 声明为指向整数的指针的指针"。如果有效,它指向一个指向单个整数对象的单个指针。由于可以使用任一间接级别进行指针算术运算(即*arr
等同于 arr[0]
,**arr
等同于arr[0][0]
),因此该对象可用于访问您所提问的3种情况中的任何一种(即,对于第二种情况,访问指向整数的指针数组,并且对于第三种情况,访问指向整数数组的第一个元素的指针数组),前提是这些指针指向数组的第一个元素...
然而,arr
仍然被声明为指向单个指向单个整数对象的指针。还可以声明指向定义维度的数组的指针。在这里,a
被声明为指向具有10个元素的指针数组的10元素数组的指针:
cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
int (*(*a)[10])[10]
在实践中,数组指针最常用于将多维常量大小的数组传递到函数中,并且用于传递可变长度数组。声明变量为指向数组的指针的语法很少见,因为每当它们被传递到函数中时,使用"未定义大小的数组"类型的参数会更容易一些,所以不需要声明。
void func(int (*a)[10]);
可以使用
void func(int a[][10])
可以传递一个包含多个包含10个整数的数组的多维数组。或者,可以使用typedef
减轻头痛。
struct {length,pointer to data}
或指向后者的struct
的指针。 - too honest for this siteint* (*arr)[n]
int**
几乎总是不正确的做法,详见这里和这里以及(有关数组指针的详细解释)这里。int *(*arr)[n]
。 - Antti Haapala -- Слава Україніint ** arr = &t;
会给你一个编译器错误。 - Lundinint *x[10]
之类的语句来表示一个指向整数的十个指针数组,但是 int **x
可以 - 由于指针算术运算,以三种不同的方式使用,每种方式都假设不同的内存布局,并有可能产生错误的假设。int **
以三种不同的方式使用,即 p2p2i_v1
作为指向单个 int 的指针的指针,p2p2i_v2
作为指向整数指针数组的指针的数组,以及 p2p2i_v3
作为指向整数数组的指针的指针。请注意,您不能仅通过类型区分这三个含义,对它们中的任何一个错误访问都会产生不可预测的结果,除了访问第一个元素: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;
*p2p2i_v3[0]
产生了 5,而不是您评论中所建议的 1。 - Peter - Reinstate Monica指针不会保留它们指向单个对象还是数组中的对象的信息。此外,在指针算术中,单个对象被视为由一个元素组成的数组。
考虑以下声明:
int a;
int a1[1];
int a2[10];
int *p;
p = &a;
//...
p = a1;
//...
p = a2;
p
处理的是地址。它不知道它存储的地址是指向像 a
这样的单个对象,还是指向只有一个元素的数组 a1
的第一个元素,或者是指向具有十个元素的数组 a2
的第一个元素。这种类型的
int ** arr;
arr is a pointer to a pointer to an integer
arr
可能已经初始化了,它指向另一个指针,而这个指针如果可能已经初始化,则指向一个整数。**arr = 42;
int a = **arr;
int a = **arr;
• arr可以用作指向整数数组的指针的指针。
int a = (*arr)[4];
int a = *(arr[4]);
• arr可以用作指向整数数组的指针的数组的指针
int a = arr[4][4];
指向整数的指针的指针
- 解引用是指针算术。它与2D数组完全不同。arr
的代码。
更新
关于问题的更新部分:void foo(char** x) { .... };
**x
将提供一个char字符,而*x
将提供一个char指针(在这两种情况下,假设x
已经被正确初始化)。x
,例如使用x[2]
来获取第三个char指针,那么调用者需要初始化x
,使其指向至少有3个连续char指针的内存区域。这可以描述为调用foo
的契约。在使用指针时,有一个技巧是从右往左阅读:
int** arr = NULL;
你得到的是:arr
、*
、*
、int
,因此数组是指向指向整数的指针。
int **arr;
和 int** arr;
是一样的。
int s[2] = {}; int* t[3] = {s. s. s}; int ** arr = t;
看看这个!arr
是 _指向整数数组指针的数组指针_! - Mooing Duckarr
是一个指向指针的指针。表达式"t
"从指向int类型的指针数组衰变为指向指针的指针数组,这样一个指向t
的第一个元素的值被赋给了arr
。你正在将“数组指针”定义为“指向数组第一个元素的指针”。但是“数组指针”已经作为C类型有了意义(没有值),而且它不是那个意思。 - philipxyC语言语法很有逻辑性。在声明中,标识符前面的星号表示该变量类型的指针,两个星号表示该变量类型的指向指针的指针。
在这种情况下, arr
是指向整数类型指针的指针
。
双重指针有几种用法。例如,您可以使用指向指针向量的指针来表示矩阵。该向量中的每个指针都指向矩阵本身的行。
还可以使用它创建二维数组,如下所示:
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
数组的指针看起来像这样:int (*p)[42]
。这是一个指向int[42]
数组的指针。在这里,int * p
仍然是指向int
的指针,没有更多或更少。无论p
是否指向int
数组的第一个元素取决于上下文,但仍然不会使p
指向数组,而只会指向其第一个元素。 - alkNULL
,那么它就不指向任何东西,因此它“指向零个整数”。 - underscore_dmalloc
会通过返回一个有效指针来满足对零字节内存的请求,但该指针将指向零个整数。 - Paul Ogilvieint ** arr = NULL;
这句话告诉编译器:arr是一个整数的双指针
,并将其赋值为NULL
。
int ** arr
中,只有一个陈述是正确的:"arr是指向整型指针的指针",另外两个则是错误的。 - alkint **p
(指向指针)和int (*p)[3]
(指向长度为3的数组的指针)之间的区别:http://stackoverflow.com/questions/25080413/difference-between-pointer-to-pointer-and-pointer-to-2d-array - Klas Lindbäckint * a
指向单个整数 - 总是如此。虽然您可以使用指针算术来访问连续布局的整数,但这并不改变int * a
是指向单个整数的指针的事实。它永远不会是指向数组的指针。 - Support Ukrainea
不是一个数组 - 它是指向 int 的指针。当你想动态分配一个二维数组时,差异变得更加明显。在这里,你很快会发现int **a
并不像int a[4][4]
。 - Support Ukraineint (*)[N]
。这并非小题大做,而是精确明确。在这里不准确只会增加混淆,而你对术语的更改并没有起到任何帮助。如果正确理解,这些注释非常有用。 - too honest for this site