我在遗留项目中看到了以下代码。
/* 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 T
与array of T
不同。我朋友的反驳说,两者的行为都是一样的,所以我使用v1和v2并不重要。
问题1:不完整类型的数组会耗尽指针吗?
问题2:我的朋友是对的,两个版本在行为上都保证等效吗?
答案 0 :(得分:8)
第一个版本错了。数组不是指针,声明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。
答案 1 :(得分:2)
让我们通过程序了解相同的东西
test.c的
#include<stdio.h>
#include"head.h"
struct token id_tokens[10];
int main()
{
printf("In original file: %p",id_tokens);
testing();
}
head.h
struct token {
int temp;
};
test1.c with v1
#include<stdio.h>
#include"head.h"
extern struct token* id_tokens;
void testing () {
printf("In other file %p",id_tokens);
}
输出:在原始文件中:0x601040在其他文件中(无)
test1.c with 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正确。
答案 2 :(得分: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
中,将使用此声明生成以下说明。
- 醇>
extern struct token id_tokens[];
id_tokens [i]
a。可能与其他编译单元链接的id_tokens的地址 采取了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
中,将使用此声明生成以下说明:
- 醇>
extern struct token *id_tokens;
id_tokens [i]
a。来自其他地方的id_tokens地址的内容 编制单位 (如果因类型不匹配而出现在同一编译单元中,将导致编译错误)b。 i的偏移量加上
c。 引用值
假设size id_token[0]
为2
字节,指向id_token[0]
的指针大小为4
字节。
您之后的声明可能(错误)解释id_tokens[0]
&amp; id_tokens[1]
作为地址并为其添加一些偏移量(可能是现有的或不存在的地址,已知的对齐或非对齐地址)。
如果这是您的好日子,程序可能会立即崩溃或发生段错误,您将有机会修复该错误。如果这是你糟糕的一天,程序可能只是搞乱了一些其他内存或者将错误的状态传达给某个模块,这可能导致难以跟踪错误并导致噩梦。
现在我想你明白了为什么(nil)
作为Mr. 32's answer的输出。