Pebble C 中的数组中断
Array breaking in Pebble C
我正在尝试在 CloudPebble 上使用 C 在 Pebble 中创建一个简单的掷骰子应用程序。我有一系列不同的模具尺寸,您可以使用 Up/Down 滚动浏览,然后使用中间按钮滚动(目前只生成一个随机数,稍后会变得更漂亮)。顶部还有一个标签,显示当前的骰子。
大部分时间都可以使用,但有一个我一辈子都弄不明白的错误:
- 滚动骰子时,至少有一个骰子不会显示骰子号码
- 破碎的骰子仍然会滚动,但是 rand() 函数似乎没有上限(或者上限可能是 1000;我在破碎的骰子上滚动测试的最高值是 880)
刚开始的时候,D6坏了。我修补和调整并添加了一些代码以查看后台发生了什么,但突然 D6 开始工作(不知道为什么)并且 D20 坏了。我尝试将数组中的“20”项更改为“35”,只是为了看看它会做什么,现在的 D35 可以工作,但 D2 和 D4 坏了。我改回来了,不同的骰子坏了。
附件是我当前的代码,目前显示 D20 和 D4 已损坏。我已经删除了所有用于故障排除的垃圾代码、返工代码和辅助代码,因为其中 none 有帮助。
谁能帮忙解释一下为什么骰子会随机破开,如何解决?谢谢!
#include <pebble.h>
static Window *s_main_window;
static TextLayer *s_label_layer;
static TextLayer *s_output_layer;
static int dice_select = 6;
static char *die_label = "D";
static char *dice_array[] = {"2", "4", "6", "8", "10", "12", "20", "100", NULL};
// === Separate Processes ===
static void update_label(char *die) {
die_label[1] = '[=10=]';
strcat(die_label, die);
text_layer_set_text(s_label_layer, die_label);
}
// === Main Window Processes ===
static void main_window_load(Window *window) {
Layer *window_layer = window_get_root_layer(window);
GRect window_bounds = layer_get_bounds(window_layer);
// Create label TextLayer
s_label_layer = text_layer_create(GRect(5, 0, window_bounds.size.w - 10, 24));
text_layer_set_font(s_label_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
text_layer_set_text_alignment(s_label_layer, GTextAlignmentCenter);
text_layer_set_text(s_label_layer, "D20");
text_layer_set_overflow_mode(s_label_layer, GTextOverflowModeWordWrap);
layer_add_child(window_layer, text_layer_get_layer(s_label_layer));
// Create output TextLayer
s_output_layer = text_layer_create(GRect(5, 24, window_bounds.size.w - 10, window_bounds.size.h - 24));
text_layer_set_font(s_output_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28));
text_layer_set_text(s_output_layer, "No button pressed yet.");
text_layer_set_overflow_mode(s_output_layer, GTextOverflowModeWordWrap);
layer_add_child(window_layer, text_layer_get_layer(s_output_layer));
}
static void main_window_unload(Window *window) {
// Destroy output TextLayer
text_layer_destroy(s_label_layer);
text_layer_destroy(s_output_layer);
}
// === Button Handlers ===
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
// Increment dice selection
if(dice_select < 7) {
dice_select += 1;
}
update_label(dice_array[dice_select]);
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
// Establish die selection
char *die = dice_array[dice_select];
int die_sides = atoi(die);
// Roll die
int result = rand() % die_sides + 1;
char *result_str = "";
snprintf(result_str, sizeof(result_str), "%d", result);
// Print result
text_layer_set_text(s_output_layer, result_str);
}
static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
// Increment dice selection
if(dice_select > 0) {
dice_select -= 1;
}
update_label(dice_array[dice_select]);
}
static void click_config_provider(void *context) {
// Register the ClickHandlers
window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
}
// === Initialize/Deinitialize App ===
static void init() {
// Create main Window
s_main_window = window_create();
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = main_window_load,
.unload = main_window_unload,
});
window_set_click_config_provider(s_main_window, click_config_provider);
window_stack_push(s_main_window, true);
}
static void deinit() {
// Destroy main Window
window_destroy(s_main_window);
}
int main(void) {
init();
app_event_loop();
deinit();
}
(注:我是一个未经训练的自学初学者,所以像"Why did you do this that way?"这样的所有问题的答案都是"Because I didn't know any better.")
编辑:根据评论添加的新代码。
这部分效果很好:
static char die_label[32] = "D";
static void update_label(char *die) {
snprintf(die_label, sizeof(die_label), "D%s", die);
text_layer_set_text(s_label_layer, die_label);
}
但是这部分将不再打印掷骰结果:
// Roll die
int result = rand() % die_sides + 1;
char result_str[32];
snprintf(result_str, sizeof(result_str), "%d", result);
// Print result
text_layer_set_text(s_output_layer, result_str);
问题出在这一行
static char *die_label = "D";
指向 die_label
的内存区域 a) 不应写入,并且 b) 仅有两个字符 space,D
和 [=17=]
终止符。所以 strcat
正在写入不应该写入的内存。
解决方案是将die_label
声明为
static char die_label[32] = "D"; // reserve space for 32 characters
然后我建议将更新功能更改为
static void update_label(char *die)
{
sprintf(die_label, "D%s", die);
text_layer_set_text(s_label_layer, die_label);
}
这两行你有同样的问题
char *result_str = "";
snprintf(result_str, sizeof(result_str), "%d", result);
在这种情况下,一个字符只有 space,但传递给 snprintf
的长度是指针的大小,它是 4 或 8 个字节。我会将这些行更改为
char result_str[32];
sprintf( result_str, "%d", result );
回应更新后的问题:
是的,我想知道这一点。显然 text_layer_set_text
不会复制字符串,它只是保留一个指向它的指针。这意味着 result_str
不能是局部变量,因为一旦函数 returns,局部变量就会超出范围。解决方案是像 die_label
一样在文件顶部声明 result_str
,或者在函数本身中将其声明为 static
。
我正在尝试在 CloudPebble 上使用 C 在 Pebble 中创建一个简单的掷骰子应用程序。我有一系列不同的模具尺寸,您可以使用 Up/Down 滚动浏览,然后使用中间按钮滚动(目前只生成一个随机数,稍后会变得更漂亮)。顶部还有一个标签,显示当前的骰子。
大部分时间都可以使用,但有一个我一辈子都弄不明白的错误:
- 滚动骰子时,至少有一个骰子不会显示骰子号码
- 破碎的骰子仍然会滚动,但是 rand() 函数似乎没有上限(或者上限可能是 1000;我在破碎的骰子上滚动测试的最高值是 880)
刚开始的时候,D6坏了。我修补和调整并添加了一些代码以查看后台发生了什么,但突然 D6 开始工作(不知道为什么)并且 D20 坏了。我尝试将数组中的“20”项更改为“35”,只是为了看看它会做什么,现在的 D35 可以工作,但 D2 和 D4 坏了。我改回来了,不同的骰子坏了。
附件是我当前的代码,目前显示 D20 和 D4 已损坏。我已经删除了所有用于故障排除的垃圾代码、返工代码和辅助代码,因为其中 none 有帮助。
谁能帮忙解释一下为什么骰子会随机破开,如何解决?谢谢!
#include <pebble.h>
static Window *s_main_window;
static TextLayer *s_label_layer;
static TextLayer *s_output_layer;
static int dice_select = 6;
static char *die_label = "D";
static char *dice_array[] = {"2", "4", "6", "8", "10", "12", "20", "100", NULL};
// === Separate Processes ===
static void update_label(char *die) {
die_label[1] = '[=10=]';
strcat(die_label, die);
text_layer_set_text(s_label_layer, die_label);
}
// === Main Window Processes ===
static void main_window_load(Window *window) {
Layer *window_layer = window_get_root_layer(window);
GRect window_bounds = layer_get_bounds(window_layer);
// Create label TextLayer
s_label_layer = text_layer_create(GRect(5, 0, window_bounds.size.w - 10, 24));
text_layer_set_font(s_label_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
text_layer_set_text_alignment(s_label_layer, GTextAlignmentCenter);
text_layer_set_text(s_label_layer, "D20");
text_layer_set_overflow_mode(s_label_layer, GTextOverflowModeWordWrap);
layer_add_child(window_layer, text_layer_get_layer(s_label_layer));
// Create output TextLayer
s_output_layer = text_layer_create(GRect(5, 24, window_bounds.size.w - 10, window_bounds.size.h - 24));
text_layer_set_font(s_output_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28));
text_layer_set_text(s_output_layer, "No button pressed yet.");
text_layer_set_overflow_mode(s_output_layer, GTextOverflowModeWordWrap);
layer_add_child(window_layer, text_layer_get_layer(s_output_layer));
}
static void main_window_unload(Window *window) {
// Destroy output TextLayer
text_layer_destroy(s_label_layer);
text_layer_destroy(s_output_layer);
}
// === Button Handlers ===
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
// Increment dice selection
if(dice_select < 7) {
dice_select += 1;
}
update_label(dice_array[dice_select]);
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
// Establish die selection
char *die = dice_array[dice_select];
int die_sides = atoi(die);
// Roll die
int result = rand() % die_sides + 1;
char *result_str = "";
snprintf(result_str, sizeof(result_str), "%d", result);
// Print result
text_layer_set_text(s_output_layer, result_str);
}
static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
// Increment dice selection
if(dice_select > 0) {
dice_select -= 1;
}
update_label(dice_array[dice_select]);
}
static void click_config_provider(void *context) {
// Register the ClickHandlers
window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
}
// === Initialize/Deinitialize App ===
static void init() {
// Create main Window
s_main_window = window_create();
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = main_window_load,
.unload = main_window_unload,
});
window_set_click_config_provider(s_main_window, click_config_provider);
window_stack_push(s_main_window, true);
}
static void deinit() {
// Destroy main Window
window_destroy(s_main_window);
}
int main(void) {
init();
app_event_loop();
deinit();
}
(注:我是一个未经训练的自学初学者,所以像"Why did you do this that way?"这样的所有问题的答案都是"Because I didn't know any better.")
编辑:根据评论添加的新代码。
这部分效果很好:
static char die_label[32] = "D";
static void update_label(char *die) {
snprintf(die_label, sizeof(die_label), "D%s", die);
text_layer_set_text(s_label_layer, die_label);
}
但是这部分将不再打印掷骰结果:
// Roll die
int result = rand() % die_sides + 1;
char result_str[32];
snprintf(result_str, sizeof(result_str), "%d", result);
// Print result
text_layer_set_text(s_output_layer, result_str);
问题出在这一行
static char *die_label = "D";
指向 die_label
的内存区域 a) 不应写入,并且 b) 仅有两个字符 space,D
和 [=17=]
终止符。所以 strcat
正在写入不应该写入的内存。
解决方案是将die_label
声明为
static char die_label[32] = "D"; // reserve space for 32 characters
然后我建议将更新功能更改为
static void update_label(char *die)
{
sprintf(die_label, "D%s", die);
text_layer_set_text(s_label_layer, die_label);
}
这两行你有同样的问题
char *result_str = "";
snprintf(result_str, sizeof(result_str), "%d", result);
在这种情况下,一个字符只有 space,但传递给 snprintf
的长度是指针的大小,它是 4 或 8 个字节。我会将这些行更改为
char result_str[32];
sprintf( result_str, "%d", result );
回应更新后的问题:
是的,我想知道这一点。显然 text_layer_set_text
不会复制字符串,它只是保留一个指向它的指针。这意味着 result_str
不能是局部变量,因为一旦函数 returns,局部变量就会超出范围。解决方案是像 die_label
一样在文件顶部声明 result_str
,或者在函数本身中将其声明为 static
。