在C中使用**时的差异

时间:2017-02-06 10:18:59

标签: c arrays pointers syntax

我最近开始学习C,并且我在理解指针语法方面遇到了问题,例如当我写下以下行时:

int ** arr = NULL;

我怎么知道:

  • arr是指向整数

  • 指针的指针
  • arr是指向整数指针数组的指针

  • arr是指向整数数组指针数组的指针

int **不一样吗?

<小时/> 针对同一问题的另一个问题是:

如果我有一个接收char ** s作为参数的函数,我想把它称为pointer到一个字符串数组,这意味着指向一个指向数组的指针数组的指针chars,但它也是指向char的指针的指针吗?

11 个答案:

答案 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声明为指向整数的指针”。它(如果有效)指向指向单个整数对象(如果有效)的单个指针。因为可以使用具有任一间接级别的指针算法(即*arrarr[0]相同而**arrarr[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语法是合乎逻辑的。在声明中的标识符之前的星号表示指向变量类型的指针,两个星号表示指向变量类型的指针。

在这种情况下,arra 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声明,这是非常好的。