C编译器如何知道char ** x指向一个数组?

时间:2018-03-02 18:38:59

标签: c arrays pointers

在C中,我读过char** xchar* x[]的意思完全相同。我理解第二个例子,但我不明白第一个例子。

对我来说,第一个例子是“指向指向字符的指针”,但是C会将其视为“指向字符数组的指针列表”?

有人能够以外行的方式向我解释这一点,因为我一直在抓住它时遇到异常困难的时间。

编辑:我承认我说的第一点是我认为人们想要听到的而不是我的实际解释。我也把它看作是指向char的指针,但是当我在这个问题上阅读最佳答案时,我的理解力下降了:Difference between char *argv[] and char **argv for the second argument to main()

这表明当用作参数向量时,它被用作指向字符指针数组的指针...

5 个答案:

答案 0 :(得分:3)

长话短说 - 它没有区分。编译器不知道它指向一个数组。它看到的只是一个指针。 您展示的方式 (作为函数参数声明的一部分 - 传递给函数)char *[]char **表示相同的事情。这是什么?指向char*的指针。没有比这更好的了。

编译器没有保留额外信息,可以区分您是通过char*数组还是char**,因为最终char*数组衰减成指向第一个元素的指针 - 这是char**

试试这个,你会理解: -

char c = 'a';
char *pc = &c;
char **ppc = &pc;
f(ppc);

...

void f(char *ppc[]){

}

这不会让编译器抱怨。因为它最终被编译器视为char **ppc

为了证明编译器在传递给函数时没有看到数组 - 你可以看到这个引用(基本上说它有衰减,它归结为指向第一个元素的指针。所以它看到的指针是所有)(这通过没有方式表明{strong}所有案例中char**char*[]相同,因为腐朽 - 它不是)< / p>

§6.3.2.1¶3标准N1570

  

除非它是sizeof运算符,_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,类型为'array of type'的表达式将转换为类型为'指向类型的指针'的表达式,该表达式指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则行为未定义。

编辑是为了清除让某人不理解答案的错误解释。

答案 1 :(得分:2)

  

C编译器如何知道char ** x指向数组?

没有!

  

对我来说,第一个例子是“指向指向角色的指针”

正确。

  

但是C会将其视为“指向字符数组指针列表”?

也正确。

诀窍在于虽然语言不知道列表有多长,但只包含一个内容的列表仍然是一个列表。所以这并不重要。 1

给定指针T* ptr*ptr将为您提供ptr指向(一个)中的一个对象,或者它将为您提供{{1}的第一个对象指向(超过一个)。

只有当您开始递增该指针(例如ptr或者在访问期间ptr++)时,才会知道实际上在另一端有多少个对象。但是,这对语言来说无关紧要,因为这需要程序员才能做好。

1。确实存在指向数组的指针类型,因此可以将类型系统纳入讨论,然后 重要。但我们只讨论指向单对象类型的指针(尽管链接到的答案使得“按值传递”数组的主题变得非常混乱)。

如果有帮助,请将您所链接问题的最佳答案的开场陈述:

  

基本上,char * argv []表示char指针数组,而char ** argv表示指向char指针的指针。

......错了。 ptr[152]char* argv[]透明地重新驱逐(非常感谢,C!)所以这两者都意味着同样的事情。

你应该在一个问题下阅读所有答案,而不仅仅是一个; bmargulies's更准确。

答案 2 :(得分:1)

首先,char **xchar *x[]不等效,除非是函数参数声明。

等效:

int main(int argc, char **argv);
int main(int argc, char *argv[]); // equivalent

不等同于:

{
   char **pointer;
   char *array[3];

}

AC T *指针可以指向T类型的单个对象,也可以指向T&#34;&#34;数组的任何元素。 ,并且还可以将一个元素指向超出此类数组的末尾。

此信息不包含在指针的语言级别,程序员可见类型信息中。

在某些情况下,编译器可以推断出这些情况,并在出现错误时提供有用的诊断。

编译器可以跟踪表达式值来自的数据流。

例如,给定:

char *array[3];      /* array of 3 "char *" pointers */
char **ptr = array;  /* pointer to array[0] */

在这里,编译器可以分析从ptr初始化array。因此,在初始化和程序中的任何其他点之间,如果没有对ptr进行分配,则可以假定它仍然指向array[0]

因此,编译器可能会为ptr + 5等可疑表达式生成警告,同时对ptr + 3保持静默。

ISO C不要求任何此类分析或诊断;它是一个&#34;实施质量&#34;物质

静态诊断的数量有限制。 C函数可以接收指针值作为参数&#34;无处不在&#34;,在编译时无法合理地知道任何内容。

顺便说一句,宣言风格char* ptr是一个坏习惯;它的样式应为char *ptr。请注意,char* a, ba声明为指针,将b声明为char。语法是char是&#34;声明说明符&#34;的列表。这些说明符后跟逗号分隔的&#34;声明符&#34;列表。这些声明符分别为*ab。如果你写char* ptr,你就会在声明符的中间放一个空格分区,同时将它与说明符一起聚集在一起。

这有点像采用算术表达式x + x*y + y并将其写为x+x * y+y,所以看起来像乘法最后完成。

在编程中避免这种误导性的空白。

答案 3 :(得分:0)

为了简化说明,我们将char*替换为TypeName,这可以是任何内容。

当它们用作函数参数时,

TypeName* xTypeName x[]完全相同。

一些使用第一个表单指示指向单个对象的指针,第二个表单指示指向数组第一个元素的指针。从编译器的角度来看,它们完全相同。

假设你有一个变量:

TypeName a[10];

a用作函数调用中的参数时,衰减到指向第一个alement的指针

 foo(a);

与:

相同
foo(&a[0]);

在函数中,参数可以声明为TypeName* xTypeName x[]

无论参数的声明方式如何,您都可以使用数组语法a访问x[i]中数组的元素。

void foo(TypeName* x)
{
    x[0] = <some expression>;
}

void foo(TypeName x[])
{
    x[0] = <some expression>;
}

是一样的。它们为数组的第一个元素赋值。

答案 4 :(得分:0)

在函数参数声明的上下文中,T a[N]T a[]被解释为T *a - 所有三个声明a作为指向T的指针:

void foo( T a[N] )

相同
void foo( T a[] )

相同
void foo( T *a )

所以,将T替换为char *,然后获得

void foo( char *arr[N] )

相同
void foo( char *arr[] )

相同
void foo( char **arr )

对于函数参数声明,这是为true - 在常规声明中

T a[N];

相同
T *a;

那为什么会这样呢?

除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则类型为表达式 &#34; T&#34;的N元素数组;将被转换(&#34;衰减&#34;)到类型为#34的表达式;指向T&#34;的指针,并且表达式的值将是该表达式中第一个元素的地址阵列。

因此,如果将数组声明为

T a[N];

并将其传递给函数

foo( a );

函数调用中的表达式 a是从T&#34;的类型&#34; N元素数组转换而来的。 to&#34;指向T&#34;的指针,以及函数foo实际接收的是指针值,而不是数组:

void foo( T *a ) { ... }

使用a[]代替*a是B编程语言的延续,C语言源于此。很坦率地说,很多C的阵列奇怪都可以追溯到B.就个人而言,我认为这会产生更多的混乱而不是它的价值,但我并没有写出制定标准的委员会。

你有时会听到有人宣称&#34;数组只是一个指针&#34; - 这是对规则的误解。数组和指针不相同,但在大多数情况下,数组表达式将转换为指针表达式。