$bash

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< stringint > 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