I'm having a very hard time tracing down a strange error in my pebble watch app. I suspect it's a memory error but I can't find my mistake. I have an array of strings, and when I call menu_layer = menu_layer_create(bounds);
it somehow seems to be corrupting my string. I explain more fully below. The description of the error is bolded.
I have a header files which declares three variables as extern so that they're global.
//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
These three variables are modified in main.c, but are used in other c files. Below is a sample of my main.c file where I set the array of strings str_titles
and str_teasers
. The structure info
contains two strings which are very long but are separated with the delimiter |
. I copy these strings into a temporary buffer s_buffer
and parse them apart with strtok
, saving each new string into my array of strings.
This seems to work well, I've checked each string in a for loop and the last character is always the null-terminated byte and the one previous to it is a period (end of the sentence). I don't modify these values anywhere else, and I don't free them until the very very end of my program (because they should exist for the program's life).
I create a menu with a dynamic number of entries (in this case 6), and each menu has the title of one of the 6 strings within str_titles
. There are no issues here. I can iterate through and APP_LOG
this array of strings with no problem at any time in my program.
When each menu item is pressed, it should display the longer string from str_teasers
in a scroll layer. It does this reliably only for the first three menu items. For the last three it is always blank. Trying to iterate and print the array of strings here with APP_LOG
produces a litany of errors in the python framework used with the pebble logs and always ends with something like:
UnicodeDecodeError: 'utf8' codec can't decode byte 0x98 in position 0: invalid start byte
The last part invalid start byte
is sometimes something different, and the decode bytes and position changes based on the string. Note that in the logs always produces some error for the last three blank menu items, and only sometimes does it produce the error for the first three even if the scroll layer is not blank and displays the correct text (for the first three sometimes it prints to the logs without issue).
I've tried APP_LOG
on str_teasers
at various points, and when it fails, it does so after I call menu_layer = menu_layer_create(bounds);
to create my menu. Before I make that call I can print all the strings with APP_LOG with no issue at all. I thought it was a heap corruption error, but I have available heap memory (~8100 Bytes) before and after the creation of the layer and my app doesn't crash.
Maybe I'm missing something very simple, but I can't find my mistake. I've allocated the memory for str_teasers
correctly I believe, so I don't see why it should be modified at all. I've included a modified sample code below for reference.
//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);
In this code block
// 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++;
}
you allocate memory but immediately overwrite the pointer with the token pointer. Then, in the other, similar code block, you have overwritten the string into which these point. You might have been "lucky" since the memory was reallocated, and the previous string memory was still untouched by other processes. And indeed, you end by freeing this string buffer anyway, without having copied the tokens.
I think you need a strcpy
here
// 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++;
}
Similarly for the teasers code block.
I also notice that you index by counter
but have not checked it against the str_count
you were supplied with, which was used to allocate memory for the array of string pointers.