Может ли strcpy редактировать адреса памяти на Arduino?

Вы также можете использовать HTML5 replaceState, если хотите изменить URL-адрес, но не хотите добавлять запись в историю браузера:

if (window.history.replaceState) {
   //prevents browser from storing history with each change:
   window.history.replaceState(statedata, title, url);
}

Это «сломало» функциональность кнопки «Назад». Это может потребоваться в некоторых случаях, например, в галерее изображений (где вы хотите, чтобы обратная кнопка возвращалась обратно на индексную страницу галереи, вместо того чтобы перемещаться по каждому просматриваемому вами изображению), предоставляя каждому изображению свой собственный уникальный URL.

0
задан gre_gor 18 January 2019 в 16:12
поделиться

2 ответа

Я сделал тест и точно выяснил, что здесь происходит.

Это проблема со strtok, а также тот факт, что вы предоставляете символьные массивы всем этим строковым функциям без пробела для разделителя строк, то есть «0».

Это тестовая функция, которую я написал на компьютере для тестирования.

void printAllObjects(int location,char *x, char* strtokIndx, char *gva_testDate,char *gva_testTime)
{
    printf("At location %d\n x pointer %d, string %s\n"
            " strtokIndx pointer %d, string %s\n "
            "gva_testDate pointer %d, string %s\n "
            "gva_testTime pointer %d, string %s\n ",location,x,x,strtokIndx,strtokIndx,gva_testDate,gva_testDate,gva_testTime,gva_testTime);
}

Затем я скопировал весь ваш код arduino на мой компьютер, закомментировал все последовательные команды, добавил заголовок stdio, и использовал вышеупомянутую функцию в нескольких местах.

#include <cstdlib>
#include <String.h>
#include <stdio.h>

using namespace std;

/*
 * 
 */

char gva_logfile[24] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
char gva_testDate[6] = {'Y', 'Y', 'm', 'm', 'D', 'D'}; // is appending?
char gva_testTime[6] = {'H', 'H', 'M', 'M', 'S', 'S'}; 

char lva_testDate[6];

//String y = "BATCH|190117;151442$";
//int lan = y.length(); // should be 20

char x[] = "BATCH|190117;151442$";
int lan = strlen(x);
//y.toCharArray(x, lan);
//strcpy(x, y);
//y.toCharArray(x, 20);

//strcpy(x, "BATCH|190117;151442$");


void printAllObjects(int location,char *x, char* strtokIndx, char *gva_testDate,char *gva_testTime)
{
    printf("At location %d\n x pointer %d, string %s\n"
            " strtokIndx pointer %d, string %s\n "
            "gva_testDate pointer %d, string %s\n "
            "gva_testTime pointer %d, string %s\n ",location,x,x,strtokIndx,strtokIndx,gva_testDate,gva_testDate,gva_testTime,gva_testTime);
}

void setup() {
  // put your setup code here, to run once:
//  Serial.begin(9600);
//
//  Serial.print("<");
//  for(int i=0; i<6; i++){
//    Serial.print(gva_testDate[i]); // works
//    //Serial.print(gva_testTime[i]); // works
//  }
//  Serial.println(">"); 
}



void loop() {
  // put your main code here, to run repeatedly:
  char tele[6] = {' ', ' ', ' ', ' ', ' ', ' '};
  while(1){
    char flarb[lan];
    strcpy(flarb, x);
    //Serial.println(flarb);

    if(strstr(flarb, "BATCH|")){
      char * strtokIndx;
      printAllObjects(1,x,strtokIndx,gva_testDate,gva_testTime);
      strtokIndx = strtok(x, "|");
      printAllObjects(2,x,strtokIndx,gva_testDate,gva_testTime);
      //strcpy(tele, strtokIndx);     // did nothing?
      strtokIndx = strtok(NULL, ";");
      printAllObjects(3,x,strtokIndx,gva_testDate,gva_testTime);
      strcpy(gva_testDate, strtokIndx); // missing?
      printAllObjects(4,x,strtokIndx,gva_testDate,gva_testTime);

//      Serial.println(gva_testDate); // Not missing
      for(int i=0; i<6; i++){
        lva_testDate[i] = gva_testDate[i];  
      }      

      strtokIndx = strtok(NULL, "$");
      printAllObjects(5,x,strtokIndx,gva_testDate,gva_testTime);
      strcpy(gva_testTime, strtokIndx); // is fine...
      printAllObjects(6,x,strtokIndx,gva_testDate,gva_testTime);

//      Serial.println(gva_testDate); // MISSING
//      Serial.println(lva_testDate);

      if(strstr(gva_testDate, "YYmmDD")!=NULL || strstr(gva_testTime, "HHMMSS")!=NULL){
          //if((gva_testDate == "YYmmDD") || (gva_testTime == "HHMMSS")){  
          char io[28]; // 16 + 2*6
          sprintf(io, "063 ERROR: %s,%s", gva_testDate, gva_testTime);
          //logArdData(io);
//          Serial.print("<");
//          Serial.print(io);
//          Serial.println(">");
//          Serial.flush();
        }
        //else if((strstr(gva_testDate, "YYmmDD") && strstr(gva_testTime, "HHMMSS"))==NULL){
        else if((strstr(gva_testDate, "YYmmDD")==NULL && strstr(gva_testTime, "HHMMSS")==NULL)){  
          //else if((gva_testDate != "YYmmDD") && (gva_testTime != "HHMMSS")){  
          char io[26]; // 14 + 2*6

          //sendArdData(gva_testDate); // is combinined?
          //sendArdData(gva_testTime); // still itself

          sprintf(io, "Assigned %s,%s", gva_testDate, gva_testTime); // is combining testDate and testTime, then printing testTime?
//          Serial.print("<");
//          Serial.print(io);
//          Serial.println(">");
          //sendArdData(io);
          //logArdData(io);
//          Serial.flush();        
        }
    }
  }
}
int main(int argc, char** argv) {
    setup();
    printf("Entering loop\n");
    loop();
    return 0;
}

Это вывод, который я получил

Entering loop
At location 1
 x pointer 199946368, string BATCH|190117;151442$
 strtokIndx pointer 0, string (null)
 gva_testDate pointer 199946344, string YYmmDDHHMMSS
 gva_testTime pointer 199946350, string HHMMSS
 At location 2
 x pointer 199946368, string BATCH
 strtokIndx pointer 199946368, string BATCH
 gva_testDate pointer 199946344, string YYmmDDHHMMSS
 gva_testTime pointer 199946350, string HHMMSS
 At location 3
 x pointer 199946368, string BATCH
 strtokIndx pointer 199946374, string 190117
 gva_testDate pointer 199946344, string YYmmDDHHMMSS
 gva_testTime pointer 199946350, string HHMMSS
 At location 4
 x pointer 199946368, string BATCH
 strtokIndx pointer 199946374, string 190117
 gva_testDate pointer 199946344, string 190117
 gva_testTime pointer 199946350, string 
 At location 5
 x pointer 199946368, string BATCH
 strtokIndx pointer 199946381, string 151442
 gva_testDate pointer 199946344, string 190117
 gva_testTime pointer 199946350, string 
 At location 6
 x pointer 199946368, string BATCH
 strtokIndx pointer 199946381, string 151442
 gva_testDate pointer 199946344, string 190117151442
 gva_testTime pointer 199946350, string 151442

На основании этих данных это мой вывод.

Функция strtok изменяет исходную строку и заменяет поисковый символ на ноль, затем копирует строку в новый индекс и возвращает индекс.

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

Как видно из результатов, gva_testDate и gva_testTime имеют разность указателей, равную 6, поэтому, когда gva_testTime заполняется, если вы используете gva_testDate в качестве строки, ноль отображается только в конце 12 символов. [1113 ]

Также предполагается, что lva_testDate является буфером для вас, вам нужно дать ему значение для начала, или он станет нулевым буфером и может закончить тем, что изменил разные вещи в зависимости от того, к чему скомпилирован код.

Чтобы решить эту проблему, я инициализировал все ваши переменные, поэтому вывод выводится ниже.

char gva_testDate[] = "YYmmDD"; // is appending?
char gva_testTime[] = "HHMMSS";

char lva_testDate[20];

//String y = "BATCH|190117;151442$";
//int lan = y.length(); // should be 20

char x[] = "BATCH|190117;151442$";
int lan = strlen(x);

вывод

Entering loop
At location 1
 x pointer 107077760, string BATCH|190117;151442$
 strtokIndx pointer 0, string (null)
 gva_testDate pointer 107077736, string YYmmDD
 gva_testTime pointer 107077743, string HHMMSS
 At location 2
 x pointer 107077760, string BATCH
 strtokIndx pointer 107077760, string BATCH
 gva_testDate pointer 107077736, string YYmmDD
 gva_testTime pointer 107077743, string HHMMSS
 At location 3
 x pointer 107077760, string BATCH
 strtokIndx pointer 107077766, string 190117
 gva_testDate pointer 107077736, string YYmmDD
 gva_testTime pointer 107077743, string HHMMSS
 At location 4
 x pointer 107077760, string BATCH
 strtokIndx pointer 107077766, string 190117
 gva_testDate pointer 107077736, string 190117
 gva_testTime pointer 107077743, string HHMMSS
 At location 5
 x pointer 107077760, string BATCH
 strtokIndx pointer 107077773, string 151442
 gva_testDate pointer 107077736, string 190117
 gva_testTime pointer 107077743, string HHMMSS
 At location 6
 x pointer 107077760, string BATCH
 strtokIndx pointer 107077773, string 151442
 gva_testDate pointer 107077736, string 190117
 gva_testTime pointer 107077743, string 151442

Надеюсь, это поможет.

0
ответ дан Nitro 18 January 2019 в 16:12
поделиться

Как Мелпомена и Ален предложили в комментариях, ваши проблемы связаны с отсутствием NULL-терминаторов в ваших строках. Из вашего комментария, я полагаю, вы можете использовать класс C ++ String, который хранит свойства, которые сообщают вам размер строки сразу. Однако в Си строки - это просто массивы символов в памяти, и не существует непосредственного способа определить их длину, кроме подсчета символов, пока мы не достигнем значения NULL (0x00).

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

Я вижу в вашем коде, что некоторые из ваших строк предназначены для фиксированного размера, и нулевой символ может быть избыточным. В этом случае, когда вы знаете размер строки (либо потому, что вы жестко ее кодируете, либо сохраняете ее в некоторой переменной), вам нужна функция memcpy. Он просто скопирует установленное количество байтов с одного адреса на другой, не полагаясь на нулевые терминаторы. НО Serial.print будет по-прежнему ожидать, что нулевые терминаторы будут печатать его, поэтому вам также придется это изменить.

Самое простое решение - просто оставить больше места в строках. Например, если вы хотите создать строку «abc» , вам следует использовать char myString[4] = "abc". Компилятор автоматически заполняет память, начиная с myString, 0x61 0x62 0x63 0x00, поскольку компилятор знает, что в C строки (отмеченные ") должны заканчиваться нулем, вы должны предоставить только один дополнительный байт для нулевого значения. терминатор. Если ваша строка будет иметь переменную длину, вы должны предоставить достаточно памяти для сценария наихудшего случая плюс нулевой терминатор, чтобы ваша переменная x могла извлечь выгоду из более чем 21 байта. Вы не можете изменить этот размер позже, но то, что определяет длину вашей строки, это нулевой терминатор, а не объем памяти, который он зарезервировал.

Строки этой формы, однако:

char gva_testTime[6] = {'H', 'H', 'M', 'M', 'S', 'S'};

не будет не включать нулевой символ, потому что вместо строки вы объявили массив символов. Если вы хотите использовать strcpy или strtok в этой переменной, вам нужно будет включить нулевой терминатор самостоятельно:

char gva_testTime[7] = {'H', 'H', 'M', 'M', 'S', 'S', 0};

Или объявить его в виде строки:

char gva_testTime[7] = "HHMMSS";

Итак, в основном, просмотрите все ваши объявления строк, чтобы включить в них один дополнительный байт для нулевых терминаторов, и убедитесь, что способ, которым вы объявляете, будет включать нулевой терминатор.

0
ответ дан Arthur Moraes Do Lago 18 January 2019 в 16:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: