如何在 C 中正确创建数组的副本或设置对一个数组的引用?
How do I correctly create a copy of an array in C or set a reference to one?
所以,我是 C 的新手,来自 Java/C# 背景,到目前为止我还不能完全理解它。
我想做的是对微控制器进行编程(在本例中为 Adafruit Feather 运行ning atmega32u4)以充当 Nintendo Switch 的 USB 耦合控制器和 运行 自动命令.
The project I'm trying to expand upon 正在使用这样的命令结构数组:
typedef struct {
Buttons_t button;
uint16_t duration; // 1 equals 0.025s on ATmega32u4 => 1s = 40
} command;
static const command loop[] = {
// do stuff
{ NOTHING, 150 },
{ TRIGGERS, 15 }, { NOTHING, 150 },
{ TRIGGERS, 15 }, { NOTHING, 150 },
{ A, 5 }, { NOTHING, 250 }
};
最初这就是它的全部内容,程序会循环执行命令,向控制台发送一个按钮,然后 "hold" 它会持续定义的时间段。当程序运行数组结束时,它会简单地重置索引并重新开始。
现在,我尝试根据一些简单的 if..else 查询向控制台发送不同的命令。具体来说,该程序将从日、月和年变量(Switch 控制台当前设置的日期)开始,然后逐个向前滚动日期以达到未来的设定日期。为此,我想在每个 'step' 检查日期 +1 天是否有效 as described in this tutorial 并根据结果滚动一天、一天和一个月或一天、一个月和一年前。然后我希望它在一定天数后结束。
我写了几个命令数组来表示设置控制器所需的不同步骤,移动到它应该循环的地方,像这样滚动一天、一个月或一年:
static const command setupController[] = {
// Setup controller
...
};
static const command moveToLoop[] = {
// Go into date settings
...
};
static const command rollDay[] = {
//roll to next day
...
};
static const command rollMonth[] = {
//roll to next month
...
};
static const command rollYear[] = {
//roll to next year
...
};
我想将它们复制到另一个数组中:
#define COMMANDMAXSIZE 100
static command activeCommand[COMMANDMAXSIZE];
我知道这(极度)浪费内存,但我的 C 语言绝对不够好,无法提出更高级、更保守的解决方案。
然后我进入我的程序,它看起来像这样:
int main(void) {
SetupHardware(); //Irrelevant, because it is exactly like I downloaded it and it works even with the bumbling changes I've made
GlobalInterruptEnable(); //Ditto
RunOnce(setupController);
RunOnce(moveToLoop);
while (daysSkipped != stopDay)
{
if (datevalid((dayOfMonth + 1), month, year)) {
dayOfMonth++;
RunOnce(rollDay);
}
else if (datevalid(1, (month + 1), year)) {
dayOfMonth = 1;
month++;
RunOnce(rollMonth);
}
else if (datevalid(1, 1, (year + 1))) {
dayOfMonth = 1;
month = 1;
year++;
RunOnce(rollYear);
}
daysSkipped++;
}
}
最后(我发誓我很快就会完成),RunOnce 的开始看起来像这样
void RunOnce(command stepsToRun[]) {
memcpy(activeCommand, stepsToRun, sizeof(activeCommand)); //set the setup commands to be active
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
...
稍后在程序中,将命令转换为控制台按钮的任务实际上 运行 是一个固定数组,所以我想我只是 "mark" 命令到 运行 为活动数组,并且仅 运行 为活动数组。只是,它没有按预期工作:
程序运行s,设置控制器,移动到日期设置,确实开始滚动日期,但是,无论第二天是否有效,它都会向前滚动一个月, 然后一年然后它卡住向上移动模拟模拟摇杆并无限期地按 A.
我想问题出在我的 memcpy 上,它用接下来 运行 想要的步骤覆盖活动数组,但我想不出解决它的方法。我尝试编写一个函数,该函数应该使用 for 循环逐个元素地覆盖活动数组元素,但这样一来,控制器甚至无法正确设置自己,实际上什么也没有发生。通常,对于任何类型的输出功能,我都会尝试在感兴趣的地方进行打印,但我几乎无法获得关于我的微控制器的反馈。
如有任何帮助,我们将不胜感激。
当您将数组传递给函数时,它会衰减为指针。
RunOnce(rollYear);
因此
void RunOnce(command stepsToRun[]) {
memcpy(activeCommand, stepsToRun, sizeof(activeCommand)); //set the setup commands to be active
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
}
sizeof(stepsToRun)
没有产生您预期的正确结果,因为它现在 sizeof(pointer)
正在运行。
您必须将数组的大小作为额外参数传递给 RunOnce
函数。
忽略对数据进行硬拷贝非常缓慢和浪费,这确实也是不正确的。
memcpy(activeCommand, stepsToRun, sizeof(activeCommand));
这里需要复制你传递的数据的大小,而不是目标缓冲区的大小!现在你最终复制了比你拥有的更多的数据,因为所有这些声明 static const command rollDay[]
等都会根据初始化列表中的项目数量获得可变大小。
解决您眼前问题的快速而肮脏的方法是传递尺寸:
void RunOnce(size_t size, command stepsToRun[size])
{
memcpy(activeCommand, stepsToRun, size);
然后用RunOnce(sizeof rollDay, rollDay);
等调用这个函数
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
部分也不正确,但不是该错误的直接原因。参见 How to find the 'sizeof' (a pointer pointing to an array)? and What is array to pointer decay? 等
所以,我是 C 的新手,来自 Java/C# 背景,到目前为止我还不能完全理解它。
我想做的是对微控制器进行编程(在本例中为 Adafruit Feather 运行ning atmega32u4)以充当 Nintendo Switch 的 USB 耦合控制器和 运行 自动命令.
The project I'm trying to expand upon 正在使用这样的命令结构数组:
typedef struct {
Buttons_t button;
uint16_t duration; // 1 equals 0.025s on ATmega32u4 => 1s = 40
} command;
static const command loop[] = {
// do stuff
{ NOTHING, 150 },
{ TRIGGERS, 15 }, { NOTHING, 150 },
{ TRIGGERS, 15 }, { NOTHING, 150 },
{ A, 5 }, { NOTHING, 250 }
};
最初这就是它的全部内容,程序会循环执行命令,向控制台发送一个按钮,然后 "hold" 它会持续定义的时间段。当程序运行数组结束时,它会简单地重置索引并重新开始。
现在,我尝试根据一些简单的 if..else 查询向控制台发送不同的命令。具体来说,该程序将从日、月和年变量(Switch 控制台当前设置的日期)开始,然后逐个向前滚动日期以达到未来的设定日期。为此,我想在每个 'step' 检查日期 +1 天是否有效 as described in this tutorial 并根据结果滚动一天、一天和一个月或一天、一个月和一年前。然后我希望它在一定天数后结束。
我写了几个命令数组来表示设置控制器所需的不同步骤,移动到它应该循环的地方,像这样滚动一天、一个月或一年:
static const command setupController[] = {
// Setup controller
...
};
static const command moveToLoop[] = {
// Go into date settings
...
};
static const command rollDay[] = {
//roll to next day
...
};
static const command rollMonth[] = {
//roll to next month
...
};
static const command rollYear[] = {
//roll to next year
...
};
我想将它们复制到另一个数组中:
#define COMMANDMAXSIZE 100
static command activeCommand[COMMANDMAXSIZE];
我知道这(极度)浪费内存,但我的 C 语言绝对不够好,无法提出更高级、更保守的解决方案。
然后我进入我的程序,它看起来像这样:
int main(void) {
SetupHardware(); //Irrelevant, because it is exactly like I downloaded it and it works even with the bumbling changes I've made
GlobalInterruptEnable(); //Ditto
RunOnce(setupController);
RunOnce(moveToLoop);
while (daysSkipped != stopDay)
{
if (datevalid((dayOfMonth + 1), month, year)) {
dayOfMonth++;
RunOnce(rollDay);
}
else if (datevalid(1, (month + 1), year)) {
dayOfMonth = 1;
month++;
RunOnce(rollMonth);
}
else if (datevalid(1, 1, (year + 1))) {
dayOfMonth = 1;
month = 1;
year++;
RunOnce(rollYear);
}
daysSkipped++;
}
}
最后(我发誓我很快就会完成),RunOnce 的开始看起来像这样
void RunOnce(command stepsToRun[]) {
memcpy(activeCommand, stepsToRun, sizeof(activeCommand)); //set the setup commands to be active
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
...
稍后在程序中,将命令转换为控制台按钮的任务实际上 运行 是一个固定数组,所以我想我只是 "mark" 命令到 运行 为活动数组,并且仅 运行 为活动数组。只是,它没有按预期工作:
程序运行s,设置控制器,移动到日期设置,确实开始滚动日期,但是,无论第二天是否有效,它都会向前滚动一个月, 然后一年然后它卡住向上移动模拟模拟摇杆并无限期地按 A.
我想问题出在我的 memcpy 上,它用接下来 运行 想要的步骤覆盖活动数组,但我想不出解决它的方法。我尝试编写一个函数,该函数应该使用 for 循环逐个元素地覆盖活动数组元素,但这样一来,控制器甚至无法正确设置自己,实际上什么也没有发生。通常,对于任何类型的输出功能,我都会尝试在感兴趣的地方进行打印,但我几乎无法获得关于我的微控制器的反馈。
如有任何帮助,我们将不胜感激。
当您将数组传递给函数时,它会衰减为指针。
RunOnce(rollYear);
因此
void RunOnce(command stepsToRun[]) {
memcpy(activeCommand, stepsToRun, sizeof(activeCommand)); //set the setup commands to be active
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
}
sizeof(stepsToRun)
没有产生您预期的正确结果,因为它现在 sizeof(pointer)
正在运行。
您必须将数组的大小作为额外参数传递给 RunOnce
函数。
忽略对数据进行硬拷贝非常缓慢和浪费,这确实也是不正确的。
memcpy(activeCommand, stepsToRun, sizeof(activeCommand));
这里需要复制你传递的数据的大小,而不是目标缓冲区的大小!现在你最终复制了比你拥有的更多的数据,因为所有这些声明 static const command rollDay[]
等都会根据初始化列表中的项目数量获得可变大小。
解决您眼前问题的快速而肮脏的方法是传递尺寸:
void RunOnce(size_t size, command stepsToRun[size])
{
memcpy(activeCommand, stepsToRun, size);
然后用RunOnce(sizeof rollDay, rollDay);
等调用这个函数
activeBounds = sizeof(stepsToRun) / sizeof(stepsToRun[0]);
部分也不正确,但不是该错误的直接原因。参见 How to find the 'sizeof' (a pointer pointing to an array)? and What is array to pointer decay? 等