8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

数组名是指针吗?

Pahashi Infotech 1月前

51 0

在 C 语言中,数组的名称是指针吗?如果不是,那么数组的名称和指针变量有什么区别?

在 C 语言中,数组的名称是指针吗?如果不是,那么数组的名称和指针变量有什么区别?

帖子版权声明 1、本帖标题:数组名是指针吗?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Pahashi Infotech在本站《function》版块原创发布, 转载请注明出处!
最新回复 (0)
  • @Thomas 同意,但就指针而言,当你取消引用数组和 &array[0] 时,它们会产生相同的值 array[0]。即 *array == array[0]。没有人认为这两个指针是相同的,但在这种特定情况下(指向第一个元素),你可以使用数组的名称。

  • 数组是数组,指针是指针,但大多数情况下数组名会 转换 为指针。常用的一个术语是它们 会衰减 为指针。

    这是一个数组:

    int a[7];
    

    a 包含七个整数的空间,您可以通过赋值将值放入其中一个整数中,如下所示:

    a[3] = 9;
    

    这是一个指针:

    int *p;
    

    p 不包含任何整数空间,但它可以指向整数空间。例如,我们可以将其设置为指向数组中的某个位置 a ,例如第一个位置:

    p = &a[0];
    

    令人困惑的是,你也可以这样写:

    p = a;
    

    不会 将数组的内容复制 a 到指针中 p (无论这意味着什么)。相反,数组名称 a 将转换为指向其第一个元素的指针。因此该赋值与上一个赋值相同。

    现在你可以 p 以类似的方式使用数组:

    p[3] = 17;
    

    之所以有效,是因为 C 中的数组解引用运算符 [ ] 是根据指针定义的。 x[y] 意思是:从指针 开始 x ,将 y 元素向前移动指针指向的内容,然后取其中的内容。使用指针算术语法, x[y] 也可以写成 *(x+y) .

    为了使它能够与普通数组(例如我们的)一起使用, a 中的 a 名称 a[3] 转换为指针(指向 中的第一个元素 a )。然后我们向前移动 3 个元素,并获取其中的任何元素。换句话说:获取数组中位置 3 处的元素。(这是数组中的第四个元素,因为第一个元素的编号为 0。)

    因此,总而言之,C 程序中的数组名称(在大多数情况下)会转换为指针。一个例外是当我们 sizeof 在数组上使用运算符时。如果 a 在此上下文中转换为指针, sizeof a 则会给出指针的大小而不是实际数组的大小,这将是毫无用处的,因此在这种情况下 a 意味着数组本身。

  • 类似的自动转换也应用于函数指针——奇怪的是,functionpointer() 和 (*functionpointer)() 的含义相同。

  • 此外,除了 sizeof() 之外,另一个不存在数组->指针衰减的上下文是运算符 & - 在上面示例中,&a 将是一个指向 7 个 int 数组的指针,而不是指向单个 int 的指针;也就是说,它的类型将是 int(*)[7],它不能隐式转换为 int*。这样,函数实际上可以接受指向特定大小的数组的指针,并通过类型系统强制执行限制。

  • 当数组作为值时,其名称代表第一个元素的地址。
    当数组不用作值时,其名称代表整个数组。

    int arr[7];
    
    /* arr used as value */
    foo(arr);
    int x = *(arr + 1); /* same as arr[1] */
    
    /* arr not used as value */
    size_t bytes = sizeof arr;
    void *q = &arr; /* void pointers are compatible with pointers to any object */
    
  • 如果数组类型的表达式(例如数组名​​称)出现在较大的表达式中,并且它不是或运算符的操作数 & sizeof 则数组表达式的类型将从“T 的 N 元素数组”转换为“指向 T 的指针”,并且表达式的值是数组中第一个元素的地址。

    简而言之,数组名不是指针,但在大多数情况下它被视为 指针

    编辑

    回答评论中的问题:

    如果我使用 sizeof,我是否只计算数组元素的大小?那么数组“head”也会占用空间,其中包含有关长度和指针的信息(这意味着它比普通指针占用更多空间)?

    创建数组时,唯一分配的空间是元素本身的空间;不会为单独的指针或任何元数据实现存储空间。鉴于

    char a[10];
    

    你记忆中得到的是

       +---+
    a: |   | a[0]
       +---+ 
       |   | a[1]
       +---+
       |   | a[2]
       +---+
        ...
       +---+
       |   | a[9]
       +---+
    

    表达式 引用整个数组,但没有 a 与数组元素本身分开的 对象 a 给出整个数组的大小(以字节为单位)。表达式 sizeof a 给出数组的地址, &a 它与第一个元素的地址相同 &a 区别 &a[0] 1的类型- char (*)[10] 在第一种情况下, char * 在第二种情况下。

    当您想要访问单个元素时,事情就会变得奇怪 - 表达式 a[i] 被定义为 *(a + i) 给定地址值 a 、从该地址偏移 i 元素( 而不是字节 )并取消引用结果的结果。

    问题是 a 不是指针或地址 - 它是整个数组对象。因此,C 中的规则是,每当编译器看到数组类型的表达式(例如 a ,其具有类型 char [10] ) 并且 该表达式不是 sizeof 或 一元运算符 & ,该表达式的类型将转换(“衰减”)为指针类型( char * ),并且表达式的值是数组第一个元素的地址。因此,表达式 具有 a 与表达式相同的类型和值 &a[0] (并且通过扩展,表达式 *a 具有与表达式 相同的类型和值 a[0] )。

    C 源自一种名为 B 的早期语言,在 B 中, a 指针对象与数组元素 a[0] , a[1] 。Ritchie 想保留 B 的数组语义,但他不想存储单独的指针对象。所以他去掉了它。相反,编译器会在翻译过程中根据需要将数组表达式转换为指针表达式。

    请记住,我说过数组不存储有关其大小的任何元数据。一旦该数组表达式“衰减”为指针,您所拥有的就只是一个指向单个元素的指针。该元素可能是元素序列中的第一个,也可能是一个单个对象。没有办法根据指针本身来知道。

    当您将数组表达式传递给函数时,函数收到的只是指向第一个元素的指针 - 它不知道数组有多大(这就是为什么该 gets 函数如此危险并最终从库中删除的原因)。为了让函数知道数组有多少个元素,您必须使用标记值(例如 C 字符串中的 0 终止符),或者必须将元素数量作为单独的参数传递。


    1. 这*可能*会影响地址值的解释方式——取决于机器。
  • 我已经找了很长时间才找到这个答案。谢谢!如果你知道,你能进一步告诉我什么是数组表达式吗?如果我使用sizeof,我是否只计算数组元素的大小?然后数组“head”也占用了空间,其中包含有关长度和指针的信息(这意味着它比普通指针占用更多空间)?

  • 还有一件事。长度为 5 的数组是 int[5] 类型。那么当我们调用 sizeof(array) 时,我们从哪里知道长度 - 从其类型?这是否意味着不同长度的数组就像不同类型的常量?

  • 引用 10

    @AndriyDmytruk:sizeof 是一个运算符,它的计算结果是操作数中的字节数(表示对象的表达式,或括号中的类型名称)。因此,对于数组,sizeof 的计算结果是元素数乘以单个元素中的字节数。如果 int 的宽度为 4 个字节,则 5 个元素的 int 数组占用 20 个字节。

  • 引用 11

    @Stan:下标操作 a[i] 定义为 *(a + i) - 给定起始地址 a,找到数组中第 i 个对象的地址,并取消引用结果。如果 a 是数组类型的表达式,则在执行加法之前将其转换为指针。请记住,在指针运算中,a + 1 得出指向类型的下一个对象的地址,而不是下一个字节。如果 a 指向 4 字节 int,则 a + 1 指向下一个 4 字节 int。如果 a 指向 128 字节结构,则 a + 1 指向下一个 128 字节结构。

  • 引用 12

    @Stan:表达式 a 的类型为 int [2][3],它会“衰减”为 int (*)[3] 类型。表达式 *(a + 1) 的类型为 int [3],它会“衰减”为 int *。因此,*(*(a + 1) + 2) 的类型为 int。a 指向第一个 3 元素 int 数组,a + 1 指向第二个 3 元素 int 数组,*(a + 1) 是第二个 3 元素 int 数组,*(a + 1) + 2 指向第二个 int 数组的第三个元素,因此 *(*(a + 1) + 2) 是第二个 int 数组的第三个元素。如何将其映射到机器代码完全取决于编译器。

  • 引用 13

    像这样声明的数组

    int a[10];
    

    分配内存 10 int 秒。您无法修改 a ,但可以使用以下方法进行指针运算 a .

    像这样的指针仅为指针分配内存 p

    int *p;
    

    它没有分配任何 int s。您可以修改它:

    p = a;
    

    并使用数组下标,就像使用 a 一样:

    p[2] = 5;
    a[2] = 5;    // same
    *(p+2) = 5;  // same effect
    *(a+2) = 5;  // same effect
    
  • 数组并不总是分配在堆栈上。这是一个实现细节,因编译器而异。在大多数情况下,静态或全局数组将从与堆栈不同的内存区域分配。const 类型的数组可能从另一个内存区域分配

  • 我认为 Grumdrig 的意思是“分配 10 个整数,并自动存储持续时间”。

  • 数组名本身会产生一个内存位置,因此您可以将数组名视为指针:

    int a[7];
    
    a[0] = 1976;
    a[1] = 1984;
    
    printf("memory location of a: %p", a);
    
    printf("value at memory location %p is %d", a, *a);
    

    除了可以对指针执行其他有趣的操作(例如,添加/减去偏移量)之外,还可以对数组执行其他有趣的操作:

    printf("value at memory location %p is %d", a + 1, *(a + 1));
    

    从语言角度来看,如果 C 没有将数组公开为 某种“指针” (严格地说,它只是一个内存位置。它不能指向内存中的任意位置,也不能由程序员控制)。我们总是需要编写这样的代码:

    printf("value at memory location %p is %d", &a[1], a[1]);
    
  • 当 sizeof (int*) != sizeof (void*) 时,此代码不会导致 UB 吗?公平地说,我不知道有哪个系统会出现这种情况。

  • @12431234123412341234123 在所有系统上都是 UB(由于格式说明符不匹配),但更有可能在您描述的系统上产生意外的输出

  • 我认为这个例子可以阐明这个问题:

    #include <stdio.h>
    int main()
    {
            int a[3] = {9, 10, 11};
            int **b = &a;
    
            printf("a == &a: %d\n", a == b);
            return 0;
    }
    

    它在 gcc 4.9.2 中编译正常(有 2 个警告),并打印以下内容:

    a == &a: 1
    

    哎呀 :-)

    因此,结论是否定的,数组不是指针,它不是作为指针存储在内存中(甚至不是只读的),即使它看起来像,因为您可以使用 & 运算符获取其地址。但是 - 哎呀 - 该运算符不起作用 :-)),无论如何,您已经收到警告:

    p.c: In function ‘main’:
    pp.c:6:12: warning: initialization from incompatible pointer type
      int **b = &a;
                ^
    p.c:8:28: warning: comparison of distinct pointer types lacks a cast
      printf("a == &a: %d\n", a == b);
    

    C++ 拒绝任何此类尝试,并在编译时产生错误。

    编辑:

    这就是我想要证明的:

    #include <stdio.h>
    int main()
    {
        int a[3] = {9, 10, 11};
        void *c = a;
    
        void *b = &a;
        void *d = &c;
    
        printf("a == &a: %d\n", a == b);
        printf("c == &c: %d\n", c == d);
        return 0;
    }
    

    尽管 c a “指向”同一块内存,你可以获取指针的地址 c ,但无法获取指针的地址 a

  • “编译正常(有 2 个警告)”。这可不行。如果您通过添加 -std=c11 -pedantic-errors 告诉 gcc 将其编译为正确的标准 C,您会收到编译器错误,因为编写了无效的 C 代码。原因是您尝试将 int (*)[3] 分配给 int** 变量,这两种类型完全没有关系。所以我不知道这个例子要证明什么。

返回
作者最近主题: