调用 menu_layer 时字符串损坏

String corruption when calling menu_layer

我很难在我的 pebble watch 应用程序中找到一个奇怪的错误。我怀疑这是一个内存错误,但我找不到我的错误。我有一个字符串数组,当我调用 menu_layer = menu_layer_create(bounds); 时,它似乎以某种方式破坏了我的字符串。我在下面更全面地解释。错误的描述以粗体显示。

我有一个 header 文件,其中将三个变量声明为外部变量,因此它们是全局变量。

//externs.h
#ifndef EXTERNS_H
#define EXTERNS_H

// These global variables are accessible by all source files. Modified in main.c
extern int str_count;
extern char **str_titles;
extern char **str_teasers;

#endif

这三个变量在main.c中修改,但在其他c文件中使用。下面是我的 main.c 文件的示例,我在其中设置了字符串数组 str_titlesstr_teasers。结构info包含两个很长的字符串,但用分隔符|分隔。我将这些字符串复制到临时缓冲区 s_buffer 并使用 strtok 将它们分开解析,将每个新字符串保存到我的字符串数组中。

这似乎很有效,我已经检查了 for 循环中的每个字符串,最后一个字符始终是 null-terminated 字节,之前的字符是一个句点(句子的结尾)。我不会在其他任何地方修改这些值,直到我的程序结束时我才会释放它们(因为它们应该在程序的生命周期内存在)。

我创建了一个包含动态条目数(在本例中为 6)的菜单,每个菜单的标题为 str_titles 中的 6 个字符串之一。这里没有问题。我可以在我的程序中随时遍历 APP_LOG 这个字符串数组,没有问题。

当按下每个菜单项时,它应该在滚动层中显示 str_teasers 中较长的字符串。它仅对前三个菜单项可靠地执行此操作。对于最后三个,它始终是空白的。尝试在此处使用 APP_LOG 迭代和打印字符串数组会在与 pebble 日志一起使用的 python 框架中产生一连串错误,并且总是以类似以下内容结尾:

UnicodeDecodeError: 'utf8' codec can't decode byte 0x98 in position 0: invalid start byte

最后一部分invalid start byte有时有些不同,解码字节和位置根据字符串而变化。请注意,在日志中,最后三个空白菜单项总是会产生一些错误,即使滚动层不是空白并显示正确的文本,有时也会对前三个菜单项产生错误(对于前三个,有时它会打印到日志没有问题)。

我已经在 str_teasers 的不同点尝试了 APP_LOG,当它失败时,它会在我调用 menu_layer = menu_layer_create(bounds); 创建我的菜单后这样做。在我进行该调用之前,我可以毫无问题地打印所有带有 APP_LOG 的字符串。我认为这是一个堆损坏错误,但我在创建层之前和之后都有可用的堆内存(~8100 字节),我的应用程序没有崩溃。

也许我遗漏了一些非常简单的东西,但我找不到我的错误。我相信我已经正确地为 str_teasers 分配了内存,所以我根本不明白为什么要修改它。我在下面包含了修改后的示例代码以供参考。

//main.c
#include "strtok.h"
#include "externs.h"

int str_count;
char **str_titles;
char **str_teasers;

char *s_buffer;
const char delim[1] = "|";
char *token;

typedef struct {
  int s_count;
  char* s_titles;
  char* s_teasers;
} s_info;
s_info info;


// Sample code

str_count = info.s_count;

// Declare arrays of appropriate size
str_titles = malloc(str_count * sizeof(char*));
str_teasers = malloc(str_count * sizeof(char*));

// This creates a copy of the entire string s_titles into s_buffer
int len = strlen(info.s_titles) + 1;
s_buffer = (char *)malloc(len);
strcpy(s_buffer, info.s_titles);

token = strtok(s_buffer, delim); // Get the first token for the titles

// Walk through the other tokens
int counter = 0;
while(token != NULL) {
  *(str_titles + counter) = malloc((strlen(token) + 1) * sizeof(char));
  *(str_titles + counter) = token;
  token = strtok(NULL, delim);
  counter++;
}

// This creates a copy of the entire string s_teasers into s_buffer
len = strlen(info.s_teasers) + 1;
s_buffer = (char *)realloc(s_buffer, len);
strcpy(s_buffer, info.s_teasers);

token = strtok(s_buffer, delim); // Get the first token for the teasers

// Walk through the other tokens
counter = 0;
while(token != NULL) {
  *(str_teasers + counter) = malloc((strlen(token) + 1) * sizeof(char));
  *(str_teasers + counter) = token;
  token = strtok(NULL, delim);
  counter++;
}
free(s_buffer);

在此代码块中

// Walk through the other tokens
int counter = 0;
while(token != NULL) {
  *(str_titles + counter) = malloc((strlen(token) + 1) * sizeof(char));
  *(str_titles + counter) = token;
  token = strtok(NULL, delim);
  counter++;
}

您分配内存但立即用令牌指针覆盖指针。然后,在另一个类似的代码块中,您已经覆盖了这些指向的字符串。您可能已经 "lucky" 因为内存被重新分配,并且之前的字符串内存仍未被其他进程触及。事实上,您最终还是释放了这个字符串缓冲区,而没有复制标记。

我想你需要 strcpy 这里

// Walk through the other tokens
int counter = 0;
while(token != NULL) {
  *(str_titles + counter) = malloc((strlen(token) + 1) * sizeof(char));
  strcpy(*(str_titles + counter), token);         // <<--- here
  token = strtok(NULL, delim);
  counter++;
}

预告片代码块也是如此。

我还注意到您按 counter 编制索引,但没有根据提供给您的 str_count 进行检查,后者用于为字符串指针数组分配内存。