Как я могу перейти к значению строки в C?

Я программирую робота на C и столкнулся с проблемой, которую не могу понять.

Единственный способ решить эту проблему — использовать множество операторов goto. Я пытаюсь найти способ избежать написания более 100 goto точек и операторов, операторов if и т. д., и мне интересно, есть ли способ перейти к значению строки. Например-

string Next = "beginning";
goto Next;
beginning:

Есть ли способ goto значение Next или подставить значение Next в оператор goto?

Если есть способ сделать это, то я смогу просто изменить значение Next для каждой команды движения, а затем goto независимо от значения строки Next.

Другими словами, просто преобразовать строку в идентификатор goto или подставить его вместо одного.

Спасибо за помощь!

-РЕДАКТИРОВАТЬ-

Многие из вас, ребята, предлагают использовать операторы switch. Я не уверен, что это сработает из-за того, как я это запрограммировал. Структура программы здесь - кстати, этот код включает в себя лишь немногое из того, что у меня есть на самом деле, мой реальный код на данный момент составляет более 500 строк. Кроме того, команды вождения значительно упрощены. но основная концепция здесь, ее легче понять, чем то, что было бы у меня.

task main()
{
  //integer list
  int forwardDrivingSelector = 0;
  int backwardDrivingSelector = 0;
  int rightRotatingSelector = 0;
  string nextCommand;
  int waitTime = 0;
  int countup = 0;

  //driving commands

  driveForward:
  while(forwardDrivingSelector == 1)
  {
    motor[leftMotor] = 127;
    motor[rightMotor] = 127;
    countup++;
    wait1Msec(1);
    if(countup == waitTime)
    {
      countup = 0;
      goto nextCommand;
    }
  }
  driveBackward:
  while(backwardDrivingSelector == 1)
  {
    motor[leftMotor] = -127;
    motor[rightMotor] = 127;
    countup++;
    wait1Msec(1);
    if(countup == waitTime)
    {
      countup = 0;
      goto nextCommand;
    }
  }
  rightRotate:
  while(rightRotatingSelector == 1)
  {
    motor[leftMotor] = 127;
    motor[rightMotor] = -127;
    countup++;
    wait1Msec(1);
    if(countup == waitTime)
    {
      countup = 0;
      goto nextCommand;
    }
  }

  //autonomous driving code

  //first command, drive forward for 1000 milliseconds
  forwardDrivingSelector = 1;
  nextCommand = "secondCommand";
  waitTime = 1000;
  goto driveForward;

  secondCommand:
  forwardDrivingSelector = 0;

  //second command, rotate right for 600 milliseconds
  rightRotatingSelector = 1;
  nextCommand = "thirdCommand";
  waitTime = 600;
  goto rightRotate;

  thirdCommand:
  rightRotatingSelector = 0;

  //third command, drive backwards for 750 milliseconds
  backwardDrivingSelector = 1;
  nextCommand = "end";
  waitTime = 750;
  goto driveBackward;

  end:
  backwardDrivingSelector = 0;

}

так. как это работает. у меня есть список целых чисел, включая селекторы управляющих команд, целые числа countup и waitTime, а также строку, о которой я говорил, nextCommand. затем идут команды вождения. в моем реальном коде у меня есть около 30 команд, и все они подключены к пульту дистанционного управления, и это более 400 строк только для команд управления. затем идет автономный код. причина, по которой я настроил это так, заключается в том, что автономная часть кода будет короткой, простой и по существу. в значительной степени, чтобы добавить команду в управляющий код, вы включаете селектор, сообщаете строке nextCommand, какая следующая команда, устанавливаете время ожидания (которое указывает, как долго выполняется команда в миллисекундах), затем вы заставляете код перейти к команда движения, которую вы вводите.

Теоретически все это работало бы, если бы был способ заставить оператор goto «интерпретировать» строку как идентификатор, чтобы ее можно было изменить.

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

Теперь, когда вы лучше поняли мой вопрос, что-нибудь еще? :)

Кстати, я использую программу под названием robotC, и я программирую робота vex. поэтому я ДОЛЖЕН использовать простой, базовый, C, и я не могу использовать какие-либо надстройки или что-то еще... это еще одна причина, по которой это сложно, потому что я не могу иметь несколько классов и тому подобное...


person Austin Payne    schedule 09.01.2012    source источник
comment
Должен быть лучший способ делать то, что вы делаете, чем 100 gotos...   -  person Mysticial    schedule 09.01.2012
comment
Если бы вы могли описать корневую проблему, может кто-нибудь найдет способ лучше, чем эти goto   -  person Kevin    schedule 09.01.2012
comment
Фактический код, который мне нужно запрограммировать, представляет собой 60-секундный код автономного вождения, который действительно сложен и будет содержать более 100 команд из-за конкуренции. вторые 60 секунд соревнования контролируются пользователем, и этот код очень легко запрограммировать. это автономный код, который длинный   -  person Austin Payne    schedule 11.01.2012


Ответы (6)


В качестве расширения языка C GCC предоставляет функцию под названием вычисляемые переходы, которые позволяют goto вычислять метку во время выполнения. Однако я настоятельно рекомендую вам пересмотреть свой дизайн.

Вместо того, чтобы использовать gotos с более чем сотней меток (что легко приведет к неподдерживаемому спагетти-коду), рассмотрите вместо этого использование указателей на функции. Код станет гораздо более структурированным и удобным в сопровождении.

person Adam Rosenfield    schedule 09.01.2012

Вместо goto я бы вызвал одну из 100 функций. Хотя C не справится с преобразованием строки в функцию за вас, довольно просто использовать отсортированный массив структур:

struct fn {
    char name[whatever];
    void (*func)(void);
};

Затем выполните (например) двоичный поиск по массиву, чтобы найти функцию, которая соответствует строке.

Также обратите внимание, что многие реальные системы предоставляют такие вещи, как GetProcAddress (Windows) или dlsym (Unix/Linux), чтобы выполнить часть работы за вас.

person Jerry Coffin    schedule 09.01.2012

Вы думаете об этом неправильно. Каждое из действий, которые вам нужно вызвать, должно быть функцией, затем вы можете выбрать, какая функция должна быть вызвана следующей, проверив «следующую» переменную.

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

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

Небольшой совет: если вы когда-нибудь думали, что вам нужно более одного оператора goto для достижения определенной цели, вы, вероятно, ищете не лучшее решение.

person Matt Lacey    schedule 09.01.2012
comment
Я не думаю, что предложенный вами вариант является излишним. Если у него есть 100 функций, но нет возможности унифицировать их вызовы, тогда он может просто иметь 100 блоков if. - person dreamlax; 09.01.2012

Вам нужно сделать шаг назад и рассмотреть другие решения проблемы, которую вы пытаетесь решить. Один из них может выглядеть так:

void DoSomething() {
    printf("Something\n");
}

void DoSomethingElse() {
    printf("Something else\n");
}

void (*nextStep)(void) = NULL;

nextStep = DoSomething;
nextStep();
nextStep = DoSomethingElse;
nextStep();

Посмотрите в действии.

person Jon    schedule 09.01.2012

Как насчет switch? Либо используйте int/enum/whatever, либо проверьте значение строки (например, переберите ее и strcmp), чтобы выяснить место назначения.

const char *dsts[n_dsts] = {"beginning","middle",...};
...
int i;
for(i = 0; i < n_dsts; i++) if(strcmp(dsts[i]) == 0) break;
switch(i) {
    case 0: // whatever
    case 1: // whatever
    ...
        break;
    default: // Error, dest not found
}
person Kevin    schedule 09.01.2012

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

Это сказало. . . вы можете более или менее сделать это, используя оператор switch. Например:

Next = BEGINNING;
HelperLabel:
switch(Next)
{
   case BEGINNING:
     .
     .
     .
     Next = CONTINUING;
     goto HelperLabel;
   case ENDING:
     .
     .
     .
     break;
   case CONTINUING:
     .
     .
     .
     Next = ENDING;
     goto HelperLabel;
}

(Обратите внимание, что для оператора switch требуются целые числа или целочисленные значения, а не строки, но вы можете использовать enum для прямого создания этих целых чисел.)

Оригинал смотрите на http://en.wikipedia.org/wiki/Duff%27s_device. , канонический пример использования switch/case в качестве goto.

person ruakh    schedule 09.01.2012
comment
Вместо goto HelperLabel, почему бы просто не вставить оператор switch в цикл и вместо этого использовать break? - person dreamlax; 09.01.2012
comment
@dreamlax: Хорошая идея; Выполнено. Я думаю, я думал, что различные случаи нужно будет чередовать с другими структурами управления (которые goto разрешают, а break нет) - иначе зачем OP вообще хотел goto? - но поскольку это не было указано явно, да, вероятно, лучше начать с более чистого подхода. - person ruakh; 09.01.2012
comment
Именно, он переплетается с другими управляющими структурами, поэтому я использую goto - person Austin Payne; 11.01.2012
comment
@user1137900: ОК. затем я восстановил версию своего ответа, которая позволяла чередовать другие структуры управления. - person ruakh; 11.01.2012
comment
руах, ваша кодировка была бы идеальной, ЕСЛИ бы мой водительский код был в одном разделе. Он разделен на 2 раздела, в первом разделе есть все команды движения (вперед, влево и т. д.), а во втором разделе несколько раз ссылаются на первый раздел для разных команд (сначала вперед, затем налево, затем и т. д.) - person Austin Payne; 11.01.2012
comment
@ user1137900: Под двумя разделами вы подразумеваете две полностью отдельные функции или просто одну функцию с двумя логическими частями/разделами/абзацами внутри? В первом случае вы также не можете использовать goto; и в последнем случае вы можете использовать предоставленный мной код, поместив практически все содержимое функции в блок switch. (Я могу отредактировать свой ответ, если хотите, чтобы сделать его более явным.) - person ruakh; 11.01.2012