2

我正在使用 Keil C51 编译器对 8051 微控制器进行编程。由于某种原因,我的代码没有运行——我设法找到了这个错误,但我仍然很难理解它。与另一个相比,为什么第一个代码是错误的?值得注意的是,编译器没有抛出任何错误,代码甚至没有在微控制器上启动。

错误代码:

文件1.h

extern STRUCT_TYPEDEF array_var[];

文件2.c

// Global variable initialization
STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

将这些更改为:

文件1.h

extern STRUCT_TYPEDEF *array_var;

文件2.c

// Global variable initialization
STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

它开始工作了。

此外,这部分代码仅在“array_var[0].property = ...”之类的函数中被引用,但这些函数中没有一个被应用程序调用过。

some_struct 变量在另一个模块中声明。

为什么它会有这样的行为?[] 和 * 我不知道之间有什么区别吗?

EDIT1: 据说指针和数组是不同的东西......但是,“[]”语法与“*”有何不同?我认为编译器只会将它转换为指针,以防方括号为空(就像函数参数一样)。我还认为提供一个数组会导致给我第一个元素的地址。

现在,每个人都在说指针和数组是不同的——但我找不到任何关于它们到底有什么不同的信息。当我将数组作为右值而不是指向其第一个元素的指针时,编译器如何看待它?

4

2 回答 2

7

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

不是在声明中初始化数组的有效方法。数组初始化器必须是用大括号括起来的初始化器列表,例如

T arr[] = { init1, init2, init3 };

您不能用另一个数组1初始化一个数组,也不能以这种方式将一个数组分配给另一个数组:

T foo[] = { /* list of initializers */ }
T bar[] = foo; // not allowed
T bar[N];
...
bar = foo; // also not allowed

如果要复制 to 的内容some_struct.array2_var,则array_var必须使用如下库函数memcpy

memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );

您还必须声明array_var尺寸;如果你想使用它,你不能让它不完整。如果您提前知道它需要多大,这很容易:

STRUCT_TYPEDEF array_var[SIZE];
...
mempcy( array_var, some_struct.array2_var );

如果您提前知道它需要多大,那么您要么必须将其声明为可变长度数组(如果它需要在文件范围内或以其他方式具有static存储持续时间,则该数组将不起作用),或者您可以动态声明内存:

STRUCT_TYPEDEF *array_var = NULL;
...
array_var = malloc( sizeof some_struct.array2_var );
if ( array_var )
{
  memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );
}

这一切都假设这some_struct.array2_var是一个声明为的数组

STRUCT_TYPEDEF array2_var[SIZE]; 

如果它也只是一个指针,那么您将不得不以其他方式跟踪数组大小。

编辑

如果您只想array_var指向第一个元素some_struct.array2_var,您可以执行以下操作:

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

除非它是sizeofor 或一元运算符的操作数, &“N-element array of T”类型的表达式将被转换(“decay”)为“pointer to T”类型的表达式,表达式的值将是数组的第一个元素的地址。上面的代码完全等价于

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];


  1. 字符串文字除外,例如char message[] = "Hello";; 字符串文字"Hello"是一个数组表达式,但语言将其视为一种特殊情况。

于 2016-05-19T17:30:52.680 回答
1

这个 ...

extern STRUCT_TYPEDEF array_var[];

... 是一个未知大小和外部链接的数组的声明。由于未指定大小,因此该声明留下array_var了“不完整类型”;这会阻止对该变量的某些使用,直到且除非其类型由同一翻译单元中的另一个声明完成。例如,它不能是运算符的操作数sizeof

这个 ...

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

array_var...由于提供了初始化程序, 因此声称是 的定义。但是,对于数组类型的变量,初始化程序的格式不正确。数组初始值设定项由一个或多个数组元素的逗号分隔序列组成,位于强制大括号 ( {}) 内。正如 C 不支持整个数组赋值一样,它也不支持将数组值作为数组初始值设定项。


相比之下,这...

extern STRUCT_TYPEDEF *array_var;

... 是具有外部链接的指针的声明。它有一个完整的类型。和这个 ...

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

... 是变量的有效定义,具有合适的初始值设定项。因为数组值在这种情况下衰减为指针,就像在大多数(但不是全部)其他情况下一样,它等价于:

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

在将其与原始代码进行比较时,必须了解尽管它们具有密切关联,但指针和数组是完全独立的类型。

此外,这部分代码仅在“array_var[0].property = ...”之类的函数中被引用,但这些函数中没有一个被应用程序调用过。

变量是否被正常访问与编译器是否愿意接受代码无关。

[] 和 * 我不知道之间有什么区别吗?

显然是这样,因为这个问题似乎假设没有区别。

这两种形式可以互换使用来声明函数参数。在这种情况下,两者都将参数声明为指针。这是一个符号和代码清晰的便利,因为数组值作为函数参数出现衰减为指针。你永远不能真正将数组作为函数参数传递——当参数指定数组时,会传递一个指针。

然而,如上所述,这两种形式并不等同于声明普通变量。

于 2016-05-19T17:50:14.370 回答