разделение массива char * с использованием strtok дает ошибку времени выполнения

Мой код довольно прост (просто разбиение массива), но поскольку у меня нет компилятора ac, я компилирую его онлайн на http://ideone.com. Тем не менее, он дает ошибку во время выполнения, несмотря ни на что, и я также пробовал выполнять синтаксический анализ с помощью sscanf, но тщетно. Пожалуйста помоги.

#include<stdio.h>
#include<string.h>
typedef struct{
   char *header;
   char *body;
}AT_msg_Data;
static char *__rx_data = "AT+CGMI\r";
int main(){
    AT_msg_Data *data;
    printf("In main");
    data->header = strtok(__rx_data,"+");
    data->body = strtok(NULL,"+");
    //also tried sscanf_s(data->body,"[^=]%s\r",at_comm,sizeof(at_comm),at_param,sizeof(at_param));
    printf("%s %s", data->header, data->body);
}

person user3243678    schedule 29.09.2015    source источник
comment
Попробуйте static char *__rx_data = "AT+CGMI\r"; - ›static char __rx_data[] = "AT+CGMI\r";   -  person Spikatrix    schedule 29.09.2015
comment
статический символ * __ rx_data = AT + CGMI \ r; // это данные только для чтения попробуйте подход @CoolGuy   -  person Potato    schedule 29.09.2015
comment
возможный дубликат Strtok segfault   -  person AndersK    schedule 29.09.2015
comment
так много вопросов strdup, так мало интереса к поиску, был ли вопрос ранее опубликован   -  person AndersK    schedule 29.09.2015


Ответы (2)


Вы не можете вызвать strtok() для строкового литерала, потому что строковые литералы являются постоянными. Обычно безопаснее объявлять указатели на строковые литералы с квалификатором const, чтобы компилятор предупредил вас, когда программа пытается изменить указатель.

Вам нужно сделать вашу строку массивом вместо указателя на строковый литерал, например

static char __rx_data[] = "AT+CGMI\r";

это массив из 9 байтов, содержащий строку из 8 символов, которые вы можете изменять, strtok() действительно изменяет его аргументы.

Вам также необходимо выделить место для вашего struct, чтобы его можно было использовать, вы можете сделать это двумя способами, используя malloc()

AT_msg_Data *data;
data = malloc(sizeof(*data));
if (data == NULL)
    return -1; /* failed to allocate memory -- exit the program */

или просто создайте экземпляр в стеке

AT_msg_Data data;
/*         ^ no star here */

а затем получить доступ к его членам с . вместо ->.

Другой способ, который неприменим в этом случае, поскольку ваша строка не может быть выделена динамически, - это использовать malloc() или strdup().

Вы также можете использовать strchr() вот так

const char *plus;
plus = strchr(__rx_data, '+');
if (plus != NULL)
{
    size_t length; /* maybe use `ptrdiff_t' here but just for illustration */
    length = plus - __rx_data;
    data->header = malloc(length + 1); 
    if (data->header != NULL)
    {
        memcpy(data->header, __rx_data, length);
        data->header[length] = '\0';
        data->body = strdup(plus + 1);
    }
    /* note: data->header can be `NULL' from here, so check that before using it */
    /*       this also applies to data->body */
}

Таким образом, вам не нужно будет изменять __rx_data, что лучше, потому что это глобальная переменная, и я предполагаю, что вы собираетесь использовать ее повторно. Вы должны сделать свой код более безопасным, повторно объявив

static const char *const __rx_data = "AT+CGMI\r";

если вы собираетесь использовать подход strchr().

С этим объявлением __rx_data нельзя переназначить или изменить без жалоб компилятора.

Ваш код в любом случае потерпит неудачу позже, потому что '\0', помещенный strtok() в позицию знака +, будет перезаписан +, когда вы снова вызовете strtok(), strtok() сохраняет внутренний буфер для восстановления замененных символов, что также делает его небезопасным, если ваша программа многопоточный.

person Iharob Al Asimi    schedule 29.09.2015
comment
Замечу, что его строка AT_msg_Data *data; не выделяет память для использования его структурой. Не могли бы вы ответить на этот вопрос, пожалуйста, для правильности. - person zeyorama; 29.09.2015
comment
@zeyorama Я этого даже не заметил, когда я увидел static char *__rx_data = "AT+CGMI\r", я сразу начал писать ответ, так как strtok() + строковый литерал немедленно что-то вызвало у меня в голове. - person Iharob Al Asimi; 29.09.2015
comment
@iharob Извините, я был немного груб (этот комментарий звучал так, как будто вы проголосовали только потому, что я спорил. Я не спорил с этим. То, что вы сказали, было вашим мнением, а у меня было только это). Еще раз извините !! :) - person ameyCU; 29.09.2015
comment
@iharob Я восстановил удаление. Приносим извинения за такое поведение. - person ameyCU; 29.09.2015

Вы можете использовать sscanf, но сначала вам нужно выделить память для data, header и body.

data = malloc(sizeof(AT_msg_Data));
if(data==NULL){
      // handle error
 }
data->header = malloc(10);
if(data->header==NULL){
      // handle error
 }

data->body = malloc(20);
if(data->body==NULL){
      // handle error
 }

А затем используйте sscanf вот так -

 if(sscanf(__rx_data,"%[^+]+%[^\r]\r",data->header,data->body)==2){     // if sscanf is successful 
   //do something
  }

Затем после этого data->header будет содержать AT, а data->body будет иметь CGMI из строки.

person ameyCU    schedule 29.09.2015
comment
Это как таковое, вероятно, вызовет неопределенное поведение. Это разумное решение, но имейте в виду, что вы даете это решение новичку, существует огромная вероятность неправильного использования sscanf() игнорирования возвращаемого значения malloc() и, таким образом, публикации другого вопроса моя программа дает сбой или мой сосед ест свою собаку. - person Iharob Al Asimi; 29.09.2015
comment
Хорошо, если sscanf() data->header или data->body или оба могут быть прочитаны без инициализации, а также malloc() может вернуть NULL. И, кроме того, никогда не malloc() для постоянного значения, за исключением случаев крайней необходимости. Вместо malloc(10) или ... Вы можете просто переопределить их в определении struct, например, char header[10]. И всегда malloc() точное количество, нет бесконечной памяти. - person Iharob Al Asimi; 29.09.2015
comment
@iharob Он использует указатели. В какой-то момент ему придется выделить память. Я согласен с malloc(10) частью. Но что делать, если это нужно делать только с помощью указателей? - person ameyCU; 29.09.2015
comment
@iharob Я только что опубликовал это, поскольку он сказал, что тоже пробовал sscanf, так что если он будет знать об этом. - person ameyCU; 29.09.2015