【问题标题】:String corruption when calling menu_layer [closed]调用 menu_layer 时字符串损坏 [关闭]
【发布时间】:2016-07-12 09:52:20
【问题描述】:

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

我有一个头文件,其中将三个变量声明为 extern,以便它们是全局的。

//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 结尾的字节,而前面的一个是句点(句子的结尾)。我不会在其他任何地方修改这些值,并且直到我的程序结束时才释放它们(因为它们应该在程序的生命周期中存在)。

我创建了一个具有动态条目数(在本例中为 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);

【问题讨论】:

  • 您似乎没有main 函数。可执行语句必须在函数内。您是否将stdlib.h 包括在mallocstring.h 用于strcpy?您是否提供了自己的strtok?如果是这样,不建议复制库函数名称。请参阅MCVE
  • 这只是我的main.c 文件中的一个示例,它确实具有必要的main 功能。实际功能要大得多,所以这只是相关部分。是的,我必须提供我自己的 strtok,因为 pebble 不支持官方库名称,我是从 here 获得的。我已经检查过了,它似乎确实返回了以空字符结尾的正确字符串(无论如何strlen 会返回 null 我相信如果不是)。
  • 您需要添加有问题的可执行代码。我们无法执行的代码块很难提供帮助。此外,如果您在这里倾倒大量代码,那么您似乎并没有尝试自己缩小问题范围。
  • @Harry。只有当你有一块鹅卵石手表并且我为你提供了我所有的源代码时,这个问题才会重现。这就是为什么我提供了相关的评论部分和对我的错误的解释,希望有人能看到我可能遗漏的明显内容。

标签: c string pebble-watch pebble-sdk


【解决方案1】:

在这个代码块中

// 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++;
}

您分配了内存,但立即用令牌指针覆盖了指针。然后,在另一个类似的代码块中,您已经覆盖了这些指向的字符串。自从重新分配内存以来,您可能一直很“幸运”,并且之前的字符串内存仍然没有被其他进程触及。事实上,无论如何,你都会通过释放这个字符串缓冲区来结束,而无需复制令牌。

我想你需要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 检查它,该str_count 用于为字符串指针数组分配内存。

【讨论】:

  • 是的,你是对的,我做了你的更正,它按预期工作。我很幸运,这就是我怀疑发生的事情,但不知道为什么。我对 C 语言还是有点生疏,所以有时我会错过像这样非常明显的错误。此外,我不会检查str_count 与计数器,因为我 100% 确定我的数组中的字符串数量与 str_count 的值相同。这些是来自配套 .js 应用程序的变量,其中字符串的数量显式取决于 str_count 的值。感谢您的帮助!
  • 感谢您的投票!重新检查范围:从不信任外部数据源。要健壮。实施起来可能会很痛苦,但如果你把它作为你的工作方式,它会带来很大的回报。
  • 这是一个非常好的建议,谢谢。我一定会实施这项检查以提高稳健性。
猜你喜欢
  • 2013-02-25
  • 1970-01-01
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多