c - Pebble C 中的数组中断

标签 c arrays pebble-watch cloudpebble

我正在尝试在 CloudPebble 上使用 C 在 Pebble 中创建一个简单的掷骰子应用程序。我有一系列不同的模具尺寸,您可以使用向上/向下滚动浏览,然后使用中间按钮滚动(目前只生成一个随机数,稍后会变得更漂亮)。顶部还有一个标签,显示当前的骰子。

它大部分时间都在工作,但是有一个我一辈子都弄不明白的错误:

  • 滚动骰子时,至少有一个骰子不会显示骰子数
  • 损坏的骰子仍会滚动,但 rand() 函数似乎没有上限(或者上限可能为 1000;我在损坏的骰子上滚动测试的最高值为 880)<

刚开始的时候,D6坏了。我修补和调整并添加了一些代码以查看后台发生了什么,但突然 D6 开始工作(不知道为什么)并且 D20 坏了。我尝试将数组中的“20”项更改为“35”,只是为了看看它会做什么,现在的 D35 可以工作,但 D2 和 D4 坏了。我把它改回来了,不同的骰子坏了。

附件是我当前的代码,目前显示 D20 和 D4 已损坏。我已经删除了所有用于故障排除的垃圾代码、返工代码和辅助代码,因为它们都没有帮助。

任何人都可以帮助解释为什么骰子看似随机破裂,以及如何解决它?谢谢!

#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] = '\0';
      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();
}

(注意:我是一个未经训练的自学成才的初学者,所以对诸如“你为什么那样做?”之类的所有问题的答案都是“因为我不知道更好。”)

编辑:根据评论添加的新代码。

这部分效果很好:

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) 只能容纳两个字符、D\0 终止符。所以 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);

在这种情况下,您只有一个字符的空间,但传递给 snprintf 的长度是指针的大小,它是 4 或 8 个字节。我会将这些行更改为

char result_str[32];
sprintf( result_str, "%d", result );

针对更新后的问题:

是的,我想知道这一点。显然 text_layer_set_text 没有复制字符串,它只是保留一个指向它的指针。这意味着 result_str 不能是局部变量,因为一旦函数返回,局部变量就会超出范围。解决方案是像die_label一样在文件顶部声明result_str,或者在函数本身中将其声明为static

关于c - Pebble C 中的数组中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30993521/

相关文章:

java - 为什么 Java 中的字符数组接受整数值?

python - Python 是否有像 Ruby 的 %w(things in array) 这样创建数组的快捷方式

javascript - PebbleJS 中的日期格式化和操作

javascript - Pebble 返回托架在模拟器上工作,但在 watch 上不起作用

c - Matlab:调试C库,可能吗?

c - 如何将二进制整数写入二进制文件 | C

C - 如何绘制星号中相邻矩形条的轮廓

android - 使用 PebbleKit 将 bool 值发送到 Pebble

c - C 的工作良好且全面的 ADT

c - 我是否正确地使用 OpenSSL 对缓冲区进行了 base64 编码?