在C中使用**时的差异

Chi*_*hak 45 c arrays syntax pointers

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

int ** arr = NULL;
Run Code Online (Sandbox Code Playgroud)

我怎么知道:

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

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

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

是不是都一样int **


针对同一问题的另一个问题:

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

Sto*_*ica 44

是不是都一样int **

您刚刚发现了类型系统中可能被视为缺陷的内容.您指定的每个选项都可以为true.它基本上源自程序存储器的平面视图,其中单个地址可用于引用各种逻辑存储器布局.

自C诞生以来,C程序员一直在处理这个问题的方法是制定一个约定.例如,对接受这种指针的函数要求大小参数,并记录它们对存储器布局的假设.或要求使用特殊值终止数组,从而允许指向缓冲区的"锯齿状"缓冲区.


我觉得有一定的澄清是有道理的.正如你在这里查看其他非常好的答案时所看到的那样,数组肯定不是指针.然而,他们确实在足够的背景下腐烂成一个,以保证在他们的教学中犯了几十年的错误(但我离题了).

我最初写的是指代码如下:

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);
Run Code Online (Sandbox Code Playgroud)

假设func每个代码片段都在一个单独的翻译单元中编译.每个例子(禁止我的任何拼写错误)都是有效的C.当作为参数传递时,数组将衰减为"指向指针的指针".怎么定义func知道它究竟是从它的参数类型传递到底是什么!?答案是它不能.静态类型p_buffint**,但它仍然允许func间接访问(部分)具有非常不同的有效类型的对象.

  • "*你指定的每个选项都是真的.*"好吧,好吧,不.只有一个:"*指针指向`int`*"的指针. (29认同)
  • @Lundin - 一个指针数组将衰减为指向指针的指针.本质上,它指向数组的第一个元素,根据标准,它与整个数组共享一个地址.哎呀,索引甚至不能在数组上完成(它必须首先衰减到指针).该标准竭尽全力将两者尽可能地混为一谈.那有什么可辨别的区别呢? (12认同)
  • 实际上它不能,因为指针指针不是与数组指针兼容的类型.然而,它可以用于通过查找表来模拟2D阵列,这在大多数情况下是有问题的练习. (7认同)
  • @KeineLust:1)如果它是兼容的,对于`int**p,*a [10];``sizeof(p)== sizeof(a)`将保证为真.它不是!2)`int*a [10]`是**指针的数组**,而不是数组指针(请注意,在英语中你通常省略`-`). (3认同)
  • @Olaf - 那是非常光顾的.我无处误导.我没有在任何时候将指针与数组指针混淆,并且通过回答我在问题中认为重要的东西来约束,所以我觉得没有义务将许多提到的数组和指针的标准拖入到这个中.如果您不同意,您的批评已被注意到,您可以自由发布自己的答案. (3认同)
  • @StoryTeller:那好吧.我把它作为整个评论.不是作为一个修饰,但你的初步评论读作好像你认为一些小的错误是可以接受的.从我链接的新问题,您可以看到它们不是.编译器**是最差的挑选者,所以在这里也是最正确的. (3认同)
  • "指针数组将衰减为指向指针的指针" - 没有.对于3个特定的运营商而言,它不会衰减. (2认同)
  • @they也不可互换.只是放弃这个想法,它只是让理解指针和数组变得更加复杂.专注于一个数组衰减除了3个运算符以外的所有运算符到第一个元素的指针这一事实.这包括索引运算符 (2认同)
  • @Olaf*"对于3个特定的运算符,它不会衰减."* - 你肯定是正确的,但是必须将该限定符拖入*每次提及衰减为指针的数组都是非常麻烦的,即使在尝试精确时也是如此.我的回答有目的地集中在将缓冲区传递给函数,以及我对索引的评论,因为衰变肯定发生在那里. (2认同)
  • @StoryTeller:当用C语言设计数组和指针时,没有人想到堆栈溢出.这并不能证明给出不完整或误导性的答案.特别是如果你考虑在函数中获取`sizeof`和"array"参数时会产生多少错误.就像我写这篇文章一样,出现了一个新的:http://stackoverflow.com/questions/42069055/chararray-size-and-length-are-not-the-same (2认同)
  • @StoryTeller:如果你的回答严重错误,那我就把它击落了,我没有.然而,只有第一种选择**确实是**.请随意为另一方提供正确的答案,以便"光顾". (2认同)
  • @Olaf - 我会用它来假设英语不是我们任何一个人的第一语言,并且难以辨别出对在线书面交流的意图,但*我提到的*是*"没有人有堆栈溢出的想法当数组和指针是用C语言设计的时候."*,而不是你提供的信息.您的送货是光顾,而不是其他答案. (2认同)

Ant*_*ala 27

声明int **arr说:"将arr声明为指向整数的指针".它(如果有效)指向指向单个整数对象(如果有效)的单个指针.由于可以将指针算法与任一间接级别(即与之*arr相同arr[0]**arr相同arr[0][0])一起使用,因此该对象可用于访问您问题中的任何一个(即,第二个,访问数组)指向整数的指针,第三,指向整数数组的第一个元素的指针数组),前提是指针指向数组的第一个元素...


然而,arr仍然被声明为指向单个整数对象的单个指针的指针.也可以声明指向已定义维度数组的指针.这里声明为指向10个整数数组的10元素指针数组的指针:a

cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
int (*(*a)[10])[10]
Run Code Online (Sandbox Code Playgroud)

实际上,数组指针最常用于将常量维的多维数组传递给函数,以及传入可变长度数组.将变量声明为指向数组的指针的语法很少见,因为无论何时将它们传递给函数,使用"未定义大小的数组"类型的参数更容易,因此不是声明

void func(int (*a)[10]);
Run Code Online (Sandbox Code Playgroud)

一个人可以用

void func(int a[][10])
Run Code Online (Sandbox Code Playgroud)

传入10个整数数组的多维数组.或者,a typedef可用于减轻头痛.

  • 指向数组的指针并不晦涩难懂.它们是传递多维数组的唯一方法. (3认同)

Lun*_*din 9

我怎么知道:

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

它始终是指向整数的指针.

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

它永远不会那样.指向整数指针数组的指针将声明如下:

int* (*arr)[n]
Run Code Online (Sandbox Code Playgroud)

听起来好像你被int**糟糕的老师/书籍/教程欺骗了.它几乎总是不正确的做法,因为解释这里这里(约数组指针详细的说明)这里.

编辑

最后写了一篇详细的文章,解释了什么是数组,查找表是什么,后者为什么不好以及你应该使用什么:正确分配多维数组.

  • "它永远不会那样"和"这是不正确的做法"是非常不同的事情.如果你是*编写*代码,它们几乎相同; 如果你正在阅读*代码,你需要知道*编写代码的人可能做错了*. (4认同)
  • 指向整数指针的数组[n]的指针是`int*(*arr)[n]` (3认同)
  • btw:声明不会将arr声明为指向n个整数数组的指针而不是指向整数指针数组的指针 (2认同)
  • @MooingDuck不,它是指向指针的指针.即指向指针数组的第一个元素的指针.你的例子工作的唯一原因是因为数组衰减.`int**arr =&t;`会给你一个编译器错误. (2认同)

Ste*_*ner 8

只有变量的声明,你无法区分这三种情况.人们仍然可以讨论是否应该使用类似于int *x[10]表达10个指向int或其他内容的数组的东西; 但是int **x- 由于指针算术,可以以三种不同的方式使用,每种方式假设不同的内存布局,并且(好)有机会做出错误的假设.

考虑以下示例,其中a int **以三种不同的方式使用,即p2p2i_v1作为指向(单个)int p2p2i_v2的指针的指针,作为指向int 的指针数组的指针,以及p2p2i_v3指向指向数组的指针的指针.整数.请注意,您无法仅根据类型来区分这三种含义,即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;
Run Code Online (Sandbox Code Playgroud)


Mag*_*sch 7

我怎么知道:

arr是指向整数指针的指针

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

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

你不能.它可以是任何一种.它最终取决于你如何分配/使用它.

因此,如果您使用这些代码编写代码,请记录您正在使用它们执行的操作,使用它们将大小参数传递给函数,并且通常在使用之前确定您分配的内容.


Vla*_*cow 6

无论指针指向单个对象还是指向数组元素的对象,指针都不会保留信息.此外,对于指针运算,单个对象被认为是由一个元素组成的数组.

考虑这些声明

int a;
int a1[1];
int a2[10];

int *p;

p = &a;
//...
p = a1;
//...
p = a2;
Run Code Online (Sandbox Code Playgroud)

在此示例中,指针p处理地址.它不知道它存储的地址是指向单个对象,a还是指向a1只有一个元素的数组的第一个元素,或者指向a2具有十个元素的数组的第一个元素.


438*_*427 6

的类型

int ** arr;
Run Code Online (Sandbox Code Playgroud)

只有一个有效的解释.它是:

arr is a pointer to a pointer to an integer
Run Code Online (Sandbox Code Playgroud)

如果你没有比上面的声明更多的信息,那就是你可以知道的所有信息,即如果arr可能已初始化,它指向另一个指针,如果可能已经初始化,则指向一个整数.

假设正确初始化,唯一保证使用它的有效方法是:

**arr = 42;
int a = **arr;
Run Code Online (Sandbox Code Playgroud)

但是,C允许您以多种方式使用它.

•arr可以用作指向整数指针的指针(即基本情况)

int a = **arr;
Run Code Online (Sandbox Code Playgroud)

•arr可用作指向整数数组的指针

int a = (*arr)[4];
Run Code Online (Sandbox Code Playgroud)

•arr可用作指向整数指针数组的指针

int a = *(arr[4]);
Run Code Online (Sandbox Code Playgroud)

•arr可用作指向整数数组指针数组的指针

int a = arr[4][4];
Run Code Online (Sandbox Code Playgroud)

在最后三种情况下,它看起来好像你有一个数组.但是,类型不是数组.类型总是正确的a pointer to a pointer to an integer- 解除引用是指针算术.它不像2D数组.

要知道哪个对手头的程序有效,您需要查看代码初始化arr.

更新

对于问题的更新部分:

如果你有:

void foo(char** x) { .... };
Run Code Online (Sandbox Code Playgroud)

唯一确定的是,它**x会给出一个char并*x给你一个char指针(在这两种情况下x都假定正确的初始化).

如果你想以x另一种方式使用,例如x[2]获取第三个char指针,则需要调用者已初始化,x以便它指向一个至少有3个连续char指针的内存区域.这可以被描述为呼叫合同foo.