在 C 中,数组是指针还是用作指针? [英] In C, are arrays pointers or used as pointers?

查看:39
本文介绍了在 C 中,数组是指针还是用作指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解是数组只是指向值序列的常量指针,当您在 C 中声明数组时,您是在声明一个指针并为其指向的序列分配空间.

但这让我很困惑:以下代码:

char y[20];字符 *z = y;printf("y 大小为 %lu\n", sizeof(y));printf("y 是 %p\n", y);printf("z 大小为 %lu\n", sizeof(z));printf("z 是 %p\n", z);

使用 Apple GCC 编译时,结果如下:

y 大小为 20y 是 0x7fff5fbff930z 大小为 8z 是 0x7fff5fbff930

(我的机器是 64 位的,指针是 8 字节长).

如果 'y' 是一个常量指针,为什么它的大小为 20,就像它指向的值序列一样?在编译期间变量名 'y' 是否在合适的时候替换为内存地址?那么,数组是否是 C 语言中的某种语法糖,在编译时仅转换为指针内容?

解决方案

这里是 C 标准的确切语言 (n1256):

<块引用>6.3.2.1 左值、数组和函数指示符
...
3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则为类型为 ''array 的表达式type'' 被转换为类型为 ''pointer to type'' 的表达式,该表达式指向数组对象的初始元素,而不是左值.如果数组对象具有寄存器存储类,则行为未定义.

这里要记住的重要一点是,对象(在 C 术语中,意思是占用内存的东西)和用于引用的表达式之间是有区别的到那个对象.

当你声明一个数组时,比如

int a[10];

表达式指定的对象a是一个数组(即一个连续的内存块,足够容纳10个int 值),表达式 a 的类型为int的10元素数组",或int [10].如果 expression a 出现在上下文中而不是作为 sizeof& 运算符的操作数,则它的类型被隐式转换为int *,它的值为第一个元素的地址.

sizeof运算符的情况下,如果操作数是T [N]类型的表达式,则结果为数组对象的字节数,不在指向该对象的指针中:N * sizeof T.

&运算符的情况下,值是数组的地址,与数组第一个元素的地址相同,但type<表达式的/em> 不同:给定声明 T a[N];,表达式 &a 的类型是 T (*)[N],或指向 T 的 N 元素数组的指针.valuea&a[0](数组的地址与数组中第一个元素的地址相同),但类型的差异很重要.例如,给定代码

int a[10];int *p = a;int (*ap)[10] = &a;printf("p = %p, ap = %p\n", (void *) p, (void *) ap);p++;ap++;printf("p = %p, ap = %p\n", (void *) p, (void *) ap);

您将看到按顺序输出

p = 0xbff11e58, ap = 0xbff11e58p = 0xbff11e5c,ap = 0xbff11e80

IOW,推进psizeof int (4) 添加到原始值,而推进ap 添加10 * sizeof int (40).

更标准的语言:

<块引用>6.5.2.1 数组下标

约束

1 其中一个表达式的类型应该是''pointer to object type'',另一个表达式应该是整数类型,结果类型是''type''.

语义

2 后缀表达式后跟方括号[] 中的表达式是数组对象元素的下标指定.下标操作符[]的定义是E1[E2]等同于(*((E1)+(E2))).由于适用于二进制 + 运算符的转换规则,如果 E1 是一个数组对象(相当于一个指向数组对象初始元素的指针)并且 E2 是一个整数,E1[E2] 表示 E1 的第 E2 个元素(从零开始计数).

因此,当您为数组表达式添加下标时,在幕后发生的是计算与数组中第一个元素的地址的偏移量并取消引用结果.表达式

a[i] = 10;

相当于

*((a)+(i)) = 10;

相当于

*((i)+(a)) = 10;

相当于

 i[a] = 10;

是的,C 中的数组下标是可交换的;看在上帝的份上,永远不要在生产代码中这样做.

由于数组下标是根据指针操作定义的,因此您可以将下标运算符应用于指针类型和数组类型的表达式:

int *p = malloc(sizeof *p * 10);国际我;for (i = 0; i <10; i++)p[i] = some_initial_value();

这里有一个方便的表格来记住其中的一些概念:

<前>声明:T a[N];表达式类型转换为值---------- ---- ------------ -----a T [N] T * a 中第一个元素的地址;与写 &a[0] 相同&a T (*)[N] 数组地址;价值是一样的同上,但类型不同sizeof a size_t 数组中包含的字节数对象(N * sizeof T)*a[0] 处的 T 值a[i] T 值在 a[i]&a[i] T * a[i] 的地址声明:T a[N][M];表达式类型转换为值---------- ---- ------------ -----a T [N][M] T (*)[M] 第一个子数组的地址 (&a[0])&a T (*)[N][M] 数组的地址(与以上,但类型不同)sizeof a size_t 包含的字节数数组对象 (N * M * sizeof T)*a T [M] T * a[0] 的值,即地址第一个子数组的第一个元素(与 &a[0][0] 相同)a[i] T [M] T * a[i] 的值,即地址第 i 个子数组的第一个元素&a[i] T (*)[M] 第 i 个子数组的地址;相同的值以上,但不同的类型sizeof a[i] size_t 第 i 个子数组中包含的字节数对象 (M * sizeof T)*a[i] T 第 i 个元素的第一个元素的值子数组 (a[i][0])a[i][j] T 在 a[i][j] 处的值&a[i][j] T * a[i][j] 的地址声明:T a[N][M][O];表达式类型转换为---------- ---- -----------a T [N][M][O] T (*)[M][O]&a T (*)[N][M][O]*a T [M][O] T (*)[O]a[i] T [M][O] T (*)[O]&a[i] T (*)[M][O]*a[i] T [O] T *a[i][j] T [O] T *&a[i][j] T (*)[O]*a[i][j] Ta[i][j][k] T

从这里开始,高维数组的模式应该很清楚了.

所以,总而言之:数组不是指针.在大多数情况下,数组表达式被转换为指针类型.

My understanding was that arrays were simply constant pointers to a sequence of values, and when you declared an array in C, you were declaring a pointer and allocating space for the sequence it points to.

But this confuses me: the following code:

char y[20];
char *z = y;

printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);

when compiled with Apple GCC gives the following result:

y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930

(my machine is 64 bit, pointers are 8 bytes long).

If 'y' is a constant pointer, why does it have a size of 20, like the sequence of values it points to? Is the variable name 'y' replaced by a memory address during compilation time whenever it is appropiate? Are arrays, then, some sort of syntactic sugar in C that is just translated to pointer stuff when compiled?

解决方案

Here's the exact language from the C standard (n1256):

6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

The important thing to remember here is that there is a difference between an object (in C terms, meaning something that takes up memory) and the expression used to refer to that object.

When you declare an array such as

int a[10];

the object designated by the expression a is an array (i.e., a contiguous block of memory large enough to hold 10 int values), and the type of the expression a is "10-element array of int", or int [10]. If the expression a appears in a context other than as the operand of the sizeof or & operators, then its type is implicitly converted to int *, and its value is the address of the first element.

In the case of the sizeof operator, if the operand is an expression of type T [N], then the result is the number of bytes in the array object, not in a pointer to that object: N * sizeof T.

In the case of the & operator, the value is the address of the array, which is the same as the address of the first element of the array, but the type of the expression is different: given the declaration T a[N];, the type of the expression &a is T (*)[N], or pointer to N-element array of T. The value is the same as a or &a[0] (the address of the array is the same as the address of the first element in the array), but the difference in types matters. For example, given the code

int a[10];
int *p = a;
int (*ap)[10] = &a;

printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);

you'll see output on the order of

p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80

IOW, advancing p adds sizeof int (4) to the original value, whereas advancing ap adds 10 * sizeof int (40).

More standard language:

6.5.2.1 Array subscripting

Constraints

1 One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.

Semantics

2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

Thus, when you subscript an array expression, what happens under the hood is that the offset from the address of the first element in the array is computed and the result is dereferenced. The expression

a[i] = 10;

is equivalent to

*((a)+(i)) = 10;

which is equivalent to

*((i)+(a)) = 10;

which is equivalent to

 i[a] = 10;

Yes, array subscripting in C is commutative; for the love of God, never do this in production code.

Since array subscripting is defined in terms of pointer operations, you can apply the subscript operator to expressions of pointer type as well as array type:

int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
  p[i] = some_initial_value(); 

Here's a handy table to remember some of these concepts:

Declaration: T a[N];

Expression    Type    Converts to     Value
----------    ----    ------------    -----
         a    T [N]   T *             Address of the first element in a;
                                        identical to writing &a[0]
        &a    T (*)[N]                Address of the array; value is the same
                                        as above, but the type is different
  sizeof a    size_t                  Number of bytes contained in the array
                                        object (N * sizeof T)
        *a    T                       Value at a[0]
      a[i]    T                       Value at a[i]
     &a[i]    T *                     Address of a[i] 

Declaration: T a[N][M];

Expression     Type        Converts to     Value
----------     ----        ------------    -----
          a    T [N][M]    T (*)[M]        Address of the first subarray (&a[0])
         &a    T (*)[N][M]                 Address of the array (same value as
                                             above, but different type)
   sizeof a    size_t                      Number of bytes contained in the
                                             array object (N * M * sizeof T)
         *a    T [M]      T *              Value of a[0], which is the address
                                             of the first element of the first subarray
                                             (same as &a[0][0])
       a[i]    T [M]      T *              Value of a[i], which is the address
                                             of the first element of the i'th subarray
      &a[i]    T (*)[M]                    Address of the i-th subarray; same value as
                                             above, but different type
sizeof a[i]    size_t                      Number of bytes contained in the i'th subarray
                                             object (M * sizeof T)
      *a[i]    T                           Value of the first element of the i'th 
                                             subarray (a[i][0])
    a[i][j]    T                           Value at a[i][j]
   &a[i][j]    T *                         Address of a[i][j]

Declaration: T a[N][M][O];

Expression        Type             Converts to
----------        ----             -----------
         a        T [N][M][O]      T (*)[M][O]
        &a        T (*)[N][M][O]
        *a        T [M][O]         T (*)[O]
      a[i]        T [M][O]         T (*)[O]
     &a[i]        T (*)[M][O]
     *a[i]        T [O]            T *
   a[i][j]        T [O]            T *
  &a[i][j]        T (*)[O]
  *a[i][j]        T 
a[i][j][k]        T

From here, the pattern for higher-dimensional arrays should be clear.

So, in summary: arrays are not pointers. In most contexts, array expressions are converted to pointer types.

这篇关于在 C 中,数组是指针还是用作指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆