Converting char array to string [and Pebble]

2019-09-02 05:32发布

问题:

I have a char array which I am trying to turn into a char pointer to a string. I believe this involves getting the pointer to the first element of the char array, and adding a null character to the end of the char array. The reason for this is that I am trying to then pass it to a SimpleMenuItem for the pebble smartwatch, where .title needs to get a char*, pointing to a string.

While I've been able to get the char array filled and (I think) added the null character and gotten the pointer, I am unable to see the title on my pebble. I'm not sure whether this is a pebble issue or an issue of my C understanding, but I feel heavily that it may be the former.

Pebble code (C):

void in_received_handler(DictionaryIterator *received, void *context) {
    dataReceived = dict_read_first(received);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "read first");

    while (dataReceived != NULL){

        if (dataReceived->key == 0x20) {

            //original is of the format "# random string", i.e. "4 test name"
            char* original = dataReceived->value->cstring;
            APP_LOG(APP_LOG_LEVEL_DEBUG, original);

            char originalArray[strlen(original)];
            //copy over to originalArray
            for (unsigned int i = 0; i < strlen(original); i++) {
                originalArray[i] = original[i];
            }

            char* keysplit = strtok(originalArray, " ");
            APP_LOG(APP_LOG_LEVEL_DEBUG, keysplit);

            //key
            int key = atoi(keysplit);
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Int Key: %d", key);

            //good until here
            char remainderArray[sizeof(originalArray)-strlen(keysplit) +1];

            //assign rest of string to new array
            for (unsigned int i = 1; i < sizeof(remainderArray)-1; i++){
                APP_LOG(APP_LOG_LEVEL_DEBUG, "Character             : %c", originalArray[i+strlen(keysplit)]);
                remainderArray[i] = originalArray[i+strlen(keysplit)];
                APP_LOG(APP_LOG_LEVEL_DEBUG, "Character in new Array: %c", remainderArray[i]);
            }

            remainderArray[sizeof(remainderArray)-1] = '\0';

            //data is sucesfully placed into remainderArray
            char* ptr = remainderArray;
            strncpy(ptr, remainderArray, sizeof(remainderArray)+1);
            ptr[sizeof(remainderArray)+1] = '\0';

            chats[key] = (SimpleMenuItem){
                // You should give each menu item a title and callback
                .title = &remainderArray[0],
                .callback = selected_chat,
            };

        }

        dataReceived = dict_read_next(received);
        APP_LOG(APP_LOG_LEVEL_DEBUG, "read again");

    }

    layer_mark_dirty((Layer *)voice_chats);
}

If anyone has any suggestions for why the pebble isn't displaying the data it is being assigned in .title, I would love to hear them.

Thanks!

回答1:

The solution is actually much simpler than that.

First, please note that a char array and a pointer to a string are in fact the same things. They are both pointers to the address of the first byte. In both case, the system functions will search for the null character to identify the end of the string (strlen, strcpy, printf, etc). char* and char[] are interchangeable.

When you receive the data in your in_received_handler(), the pointer you get (dataReceived->value->cstring) points to the bluetooth receive buffer and you need to copy that string somewhere else. This way, every time the screen needs to be redrawn, the characters will be available.

Because you are getting a dynamic number of items, you have to dynamically allocate the memory with malloc(). You should remember to deallocate that memory later (with free()).

This compiles and should do what you want:

void in_received_handler(DictionaryIterator *received, void *context) {
  Tuple *dataReceived = dict_read_first(received);

  while (dataReceived != NULL){
    if (dataReceived->key == 0x20) {
      //original is of the format "# random string", i.e. "4 test name"
      char* original = dataReceived->value->cstring;
      APP_LOG(APP_LOG_LEVEL_DEBUG, original);

      char* keysplit = strtok(original, " ");
      APP_LOG(APP_LOG_LEVEL_DEBUG, keysplit);

      //key
      int key = atoi(keysplit);
      APP_LOG(APP_LOG_LEVEL_DEBUG, "Int Key: %d", key);

      // This will return the second part of the string.
      char *message = strtok(NULL, " ");
      // Allocate some memory to hold a copy of that message
      char *copyOfMessage = malloc(strlen(message));
      // And copy the message from the bluetooth buffer to the new memory
      strcpy(copyOfMessage, message);
      APP_LOG(APP_LOG_LEVEL_DEBUG, "Message: %d", key);

      chats[key] = (SimpleMenuItem){
        // You should give each menu item a title and callback
        .title = copyOfMessage,
        .callback = selected_chat,
      };
    }

    dataReceived = dict_read_next(received);
  }
  layer_mark_dirty((Layer *)voice_chats);
}

By the way, instead of using one string with both the key and message and parsing it in C in the client, I would recommend using another app message key index to transfer your 'key'. For example, you could have:

{
  0x1: 33,                 // the key
  0x20: "message"           // the actual message
}


回答2:

(I don't know what Pebble is, so this answer is based purely on the C language.)

I think your main problem is here:

char* ptr = remainderArray;
strncpy(ptr, remainderArray, sizeof(remainderArray)+1);

The source and destination pointers are the same. What are you trying to do here?

The C standard (or the final public draft of it) says:

7.24.2.4 The strncpy function

2. The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1308). If copying takes place between objects that overlap, the behavior is undefined.

If you need to copy overlapping object, you have to use memmove(), or copy the characters yourself in a loop.

I also think there are some off-by-one errors in your code:

char originalArray[strlen(original)];

This does not leave room for the terminating null character.

ptr[sizeof(remainderArray)+1] = '\0';

This writes one byte beyond the end of remainderArray.