【问题标题】:&array == array, what about structs?&array == 数组,那么结构呢?
【发布时间】:2016-03-02 13:46:32
【问题描述】:

我刚开始搞乱结构和指针。

这是我的 .h :

#ifndef struct_struct_h
#include <string.h>
#define struct_struct_h



#endif


int count=0;

typedef struct
{
    int num;
    double balance;
    const char * name;
    struct Account * acnt;
    
} Account;

Account* a = NULL;

Account* new_account(const char * n)
{
    Account *a1 = malloc(sizeof(Account));
    
    a1->num=++count;
    a1->name = n;
    return a1;
}

这是我的 main.c :

#include <stdio.h>
#include <string.h>
#include "struct.h"



int main(int argc, const char * argv[])
{

    // insert code here...
    Account* accounts[2];
    
    for(int i=0; i<2; i++)
    {
        accounts[i] = (i==0 ? new_account("David") : new_account("Toto") );
    }
    printf("Accounts array address is %i\n",&accounts);
    
    for(int i=0; i<2;i++)
    {
        printf("Account n°%i is owned by %s \n, its address is %i\n",accounts[i]->num,accounts[i]->name,&accounts[i]);
    }
    printf("There are %i accounts.\n",count);
    
    return 0;
}

如果我用帐户替换 &accounts,我会得到相同的结果:@array,要么是 &accounts[0],没关系。

Accounts数组地址为1606416480

如果我用 *accounts 替换 &accounts,我会得到:

Accounts数组地址为1063600

第二个输出是:

帐户 n°1 归 David 所有 ,它的地址是1606416480

账户 n°2 归 Toto 所有 ,它的地址是1606416488

其实这些是accounts中包含的account指针的@,这些@在内存中各占8B。

如果我将 &accounts[i] 替换为 accounts[i],然后由 *accounts[i] 我得到:

帐户 n°1 归 David 所有 ,它的地址是1063600

账户 n°2 为 Toto 所有,地址为 1063632

帐户 n°1 归 David 所有 ,它的地址是3874

账户 n°2 归 Toto 所有 ,它的地址是3880

在第一种情况下,我有 2 个指针,在第二种情况下,我有 2 个*指针。

*STRUCT 和 STRUCT 不一样,为什么?

【问题讨论】:

  • 使用%p 将参数转换为void * 以打印地址。
  • 如果一个数组是一个指针,它将被称为“指针”,而不是“数组”! &array array 相同。即使 iff 数组 decays 大多数时候都指向一个指针,它也是一个不同的指针。而且你有更多的误解:你的“标题”是错误的。请查看标头必须包含的内容以及守卫的实际用途。
  • 我对 C 很陌生,我读过“公共”结构应该在标题中声明。
  • 注意:分配问题:使用Account *a1 = malloc(sizeof *Account);

标签: c arrays pointers reference


【解决方案1】:

数组在内部表示为连续的内存范围。在范围的最开始,放置了数组的第一个元素。

如果您有一个数组,例如以与您的问题 accounts 相同的方式命名,则该数组的地址和该数组的第一个元素的地址具有相同的值。

如果考虑你的例子,那么你有

Account * accounts[2];

表达式中的数组(例如在 sizeof 运算符中使用它们)会被转换为指向其第一个元素的指针。

因此表达式 accounts 具有类型 Account ** 和等式

accounts == &accounts[0]

评估为真。

表达式&amp;accounts 具有相同的值,因为它是扩展区的地址,但类型不同。它的类型是

Account * ( * )[2]

那就是如果你写例子

Account *( *p )[2] = accounts;

printf( "*p = %zu\n", sizeof( *p ) ); 

那么输出将等于 16,因为在您运行自己的代码的环境中,指针的大小等于 8,并且数组由两个指针元素组成。

你不能写条件

&accounts == accounts

因为操作数有不同的类型。但是如果你写

( void *)&accounts == ( void * ) accounts 

那么这个条件的计算结果为真。

所以表达式的值

&amp;accountsaccounts&amp;accounts[0]相互相等,是数组占用的内存extent的地址。

对于结构,结构的地址等于其第一个成员的地址。但是,结构类型的对象名称不会隐式转换为指向其第一个成员的指针。

如果我将 &accounts[i] 替换为 accounts[i],然后由 *accounts[i] 我得到:

&amp;accounts[i] 给出数组元素的地址。所以你会得到价值

Account n°1 is owned by David , its address is 1606416480

Account n°2 is owned by Toto , its address is 1606416488

值之间的差等于8,即表达式sizeof( Account * )的值

accounts[i] 给出存储在数组元素中的值。它们是结构类型的每个对象的动态分配内存的地址。

*accounts[i] 是结构类型的对象。由于 printf 调用中的格式说明符与作为参数传递的对象不对应,因此函数行为未定义。

考虑到要打印指针,您必须使用格式说明符%p

【讨论】:

  • 非常感谢 Vlad 的回答,自从我开始使用 C 编写代码以来,我将内存可视化为一个大数组。这种方法可以帮助我理解高级语言中的可变性和引用之间的相关性。
【解决方案2】:

TL/DR 版本:

数组是特殊的,在大多数情况下,数组表达式被视为指向数组第一个元素的指针。 a&amp;a 将产生相同的值(数组 a 的第一个元素的地址),尽管表达式的类型会有所不同。

struct 类型(或任何其他聚合类型)不是这种情况。

大卫福斯特华莱士版本:

除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,否则 表达式 类型为 "@987654327 @-element array of T" 将被转换(“decay”)为类型为“pointer to T”的表达式,表达式的值将是数组中第一个元素的地址。

为什么会这样?

首先,让我们看看数组在内存中是如何表示的。鉴于声明

T a[N];

然后你会得到如下所示的东西:

   +---+
a: |   | a[0]
   +---+
   |   | a[1]
   +---+
    ...
   +---+
   |   | a[N-1]
   +---+

为数组元素留出存储空间,没有别的;没有存储任何元数据或指向数组第一个元素的任何指针。至关重要的是,除了数组元素之外没有对象arr(换句话说,对象arr数组元素)。

但是数组下标操作a[i]定义*(a + i);也就是说,给定地址a,我们从该地址偏移i 元素(不是字节!)并取消引用结果。

但是a 是一个数组,而不是一个指针,那么它怎么能工作呢?

像这样 - 除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,表达式类型为 " N-element array of T" 将被转换 ("decay") 为类型为 "pointer to T" 的表达式,表达式的值将是数组的第一个元素的地址1.

因此,在代码中

printf( "a = %p\n", (void *) a ); // *always* use `%p` to print pointer values

表达式a 不是sizeof 或一元&amp; 运算符的操作数,因此它被转换(“衰减”)为“指向T 的指针”类型的表达式,并且值表达式是第一个元素的地址。

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


  Expression        Type        "Decays" to        Value
  ----------        ----        -----------        -----
           a        T [N]       T *                Address of first element 
          &a        T (*)[N]    n/a                Same value as above, different type
          *a        T           n/a                Value of first element
        a[i]        T           n/a                Value of i'th element
       &a[i]        T *         n/a                Address of i'th element
    sizeof a        size_t      n/a                Number of bytes in array
   sizeof *a        size_t      n/a                Number of bytes in single element
 sizeof a[i]        size_t      n/a                Same as above
   sizeof &a        size_t      n/a                Number of bytes in pointer to array
sizeof &a[i]        size_t      n/a                Number of bytes in pointer to single element

因此,sizeof a == sizeof (T [N])sizeof *a == sizeof (T)sizeof &amp;a == sizeof (T (*)[N])sizeof &amp;a[i] == sizeof (T *)

注意表达式a&amp;a&amp;a[0]都产生相同的(数组第一个元素的地址),但类型 的表达式会有所不同。 a*&amp;a[0] 都具有 T * 类型(指向 T 的指针),但 &amp;a 具有 T (*)[N] 类型(指向 NT 元素数组的指针)。这对于指针算术之类的事情很重要。例如,假设以下代码:

T a[N];
T *p = a;
T (*ap)[N] = &a;

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

p + 1 将产生p 之后数组的下一个元素的地址。 ap + 1 将产生当前数组之后的下一个N-元素数组T 的地址。

struct 或其他聚合类型没有相应的转换规则,因为在某些情况下与其他情况下,不需要将struct 表达式视为指针。组件选择运算符.-&gt; 的工作方式与下标运算符不同。


1。 C 派生自早期的称为 B 的编程语言(如图),在 B 中存储留作指向数组第一个元素的“指针”(在 B 中,指针只是整数偏移量) )。 Ritchie 在开始设计 C 时保留了 B 的数组语义,但在开始添加 struct 类型时遇到了问题;他不想在结构数据中混合数组指针元数据。他通过创建上述转换规则解决了这个问题。

【讨论】:

    猜你喜欢
    • 2011-11-18
    • 2010-09-17
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多