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_buff
是int**
,但它仍然允许func
间接访问(部分)具有非常不同的有效类型的对象.
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
可用于减轻头痛.
我怎么知道:
- arr是指向整数指针的指针
它始终是指向整数的指针.
- arr是指向整数指针数组的指针
- arr是指向整数数组指针数组的指针
它永远不会那样.指向整数指针数组的指针将声明如下:
int* (*arr)[n]
Run Code Online (Sandbox Code Playgroud)
听起来好像你被int**
糟糕的老师/书籍/教程欺骗了.它几乎总是不正确的做法,因为解释这里和这里和(约数组指针详细的说明)这里.
编辑
最后写了一篇详细的文章,解释了什么是数组,查找表是什么,后者为什么不好以及你应该使用什么:正确分配多维数组.
只有变量的声明,你无法区分这三种情况.人们仍然可以讨论是否应该使用类似于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)
我怎么知道:
arr是指向整数指针的指针
arr是指向整数指针数组的指针
arr是指向整数数组指针数组的指针
你不能.它可以是任何一种.它最终取决于你如何分配/使用它.
因此,如果您使用这些代码编写代码,请记录您正在使用它们执行的操作,使用它们将大小参数传递给函数,并且通常在使用之前确定您分配的内容.
无论指针指向单个对象还是指向数组元素的对象,指针都不会保留信息.此外,对于指针运算,单个对象被认为是由一个元素组成的数组.
考虑这些声明
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
具有十个元素的数组的第一个元素.
的类型
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
.