【问题标题】:extern declaration, T* v/s T[]外部声明,T* v/s T[]
【发布时间】:2014-11-14 06:33:42
【问题描述】:

我在一个遗留项目中看到了以下代码。

/* token.c */
struct token id_tokens[MAX_TOKENS];

/* analyse.c (v1) */
extern struct token *id_tokens; /* Raised my eyebrow, id_token declares a pointer */

我坚持要更改analyse.c 以包含如下声明:

/* analyse.c (v2) */
extern struct token id_tokens[]; /* I am happy with this. id_tokens declares array of unspecified size. */

我想要v2,因为pointer to Tarray of T 不同。我朋友的反驳说两者的行为是一样的,所以我使用v1和v2都没有关系。

问题一:不完整类型的数组会耗尽指针吗?

问题 2:我的朋友认为这两个版本在行为上是相同的吗?

【问题讨论】:

  • this 数组不是指针很多重复,我知道这是什么混乱,在某种程度上,你和你的朋友都错了
  • 数组和指针可以互换使用,因为数组只是内存中一行中一组相同类型的变量,而指针只是指向一组变量的第一个元素同一类型在内存中的一行。这就是为什么char *foochar foo[] 相同
  • @Gophyr 你说的是我朋友的语言。检查下面的答案。它们确实不同。
  • @GyaptiJain 从技术上讲,数组也只是指向内存空间开头的指针。两种情况下的内存是一样的,只是代码中的表现看起来不同。
  • 然而,数组有一个确定的大小与之关联,但它可能会被溢出,即缓冲区溢出等。

标签: c language-lawyer extern declaration


【解决方案1】:

第一个版本是错误的。数组不是指针,声明 extern struct token *id_tokens; 与定义类型 struct token id_tokens[MAX_TOKENS]; 不匹配。

参考:C FAQ: I had the definition char a[6] in one source file, and in another I declared extern char *a. Why didn't it work?。另请参阅this

【讨论】:

    【解决方案2】:
    /* token.c */
    struct token id_tokens[MAX_TOKENS];
    
    /*
     id_tokens
      +-----+-----+-----+-----+...+-----+
      |     |     |     |     |   |     |
      +-----+-----+-----+-----+...+-----+
        [0]   [1]   [2]   [3]  ...  [MAX_TOKEN-1]
    
      To access id_tokens[i], add offset of ith element
      i.e. i * sizeof(struct token) to the **address**
      of array token
     */
    

    因此,在您的 analyse.c 中,此声明将生成以下说明。

    1. extern struct token id_tokens[];
      id_tokens[i]
      可能从其他编译单元链接的 id_token 的地址 采取
      b。添加了 i 的偏移量
      c.值被引用
    /* analyse.c (v1) */
    extern struct token *id_tokens;
    
    /*
     id_tokens
      +------+           +-----+...
      | addr |---------->|     |
      +------+           +-----+...
    
    
      To access id_tokens[i], fetch **contetnts** of pointer
      token, add offset of ith element i.e. i * sizeof(struct token)
      is added to this.
     */
    

    因此,在您的analyse.c 中,此声明将生成以下说明:

    1. extern struct token *id_tokens;
      id_tokens[i]
      来自其他链接的 id_tokens 地址的内容 编译单元被占用。
      (如果由于类型不匹配而存在于同一编译单元中,将导致编译错误)
      b.添加了 i 的偏移量
      c. 值被引用

    假设id_token[0] 的sizeof 是2 字节,而指向id_token[0] 的sizeof 指针是4 字节。

    您以后的声明可能(错误)将id_tokens[0]id_tokens[1] 解释为地址并为其添加一些偏移量(可能是现有或不存在的地址,谁知道的对齐或非对齐地址)。

    如果这是您的好日子,程序可能会立即崩溃或出现段错误,您有机会修复错误。如果这是您糟糕的一天,程序可能只是弄乱了其他一些内存或将错误状态传达给某些模块,这可能导致难以跟踪错误并引发噩梦。


    现在我想你明白为什么在Mr. 32's answer 中输出(nil)

    【讨论】:

      【解决方案3】:

      让我们通过程序理解相同的东西

      test.c

      #include<stdio.h>
      #include"head.h"
      struct token id_tokens[10];
      int main()
      {
      printf("In original file: %p",id_tokens);
      testing();
      }
      

      头.h

      struct token {
      int temp;
      };
      

      test1.c 和 v1

      #include<stdio.h>
      #include"head.h"
      extern struct token* id_tokens;
      void testing () {
      printf("In other file %p",id_tokens);
      }
      

      输出:在原始文件中:0x601040在其他文件中(无)


      test1.c 和 v2

      #include<stdio.h>
      #include"head.h"
      extern struct token id_tokens[];
      void testing () {
      printf("In other file %p",id_tokens);
      }
      

      输出:在原始文件中:0x601040在其他文件中0x601040


      这清楚地表明v1不正确,v2正确。

      【讨论】:

      • 不将 id_tokens 转换为 void * for %p 是 UB。为什么是零?
      • 在 v1 中,您已将 id_tokens 声明为指向结构令牌的外部指针,但两个文件中都没有任何全局结构令牌,因此它为空。
      • +1 非常务实的方式。是否有规范参考建议相同>
      • 这东西应该符合规范。我不阅读规范。我通常通过制作这样的小程序来清除我的疑问并明确概念。
      • @Mr.32 您可以查看我的回答以了解为什么会这样。关键点,当你在全局范围内写struct token id_tokens[10];时,内存初始化为零。
      猜你喜欢
      • 2018-05-30
      • 1970-01-01
      • 2021-08-25
      • 2018-12-03
      • 2011-09-24
      • 1970-01-01
      • 2013-03-26
      • 1970-01-01
      • 2023-03-16
      相关资源
      最近更新 更多