c - 在C语言中,数组是指针还是用作指针?

标签 c arrays pointers

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

但这让我感到困惑:以下代码:

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);

使用 Apple GCC 编译时给出以下结果:

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

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

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

最佳答案

以下是 C 标准中的确切语言 (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.

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

当你声明一个数组时

int a[10];

表达式 a 指定的对象是一个数组(即,足够大以容纳 10 个 int 值的连续内存块),并且 < em>表达式 a 是“int 的 10 元素数组”,或 int [10] 。如果表达式 a 出现在上下文中,而不是作为 sizeof& 运算符的操作数,则其类型将隐式转换为 int * ,并且其值是第一个元素的地址。

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

&运算符的情况下,值为数组的地址,与数组第一个元素的地址相同,但表达式的类型不同:给定声明 T a[N]; ,表达式 &a 的类型为 T (*)[N] ,或指向 T 的 N 元素数组的指针。a&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 = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80

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

更多标准语言:

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).

因此,当您为数组表达式添加下标时,实际上会计算距数组中第一个元素的地址的偏移量,并取消引用结果。表达式

a[i] = 10;

相当于

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

这相当于

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

这相当于

 i[a] = 10;

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

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

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

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

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

从这里开始,高维数组的模式应该很清晰。

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

关于c - 在C语言中,数组是指针还是用作指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4607128/

相关文章:

c++ - 为什么解引用运算符 (*) 也用于声明指针?

将 u_int32_t 转换为 char **

c - 如何在 C 中(在 Visual Studio 中)在全局固定大小数组中设置值?

c - 循环无法识别变量?

javascript - 如何交换给定索引号在数组中的位置(javascript)

ios - 下载数据并显示

java - 在java中发出HttpClient请求

c++ - 我有非常量 ID3D11ShaderResourceView*,并且 DirectX 函数需要 const 一个

C 函数过度使用参数?

c - 如何在 C 中正确使用 malloc?