C/C++ string in switch/case
0x0300 : Study/0x0304 : Programming요새 CTF를 하면서 C든 뭐든 프로그래밍 너무 못한다 생각이 너무나도 들어서 다시 공부하고 있다.
그때마다 생각나는거 생각을 써야겠다.
1 2 3 4 5 6 7 8 9 10 | switch(string) { case "Name": ... break; case "Age": ... break; } | cs |
위 구문은 switch 문을 그냥 의식의 흐름(?) 대로 string을 해봤는데...
switch의 controlling expression을 정수 형 대신 문자열를 사용 할 수 없기에 위 구문은 당연히 될 수가 없다.
switch 명령은 case가 많을수록 이론상으로 if문과 성능 차이가 기하급수로 벌어지게 되어 있다. 왜 그럴까?
if - else if 열거식의 경우엔 비교하는 대상 중 어느 한쪽이 상수라면 자동으로 점핑 테이블을 만들어 마치 switch - case처럼 동작하기 때문이다. 좀 더 자세히 생각해 보면, 상수 값에 따라 IP(Instruction Pointer) 는 함수호출처럼 자신의 주소를 향해 뛰어간다. break 구문을 만나면 switch 가 끝나는 블럭으로 또 한 번 점프한다. break 나 continue 는 goto 구문의 다른 (특수한 용도로 한정된) 이름이다. 그런데 상수가 아닐 경우는 점핑 테이블 생성이 불가능해진다. 결과적으로 속도에서 차이가 날 수 밖에 없다는 것이다. jmp table을 쓰는 것은 시간이 오래 걸리는 테이블 생성 작업을 1회 한 후 한 번의 비교로 원하는 곳으로 찾아가 작업하겠다는 성능을 기대하는 것인데 변수가 case에 들어가게 되면 일반적인 if - else if 로밖에 구현이 안되는 것이다.
EIP = EIP + jmptable[Index];
// 이게 맞는건지는 나도 잘 모르겠따리...
많은 case를 string을 넣어 사용한다면 결론적으로 많은 jmp table 요구한다. 게다가 변수가 상수가 아닐 경우에는 Index 값 자체가 산출되지 않아 저런 식으로는 불가능하고 연결리스트 따위를 동원해야하니 속도 성능 같은 건 기대도 못 할지도 모른다.
1 2 3 4 5 6 7 | if(strcmp(string, "Name") == 0) { } else if(strcmp(string, "Age") == 0) { } else ... | cs |
if를 써서 strcmp 를 사용하여 위의 switch 구문을 표현 할 수 있다. 사실 그냥 내가 case 써서 string으로 구현하고 싶은 맘이다.
사실2 더 큰 문제는 else if 중첩에는 한계가 있다는 것이다. (error code C106 참고)
C++ 로 먼저 좀 생각해보자. jmptable 을 요구하여 한다면, STL 를 사용하여 테이블을 생성하여 점프를 한다면 충분히 가능하지 않을까 생각이 든다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | static map< string, int > m; void Init(){ m["Name"] = ENUM_NAME; m["Age"] = ENUM_AGE; } void func(){ switch(m[string]){ case ENUM_NAME: ... break; case ENUM_AGE: ... break; } } | cs |
map를 사용하여 switch 구문에 대한 문자열 문제는 해결된다. 어느정도 많은 case 문이 나와도 깔끔하게 정리가 될 수 있고, map가 어느정도 성능을 개선 해 주기 때문에 좋다고 생각한다. 하지만 정말로 괜찮을까?
초기화 함수 하나 만들고 변수 하나 선언하고 열거형을 만들면 된다. 하지만 이런 작업을 하고 있으면 굉장한 손해를 본다. 성능상으로야 가장 좋지만(map 대신 hash_map을 쓴다면, 비록 표준은 아니지만 성능으로는 좋은 성과를 볼 수 있다.) 코드의 유지보수 관리에는 그켬을 초래할 수 있다.
머 중요한 사실은 cpp을 하는 것은 아니고 내가...이러한 문제는 따로 STL map에 따로 case token 을 선언해준다면 충분히 해결 될 것이라고 생각한다.
어디까지나 cpp 조빱이 생각한거라... 나도 올바른 정답은 아니다. 또 원래 C를 공부 하고 있어서 C에 대해서도 찾아봤다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #define BADKEY -1 #define A1 1 #define A2 2 #define B1 3 #define B2 4 typedef struct { char *key; int val; } t_symstruct; static t_symstruct lookuptable[] = { { "A1", A1 }, { "A2", A2 }, { "B1", B1 }, { "B2", B2 } }; #define NKEYS (sizeof(lookuptable)/sizeof(t_symstruct)) int keyfromstring(char *key) { int i; for (i=0; i < NKEYS; i++) { t_symstruct *sym = lookuptable + i*sizeof(t_symstruct); if (strcmp(sym->key, key) == 0) return sym->val; } return BADKEY; } /* ... */ switch (keyfromstring(somestring)) { case A1: /* ... */ break; case A2: /* ... */ break; case B1: /* ... */ break; case B2: /* ... */ break; case BADKEY: /* handle failed lookup */ } | cs |
stack overflow에서 나온 자료인데...그냥 if 쓰는게 더 나을꺼 같따...
위 소스에 대해서는 해당 링크를 참고 하면된다.
링크 :: https://stackoverflow.com/questions/4014827/best-way-to-switch-on-a-string-in-c
'0x0300 : Study > 0x0304 : Programming' 카테고리의 다른 글
C/C++ 제곱근 SQRT (0) | 2017.10.19 |
---|---|
C/C++ int main, void main (0) | 2017.10.18 |
python z3 (0) | 2017.07.24 |
python subprocess (0) | 2017.07.10 |
python json (0) | 2017.07.10 |