2013-11-01 2 views
0

Например, у меня есть этот блок:Как изменить переменную, имя которой является пользователем?

int nFirst, nSecond; 
char sInput[10]; 
printf("Which variable to change to 10?"); 
scanf("%s", &sInput); 
// BAD - inflexible and unmaintainable 
if(strcmp(sInput,"nFirst") ==0){ 
    nFirst = 10; 
} 
else if (strcmp(sInput,"nSecond")==0) { 
    nSecond =10; 
} 

Есть хороший способ сделать это? как обрабатывать строку, как будто это имя переменной?

+1

Whoa, зачем вы хотите это сделать? –

+1

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

+1

В качестве незначительной заметки ваше использование 'strcmp()' неверно; он не возвращает 'bool', он возвращает' int' с '0', что означает, что строки равны. – unwind

ответ

0

Вы можете реализовать что-то вроде словаря или двумерного массива, который содержит «имя переменной» и значение. Затем это сводится к установке элемента массива в новое значение.

Кроме этого: C# и другие объектно-ориентированные языки позволили бы это посредством отражения, но поскольку сам C не является объектно-ориентированным, вы не можете этого сделать (поддержка C++ для этого кажется очень ограниченной).

+0

не могли бы вы показать мне, как это сделать на C++? –

+0

Увы, похоже, что C++ не может легко это сделать (читайте здесь: http://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application). Я уточню свой ответ. –

0

Другой подход, основанный на платформе, состоит в том, чтобы поместить все ваши переменные в общую библиотеку, а затем получить доступ к ним по именам. Посмотрите на функции dlsym/dlopen.

void* handle = dlopen("mysymbols.so", RTLD_LOCAL | RTLD_LAZY); 
int* var = (int*) dlsym(handle, user_typed_name); 
*var = 10; /* modify the variable */ 
0

Вы можете сделать это с помощью макроса:

#define MAYBESET(name) if (strcmp(sInput, #name) ==0){ name = 10; } 

#name реальное значение name изменено на строковый литерал.

+0

Это по-прежнему негибко, так как требуется, чтобы макрос вызывался для каждой допустимой переменной во время сборки. Это уменьшает видимость кода, но не решает, что требовал плакат. Кроме того, он устраняет потенциально огромное преимущество предложения 'else', используемого плакатом, увеличивая среднее время выполнения. – mah

1

Нет, не существует «приятного» способа сделать это в C. Имена переменных (обычно) не сохраняются в сгенерированном машинном коде, за исключением поддержки отладки. C не имеет встроенного механизма для преобразования строкового значения в ссылку на переменную с тем же именем.

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

struct vn { 
    char *varname; 
    void *addr; 
    Typeinfo t; 
}; 

где Typeinfo является некоторым перечислением или другого механизмом для кодирования типа переменных, что дает вам что-то к эффекту

int foo; 
double bar; 
char *blurga; 
struct vn varsByName[] = { {"foo", &foo, IntType}, 
          {"bar", &bar, DoubleType}, 
          {"blurga", blurga, CharPtrType} }; 

Я не рекомендую это делать.

0

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

int First, Second; // Note that I got rid of your leading n 
char sInput[10]; 
printf("Which variable to change to 10?"); 
scanf("%s", &sInput); 
// BAD - inflexible and unmaintainable 
// referring to character array overflow potential, I assume 

switch (sInput[0]) 
{ 
    case 'F': 
     if (0 == strcmp("irst", sInput+1)) 
     { 
       First = 10; 
     } else 
     { 
       // error 
     } 
     break; 
    case 'S': 
     if (0 == strcmp("econd", sInput+1)) 
     { 
       Second = 10; 
     } else 
     { 
       // error 
     } 
     break; 
    default: 
     // error 
     break; 
} 

Если вам не нравится как это выглядит, тогда вы можете использовать макросы (#define), чтобы сделать его менее привлекательным, но все получится. Другим вариантом, который вы могли бы использовать, было бы написать небольшую программу, которая выводит исходный код этой программы, который будет обрабатывать все повторяющиеся и утомительные части.

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

Смежные вопросы