[출처 : http://ryu2811.tistory.com/23]


__attribute__ ((packed)) : 구조체 내의 최대 사이즈 데이터 타입으로 alignment 하지 않도록한다.

************************************************************
#include <stdio.h>

typedef struct _test_t_
{
    unsigned char uc1;
    unsigned char uc2;
    unsigned long long ui1;
} test_t;

typedef struct _testp_t_
{
    unsigned char uc1;
    unsigned
    char uc2;
    unsigned long long ui1;
} __attribute__ ((packed)) testp_t;

int main (void)
{
    test_t t1;
    testp_t t2;

    printf ("test_t : %d\n", sizeof (t1));
    printf ("testp_t : %d\n", sizeof (t2));

    return 0;
}
************************************************************
[ryu2811@qwertyryu-home ~/work/src/test] ./kkk
test_t : 16
testp_t : 10

[출처 : http://studyfoss.egloos.com/5409933]


gcc: 4.5.0
arch: x86_64


정렬 제한이란 데이터가 메모리에 저장될 때 해당 메모리의 주소에 대한 제약 사항을 말하는 것이다.
구체적으로는 데이터가 저장될 위치가 특정한 단위로 정렬(배치?) 되어 있어야 한다는 것을 뜻한다.

예를 들어 CPU 아키텍처에서 메모리 접근 시에 32비트 단위로 정렬된 주소에만 접근할 수 있다고 하자.


위의 데이터 A는 주소값이 4의 배수 형태이므로 정렬되어 있지만, B의 경우는 그렇지 않다.
B의 경우 해당 데이터에 접근 시 실행 환경에 따라 다음과 같은 여러 가지 결과를 일으킬 수 있다.
  • 비정상적인 메모리 접근을 인식하여 OS가 프로그램을 종료시킨다.
  • CPU 혹은 컴파일러가 B가 속한 두 메모리 영역에 접근한 뒤 적절한 비트 연산을 거쳐 원하는 값을 만들어준다.
  • 주소를 적당히 4의 배수로 변경하여 메모리에 접근한다.
  • 별 문제없이 실행된다.

어떤 경우이든 정렬되지 않은 메모리에 대한 접근은 성능에 도움이 되지 않는다.
최악(?)의 경우 B가 걸쳐있는 두 메모리 워드가 각자 다른 캐시 라인에 속할 수도 있음을 생각해 보자.
따라서 컴파일러는 컴파일 시에 이러한 정렬 제한을 고려하여 데이터를 메모리 상에 배치한다.
(물론 최종적으로는 링커가 이러한 작업을 수행한다.)

C 언어의 기본 데이터 타입은 모두 자신의 크기와 동일한 정렬 제한을 가진다.
gcc에서 이러한 정보는 sizeof와 __alignof__를 통해서 얻을 수 있다.

/* align-basic.c */

#include <stdio.h>

char c;
short s;
int i;
long l;
float f;
double d;

int main(void)
{
#define print_align(type, var)                        \
  printf(#type "\t%zd\t%zd\t%p\n", sizeof(type), __alignof__(type), &var)

  printf("type\tsize\talign\taddress\n");
  print_align(char, c);
  print_align(short, s);
  print_align(int, i);
  print_align(long, l);
  print_align(float, f);
  print_align(double, d);

  return 0;
}

위의 예제 프로그램을 빌드한 후 실행해보면 다음과 같은 결과를 얻을 수 있다.

$ gcc align-basic.c
$ ./a.out
type     size    align    address
char     1       1        0x40209a
short    2       2        0x402098
int      4       4        0x402094
long     8       8        0x402088
float    4       4        0x402090
double   8       8        0x402080

데이터 타입의 크기와 정렬 제한은 모두 동일한 크기를 가지며
실제 변수가 생성된 주소도 이러한 정렬 제한에 맞추어 졌음을 볼 수 있다.
좀 더 자세히 살펴보면 동일한 크기/정렬 제한을 가지는 데이터끼리 묶어서 배치하였으며
공간을 효율적으로 사용하기 위해 큰 크기/정렬 제한을 가지는 데이터부터 배치한 것도 볼 수 있다.

정렬 제한은 포인터 변환 시에도 반드시 고려해야 할 부분인데
cast 연산을 통해 강제로 변환한 주소값이 변환된 포인터가 가리키는 데이터 타입의
정렬 제한을 어길 수 있기 때문이다. 예를 들어 위의 예제에서
변수 i의 주소를 long * 타입으로 변환한다면 long 타입이 요구하는 8 바이트의 정렬 제한을
만족시키지 못하므로 해당 포인터를 통해 메모리에 접근 시 위에서 언급한 결과 중의 하나를 얻게 될 것이다.

이제 본격적으로 구조체에 대해서 살펴보기로 하자.
구조체는 여러 데이터 타입을 모아둔 집합체의 개념이므로 구조체를 이루는 각 멤버에 대해서도
위에서 언급한 정렬 제한을 모두 만족시켜야 한다. 또한 구조체는 위의 예제에서와 같이
메모리를 효율적으로 사용하기 위해 구조체 멤버의 순서를 임의로 조정할 수 없으며
반드시 구조체 선언 시에 명시된 순서대로 멤버를 메모리 상에 배치해야 한다.
따라서 이를 조정하기 위해 멤버 사이에 사용하지 않는 패딩 공간을 할당할 수 있다!!

구체적인 예를 통해 살펴보기로 하자.

/* align-struct.c */

#include <stdio.h>

struct sc {
  char c;
} sc;

struct sl {
  long l;
} sl;

struct scl {
  char c;
  long l;
} scl;

struct slc {
  long l;
  char c;
} slc;

int main(void)
{
#define print_align(type, var)                        \
  printf(#type "\t%zd\t%zd\t%p\n", sizeof(type), __alignof__(type), &var)

  printf("type\t\tsize\talign\taddress\n");
  print_align(struct sc, sc);
  print_align(struct sl, sl);
  print_align(struct scl, scl);
  print_align(struct slc, slc);

  return 0;
}

실행 결과를 살펴보기 전에 먼저 결과가 어떻게 나오게될 지 생각해 보자.
멤버로 오직 하나의 데이터 만을 가지고 있는 sc와 sl의 경우는
단순히 해당 멤버의 크기와 정렬 제한을 그대로 갖게될 것이다.
하지만 scl과 slc와 같이 서로 다른 데이터 타입으로 이루어진 경우라면 어떨까?

위의 예제를 실행해 보면 다음과 같은 결과를 얻을 수 있다.

$ gcc align-struct.c
$ ./a.out
type          size    align    address
struct sc     1       1        0x4020a8
struct sl     8       8        0x4020a0
struct scl    16      8        0x402080
struct slc    16      8        0x402090

앞서 말한대로 sc와 sl의 경우에는 특별한 사항이 없다.

scl의 경우에는 우선 정렬 제한이 8이라고 나오는데 이는 멤버 중의 가장 큰 정렬 제한을
가지는 long 타입의 정렬 제한을 그대로 물려받은 것이다.
또한 크기는 9가 아닌 16으로 나오는데 이는 8 바이트 단위로 정렬된 위치에 첫 멤버인
char 타입의 데이터가 저장되고 그 이후에 long 타입의 데이터가 다시 8바이트 단위로
정렬된 위치에 저장되기 때문이다. 즉 1~7번 바이트는 사용되지 않고 단순히 long 타입의
멤버의 정렬 제한을 보장하기 위한 용도로 채워진 패딩 바이트인 것이다.

구조체 내의 각 멤버의 위치는 offsetof 매크로를 통해 조사할 수 있는데
위의 경우 offsetof(struct scl, l)의 값은 8이 될 것이다.

slc의 경우에는 마찬가지로 정렬 제한이 8이고 크기가 16으로 나오는데
정렬 제한이야 그렇다 하겠지만 크기는 왜 9가 아닌 16으로 나오는 것일까?
만약 크기가 9이더라도 long 타입과 char 타입의 정렬 제한을 이미 모두 만족시키고 있는데
왜 굳이 구조체 뒤쪽에 불필요한 패딩 바이트를 추가하여 크기를 크게 만들었을까?

그 해답은 바로 배열 때문이다.
알고 있듯이 배열은 구조체와 같은 집합체이지만 동일한 데이터 타입으로 이루어진 것이며
중요한 사항은 배열을 이루는 각 원소들은 메모리 상에서 연속된 영역에 존재해야 하며
각 원소들 자체에 대해서도 정렬 제한을 만족시켜야 한다는 점이다.
만일 slc의 크기가 9가 된다면 배열의 다른 요소들은 정렬 제한을 만족하지 못할 것이므로
slc 내에 패딩을 포함시켜 slc의 크기 자체를 정렬 제한의 배수가 되도록 맞춘 것이다.

이렇게 구조체 내의 패딩 바이트가 추가되는 규칙을 알고 있다면
구조체 선언 시 각 멤버들의 위치를 잘 선택하여 효율적인 메모리 배치를 이루도록 할 수 있을 것이다.

하지만 상황에 따라 이러한 구조체의 크기/정렬 제한을 조정해야 하는 경우가 있을 수 있는데
(일반적으로는 그리 추천할 만 한 방법은 아닐 것이다)
이 때는 gcc에서 확장 기능으로 제공하는 aligned 혹은 packed 속성을 이용하면 된다.

역시 예제를 통해 살펴보기로 하자.

/* align-adjust.c */

#include <stdio.h>

struct asc {
  char c __attribute__((aligned(8)));
} sc;

struct __attribute__((aligned)) asl {
  long l;
} sl;

struct __attribute__((packed)) pscl {
  char c;
  long l;
} scl;

#pragma pack(push, 2)
struct pslc {
  long l;
  char c;
} slc;
#pragma pack(pop)

int main(void)
{
#define print_align(type, var)                        \
  printf(#type "\t%zd\t%zd\t%p\n", sizeof(type), __alignof__(type), &var)

  printf("type\t\tsize\talign\taddress\n");
  print_align(struct asc, sc);
  print_align(struct asl, sl);
  print_align(struct pscl, scl);
  print_align(struct pslc, slc);

  return 0;
}

앞서 말한대로 구조체의 정렬 제한은 멤버 중 가장 큰 값을 물려받는다.
따라서 구조체의 정렬 제한을 변경하는 대신 멤버의 정렬 제한을 변경한 경우에도
구조체에 영향을 미치게 된다. aligned 속성에는 2의 제곱수에 해당하는 숫자 만을
인자로 사용할 수 있으며 기본 정렬 제한보다 큰 값을 적용하는 경우에만 의미가 있다.
인자를 생략한 경우 실행 환경에 가장 적합한 정렬 제한을 가지도록 컴파일러가 결정한다.
반대로 packed 속성의 경우에는 정렬 제한을 가장 작은 수인 1로 줄이게 되며
1이 아닌 값을 적용하기 위해서는 #pragma를 이용할 수 있다.

위의 예제를 실행하면 다음과 같은 결과를 얻을 수 있다.

$ gcc align-adjust.c
$ ./a.out
type          size    align    address
struct asc    8       8        0x4020a8
struct asl    16      16       0x402080
struct pscl   9       1        0x40209a
struct pslc   10      2        0x402090

구조체에 비트 필드가 추가되는 경우 보다 복잡/미묘한 상황이 발생할 수 있는데
이에 대해서는 "더 이상의 자세한 설명은 생략한다".. ;;


=== 참고 문헌 ===
[출처 : http://jujubong.tistory.com/113]


[ Subversion 정리 ]

1. Subversion 을 사용하는 3가지 방식

   1) 탐색기 방식 
      Subversion을 윈도우즈 탐색기처럼 사용하도록 해주는 TortoiseSVN이 있다. 아래 링크를 통해 TortoiseSVN을 설치하여 사용한다. 
http://tortoisesvn.tigris.org 
   2) 웹 방식 
      Apache 서버를 구축하고 Subversion과 ViewVC를 설치하면 익스플로러를 통해 웹처럼 접근할수 있다. 
http://httpd.apache.org : Apache (apache_2.0.59-win32-x86.msi) 
http://subversion.tigris.org : Subversion (svn-1.4.3-setup.exe) 
http://www.viewvc.org : ViewVC (viewvc-1.0.3.zip) 
      ViewVC를 사용하지 않을 경우에는 "지정 도메인"/"해당 저장소" 로 접근 
    예) sample.com/repository

      ViewVC를 사용할 경우에는 "지정 도메인"/viewvc 로 접근 
    예) sample.com/viewvc 
   3) svn+ssh 방식 
       ssh클라이언트 툴인 putty를 이용하여 ssh로 암호화 된 상태로 Subversion을 사용한다.

       개인마다 키를 생성하여 이용하는데........

2. Subversion 주요 명령어 
   1) svnadmin create : 지정한 위치에 새로운 저장소(repository)를 생성한다. 
       형식 - svnadmin create "dest" 
       # svnadmin create /home/heyduk/repos 
   2) svn import : 생성된 저장소에 프로젝트 파일을 넣는다. 
       형식 - svn import "option" "source" "dest"

       # svn import -m "import first project" . file:///home/heyduk/repos/project1/trunk 
         -m 옵션을 통해 import 할때의 메세지를 남기고 현재 디렉토리의 내용을 project1라는 별도의 디렉토리에 넣는다. 
   3) svn checkout(co) : 저장소에 보관된 프로젝트 소스를 로컬 시스템의 작업 디렉토리로 복사하여 가져온다. 
       형식 - svn co "source" "dest" 
       # svn co file:///home/heyduk/repos/project1/trunk my_work 
   4) svn commit : 지역 작업본의 변경한 내용들을 저장소에 저장하며 프로젝트를 새로 리비젼시킨다. 
       형식 - svn commit(ci) "option" 
       # svn commit -m "main() modified"
 
   5) svn status : 작업 복사본에서 변경된 이후 아직 저장소로 commit 되지 않은 내용을 확인한다. 
       형식 - svn st "path"

       # svn status(st) main.c 
         출력 내용 중 첫번째 문자를 통해 상태를 확인할 수 있는데 그중 몇가지 주요 내용은 다음과 같다. 
         A : 추가됨 
         C : 충돌됨 
         D : 삭제됨 
         M : 수정됨 
         G : 병합됨

   6) svn diff : 저장소의 내용과 현재 작업 내용을 비교하여 변경한 부분(차이점)을 자세히 확인한다. 
   저장소와의 네트워킹은 일어나지 않으므로 실시간 변경 내용은 반영되지 않는다. 
      형식 - svn diff "path" , svn diff "option"

      # svn diff main.c 
        저장소의 내용과 현재 작업내용 중 main.c 파일의 차이를 확인

      # svn diff -r 1:2 
        리비전 1과 2의 차이를 확인

      # svn diff -r 1 main.c 
        리비전 1과 현재 작업중인 main.c의 차이를 확인

      # svn diff -r 2 
        리비전 2와 현재 작업중인 디렉토리의 파일내용 차이를 확인

   7) svn log : 특정 파일의 commit된 로그 메세지를 확인한다. 
      형식 - svn log "path" , svn log "option"

      # svn log main.c 
        main.c 파일이 commit 된 로그메세지를 출력

      # svn log -r 1:2 
        리비젼1부터 2까지의 로그메세지를 출력 
   8) svn update : 현재 작업디렉토리의 내용을 저장소의 최신 리비전으로, 또는 지정한 리비전으로 갱신한다. 
      형식 - svn update(up) "option"

      # svn up 
        현재 작업 내용을 최신내용으로 갱신 

      # svn up -r 1 
        현재 작업 내용을 리비전 1로 갱신(최신 리비전보다 이전으로 되돌리는 것도 가능하다.)
 
   9) svn add : 프로젝트에 파일이나 디렉토리를 추가한다. 
      형식 - svn add "path"

      # svn add Makefile 
        add로 추가한 경우 저장소에는 바로 반영되지 않기 때문에 commit 을 수행하여 새로운 리비전으로 갱신할 필요가 있다. 
   10) svn revert : 지역 작업 복사본에서 수행한 변경들을 이전 상태로 복원(실행 취소)한다. 
        형식 - svn revert "path" 

        # svn revert main.c 
           지역 복사본의 main.c 파일에 가했던 변경들을 모두 복원(취소)함
 
   11) svn merge : 저장소끼리, 또는 저장소와 지역 작업본간의 변경 내역을 작업본에 병합한다. 
        형식 - svn merge "source1" "source2" "dest" : 다른 저장소끼리의 변경 사항을 지정한 작업 복사본으로 병합함 
                  svn merge -r rev_num1:rev_num2 "dest" : 기본 저장소에 있는 두 리비전의 변경사항을 지정한 작업 복사본으로 병합함

       # svn merge file:///home/heyduk/repos/project1/trunk file:///home/heyduk/repos/project2/trunk my_work 
           저장소의 project1과 project2간의 차이를 지역 작업본인 my_work에 병합함

        # svn merge -r 6:7 file;///home/heyduk/repos/trunk my_work 
           저장소의 리비전 6~7의 변경내용을 지역 작업본인 my_work 에 병합함 

[출처 : http://jetzt.tistory.com/184]


cygwin 기본 접속은 정말 볼품없다 -_-;;
그러나 낙담하지 마시길...
Putty로 cygwin에 접속할 수있는 방법이 있습니다.

PuTTYcyg 링크

위의 링크에서 프로그램을 다운받고
 
적당한 위치에 압축을 풉니다.

압축을 푼 PuTTY로 실행시킬때 아래와 같은 옵션을 줍니다.

putty.exe -load local_session -cygterm bash --login -i

그러면 PuTTY로 cygwin에 접속되는 것을 볼 수 있습니다.
[출처 : http://www.imp17.com/tc/myevan/307?category=10]


관련글:
http://python.kr/viewtopic.php?t=26568

버튼이 리스트에 들어가게 될 경우 버튼 하나 하나에 일일이 이벤트 핸들러를 붙이는 것은 무리입니다.
더군다나 실시간으로 추가되거나 삭제될 경우에는 아예 불가능하죠.


이럴때 2.2 시절에는 lambda 로 했었는데


# vi: set sw=4 sts=4 expandtab:
buttons
= []

for button, index in zip(buttons, range(10)):
    button
.Bind(wx.EVT_BUTTON, lambda evt: self.OnClickButton(evt, index))

def OnClickButton(evt, index):
   
print index


요즘에는 내부가 전부 이터레이터식으로 변경되어서 안 되는 군요 (전부 마지막 값으로 ㄷㄷㄷ; )



# vi: set sw=4 sts=4 expandtab:
import wx
import
 copy

class TestFrame(wx.Frame):
 
 
def __init__(self, parent, title):
     
 wx
.Frame.__init__(self, parent, -1, title, pos=(0, 0), size=(320, 240))
 
     
self.CentreOnScreen(wx.BOTH)

        vsizer =
wx
.BoxSizer(wx.VERTICAL)
       for i in range(5):
             button =
wx
.Button(self, -1, "test%d" % i)
              button.Bind(wx.EVT_BUTTON,
self.OnClickButton)
            vsizer.Add(button)
     self.SetSizer(vsizer)

 
 
def OnClickButton(self, evt):
       print
evt
.GetEventObject().GetLabel() # POINT!!

class
 
TestApp(wx.App):
   def OnInit(self):
     
 
"OnInit"
       frame = TestFrame(None, "TestApp")
 
      frame
.Show()
       self.SetTopWindow(frame)
 
     
return True

    def OnExit(self):
 
     
"OnExit"
       pass

TestApp(redirect=False).MainLoop()


wxEvent::GetEventObject() 로 해결 하는 것이 가장 아름다운 것 같내요 : )
[출처 : http://www.imp17.com/tc/myevan/301?category=10]


트리 컨트롤은 디렉토리 구조를 표현할때 편리합니다.

사용자 삽입 이미지
















그런데 경로를 파싱해서 디렉토리를 만드는건 의외로 귀찮은 일이더라구요 = =)~

그래서 만들어본 예제입니다



# vi: set sw=4 sts=4 expandtab:
import wx
import os

class wxPathTreeCtrl(wx.TreeCtrl):
   def __init__(self, *args, **kwargs):
       wx.TreeCtrl.__init__(self, *args, **kwargs)

    def InitRoot(self, rootPath):
       self.branchd = {}
       self.DeleteAllItems()
       self.root = self.AddRoot(rootPath)
       self.SetPyData(self.root, ("ROOT", rootPath))
       return self.root

    def AppendPath(self, path):
       branch = path

        branches = []
       while branch:
           branch, leaf = os.path.split(branch)
           if branch in self.branchd:
               break
           elif branch:
               branches.append(branch)

        branches.reverse()

        last = self.branchd.get(branch, self.root)
       for branch in branches:
           last = self.AppendItem(last, os.path.split(branch)[1])
           self.SetPyData(last, ("DIR", branch))
           self.branchd[branch] = last

       
        item = self.AppendItem(last, os.path.split(path)[1])
       self.SetPyData(item, ("FILE", path))
       return item

    def ExpandAllDirs(self):
       self.ExpandDirs(self.root)

    def ExpandDirs(self, node):
       if self.GetPyData(node)[0] == "FILE":
           pass
       else:
           children = list(self.GenChildren(node))
           fileCount = len([child for child in children if self.GetPyData(child)[0] == "FILE"])
           if 0 == fileCount:
               self.Expand(node)

                for child in children:
                   self.ExpandDirs(child)

    def GetBranchd(self):
       return self.branchd

    def GenChildren(self, node):
       child = self.GetFirstChild(node)[0]
       while child:
           yield child
           child = self.GetNextSibling(child)

if __name__ == "__main__":

    class TestFrame(wx.Frame):
       def __init__(self, parent, title, size=(800, 600)):
           wx.Frame.__init__(self, parent, -1, title, pos=(0, 0), size=size)
           self.CentreOnScreen(wx.BOTH)

            self.pathTreeCtrl = wxPathTreeCtrl(self)
           self.pathTreeCtrl.InitRoot("root")
           self.pathTreeCtrl.AppendPath("bin/main.exe")
           self.pathTreeCtrl.AppendPath("data/char/pc/warrior/warriror.png")
           self.pathTreeCtrl.AppendPath("data/char/pc/warrior/warriror2.png")
           self.pathTreeCtrl.ExpandAllDirs()

    class TestApp(wx.App):
       def OnInit(self):
           toolFrame = TestFrame(None, "TEST")
           toolFrame.Show()
           self.SetTopWindow(toolFrame)
           
           return True

    TestApp(redirect=False).MainLoop()

[출처 : http://www.imp17.com/tc/myevan/42?category=10]


import time
from threading import Thread

class WorkThread(Thread):
    def __init__(self, onCurrentPos, onTotalCount):
        Thread.__init__(self)
        self.onCurrentPos = onCurrentPos
        self.onTotalCount = onTotalCount

    def run(self):
        self.onTotalCount(100)

        print "!!"
        for i in range(100):
            self.onCurrentPos(i+1)
            time.sleep(0.3)


import wx

class GagePanel(wx.Panel):
    MAX = 1000
    def __init__(self, parent, title):
        wx.Panel.__init__(self, parent, -1)

        self.title = title
        self.text = wx.StaticText(self, -1, title, (10, 10))

        gage = wx.Gauge(self, -1, self.MAX, (10, 30), (250, 25),
                    wx.GA_HORIZONTAL|wx.GA_SMOOTH)

        self.runButton = wx.Button(self, -1, "RUN", (210, 70))
        self.Bind(wx.EVT_BUTTON, self.OnRun, self.runButton)

        gage.SetBezelFace(5)
        gage.SetShadowWidth(5)
        self.gage = gage
        self.pos = 0
        self.count = 1

        self.workThread = None

    def OnCurrentPos(self, pos):
        self.pos = pos + 1
        self.gage.SetValue(self.MAX * self.pos / self.count)
        self.text.SetLabel("%s (%d/%d)" % (self.title, self.pos, self.count))

    def OnTotalCount(self, count):
        self.count = count

    def OnRun(self, event):
        if self.workThread:
            return

        self.workThread = WorkThread(self.OnCurrentPos, self.OnTotalCount)
        self.workThread.start()

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Progress",
                pos = (0, 0), size = (320, 240))

        self.CentreOnScreen(wx.BOTH)

        self.panel = GagePanel(self, "progress")
        self.panel.Layout()

class App(wx.App):
    def OnInit(self):
        frame = MainFrame()
        self.SetTopWindow(frame)
        frame.Show()
        return True

App(redirect = False).MainLoop()

+ Recent posts