我最近开始学习C,并且我在理解指针语法方面遇到了问题,例如当我写下以下行时:
int ** arr = NULL;
我怎么知道:
arr是指向整数
arr是指向整数指针数组的指针
arr是指向整数数组指针数组的指针
与int **
不一样吗?
<小时/> 针对同一问题的另一个问题是:
如果我有一个接收char ** s
作为参数的函数,我想把它称为pointer
到一个字符串数组,这意味着指向一个指向数组的指针数组的指针chars
,但它也是指向char
的指针的指针吗?
答案 0 :(得分:44)
与
int **
完全相同吗?
您刚刚发现了类型系统中可能被视为缺陷的内容。您指定的每个选项都可以为true。它主要源自程序存储器的平面视图,其中单个地址可用于引用各种逻辑存储器布局。
C程序员自C成立以来一直在处理这个问题的方法是通过制定一个约定。例如,对接受这种指针的函数要求大小参数,并记录它们对存储器布局的假设。或要求阵列以特殊值终止,从而允许&#34;锯齿状的&#34;指向缓冲区的缓冲区。
我觉得有一定程度的澄清。正如您在此处查看其他非常好的答案时所看到的那样,数组绝对不是指针。然而,它们会在足够的背景下衰变成一个,以保证在教导它们时有几十年的错误(但我离题了)。
我最初写的内容是指代码如下:
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.数组会衰减成指针指向&#34;当作为参数传递时。 func
的定义如何知道它的参数类型究竟是从哪个传递的?!答案是它不能。 p_buff
的静态类型为int**
,但它仍允许func
间接访问(部分)具有完全不同有效类型的对象。
答案 1 :(得分:27)
声明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
来减轻头痛。
答案 2 :(得分:9)
我怎么知道:
- arr是指向整数
指针的指针
它始终是指向整数的指针。
- arr是指向整数指针数组的指针
- arr是指向整数数组指针数组的指针
永远不会那样。指向整数指针数组的指针将声明如下:
int* (*arr)[n]
听起来好像你被糟糕的教师/书籍/教程欺骗使用int**
。根据{{3}}和here以及here的解释,这几乎总是不正确的做法。
修改
最后写了一篇详细的帖子,解释了什么是数组,查找表是什么,后者为什么不好以及你应该使用什么:( with detailed explanation about array pointers) here。
答案 3 :(得分:8)
仅具有变量的声明,您无法区分这三种情况。人们仍然可以讨论是否应该使用像int *x[10]
这样的东西来表达一个包含10个指针的数组或其他内容;但是int **x
可以 - 由于指针算术,可以以三种不同的方式使用,每种方式假设不同的内存布局,并且(好)有机会做出错误的假设。
考虑以下示例,其中int **
以三种不同的方式使用,即p2p2i_v1
作为指向(单个)int的指针,p2p2i_v2
作为指针指向int的指针数组,p2p2i_v3
指向指向int数组的指针。请注意,您无法仅通过类型来区分这三种含义,即所有三种类型的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;
答案 4 :(得分:7)
我怎么知道:
arr是指向整数
指针的指针arr是指向整数指针数组的指针
arr是指向整数数组指针数组的指针
你做不到。它可以是任何一种。它最终取决于你如何分配/使用它。
因此,如果您使用这些编写代码,请记录您正在使用它们的内容,使用它们将大小参数传递给函数,并且通常在使用之前确定您分配的内容。
答案 5 :(得分:6)
指针不保留信息,无论它们指向单个对象还是指向数组元素的对象。此外,对于指针运算,单个对象被认为是由一个元素组成的数组。
考虑这些声明
int a;
int a1[1];
int a2[10];
int *p;
p = &a;
//...
p = a1;
//...
p = a2;
在此示例中,指针p
处理地址。它不知道它存储的地址是指向a
这样的单个对象,还是指向只有一个元素的数组a1
的第一个元素或指向数组的第一个元素{{1}这有10个元素。
答案 6 :(得分:6)
的类型
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];
在最后三种情况下,它看起来好像你有一个数组。但是,类型不数组。类型始终只是a pointer to a pointer to an integer
- 解除引用是指针算术。它与2D阵列完全不同。
要知道哪个对手头的程序有效,您需要查看初始化arr
的代码。
<强>更新强>
对于问题的更新部分:
如果你有:
void foo(char** x) { .... };
你唯一知道的是**x
会给出一个字符,而*x
会给你一个字符指针(在这两种情况下都假定正确初始化x
)。
如果您想以其他方式使用x
,例如x[2]
要获取第三个char指针,它要求调用者初始化x
,使其指向一个至少有3个连续char指针的内存区域。这可以被描述为调用foo
的合同。
答案 7 :(得分:3)
C语法是合乎逻辑的。在声明中的标识符之前的星号表示指向变量类型的指针,两个星号表示指向变量类型的指针。
在这种情况下,arr
为a pointer to a pointer to integer
。
有两种双指针用法。例如,您可以表示一个矩阵,其中包含指向指针向量的指针。此向量中的每个指针都指向矩阵本身的行。
也可以使用它创建一个二维数组,就像这样
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); }
答案 8 :(得分:3)
使用指针时有一个技巧,从右侧到左侧读取:
int** arr = NULL;
你得到了什么:arr
,*
,*
,int
,所以数组是指向整数指针的指针。
int **arr;
与int** arr;
相同。
答案 9 :(得分:2)
Ext.define('MyApp.view.Picture', {
extend: 'Ext.window.Window',
alias: 'widget.mywindow',
requires: [
'Ext.toolbar.Toolbar',
'Ext.button.Button'
],
resizable: false,
closable: false,
modal: true,
title: 'Nice picture...',
maxHeight: 700,
autoScroll: true,
...
告诉编译器int ** arr = NULL;
并指定arr is a double pointer of an integer
值。
答案 10 :(得分:-1)
这里已经有了很好的答案,但我想提一下我的&#34; goto&#34;复杂声明的网站:http://cdecl.org/
访问该网站,粘贴您的声明,并将其翻译为英语。
int ** arr;
代表declare arr as pointer to pointer to int
。
该网站还显示了示例。在他们身上测试自己,然后将光标悬停以查看答案。
(double (^)(int , long long ))foo
将foo转换为block(int,long long)返回double
int (*(*foo)(void ))[3]
将foo声明为指向function(void)的指针,返回指向int
的数组3的指针
如果你的描述正确的话,它还会把英文翻译成C声明,这是非常好的。