[출처 : http://creaplz.tistory.com/29]


1. 사전 객체의 기초 연산

  - 내부적으로 해쉬(Hash)라는 기법을 이용하여 자료 저장
  - 자료의 순서를 가지지 않는다.
  - 값은 임의의 객체가 될 수 있지만, 키는 변경 불가능(Immutable) 자료형이어야 한다.
  - 문자열, 숫자, 튜플은 키가 될 수 있지만 리스트, 사전은 키가 될 수 없다.

>>> member = {'basketball': 5, 'soccer':11, 'baseball':9}
>>> member['baseball'] # 검색
9
>>> member['volleyball'] = 7 # 새 값 설정
>>> member
{'soccer': 11, 'basketball': 5, 'baseball': 9, 'volleyball': 7}
>>> member['volleyball'] = 10 # 변경
>>> member
{'soccer': 11, 'basketball': 5, 'baseball': 9, 'volleyball': 10}
>>> len(member) # 길이
4
>>> del member['baseball'] # 항목 삭제
>>> def add(a, b):
 return a+b

>>> def sub(a, b):
 return a-b

>>> action = {0:add, 1:sub} # 함수를 키나 값으로 활용
>>> action[0](4, 5)
9
>>> action[1](4, 5)
-1

※ 다양한 방법으로 사전 객체 생성하기

>>> dict() # 빈 사전 생성
{}
>>> dict(one=1, two=2) # 키워드 인수를 지정하여 사전 생성
{'two': 2, 'one': 1}
>>> dict([('one', 1), ('two', 2)]) # (key, value) 쌍의 시퀀스로부터 사전 생성
{'two': 2, 'one': 1}
>>> dict({'one':1, 'two':2}) # 사전 객체를 인수로 받아 새로운 사전 생성
{'two': 2, 'one': 1}
>>> keys = ['one', 'two', 'three']
>>> values = (1, 2, 3)
>>> zip(keys, values) # zip은 자료를 순서대로 묶어줌
[('one', 1), ('two', 2), ('three', 3)]
>>> dict(zip(keys, values))
{'three': 3, 'two': 2, 'one': 1}

2. 사전 객체의 메쏘드

>>> phone = {'jack': 9465215, 'jim': 1111, 'Joseph': 6584321}
>>> phone.keys() # 키의 리스트를 반환
['jim', 'Joseph', 'jack']
>>> phone.values() # 값의 리스트를 반환
[1111, 6584321, 9465215]
>>> phone.items() # (키, 값)의 리스트를 반환
[('jim', 1111), ('Joseph', 6584321), ('jack', 9465215)]
>>> 'jack' in phone # 'jack'이 phone의 키에 포함되어 있는가?
True
>>> 'lee'in phone
False
>>> p = phone # 사전 레퍼런스 복사(사전 객체는 공유됨)
>>> phone['jack'] = 1234 # 변경
>>> phone
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234}
>>> p # phone과 동일하게 변경됨
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234}
>>> ph = phone.copy() # 사전 복사(별도의 사전 객체를 리턴)
>>> phone['jack'] = 1111 # 변경
>>> ph # phone과 다른 객체이기 때문에 변경 안되었음
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234}
>>> ph.get('jack') # 'jack'키에 대한 값을 얻음
1234
>>> ph.get('creaplz') # 값이 없는 경우 None을 리턴
>>> ph['creaplz'] # ph['creaplz']는 값이 없는 경우 예외를 발생 

Traceback (most recent call last):
  File "<pyshell#98>", line 1, in <module>
    ph['creaplz']
KeyError: 'creaplz'

>>> ph.get('creaplz', 510) # 인수를 하나 더 써주면, 존재하지 않는 키인데도 지정한 인수를 리턴
510
>>> ph # 사전에는 변화 없음
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234}
>>> ph.setdefault('creaplz', 510) # 'creaplz'의 값을 꺼내되, 없으면 510로 설정
510
>>> ph # 'creaplz': 510이 설정
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234, 'creaplz': 510}
>>> ph.popitem() # 한 아이템을 꺼냄
('jim', 1111)
>>> ph.popitem() # 또 한 아이템을 꺼냄
('Joseph', 6584321)
>>> ph # popitem 결과
{'jack': 1234, 'creaplz': 510}
>>> ph.pop('jack') # 지정한 키를 pop
1234
>>> ph
{'creaplz': 510}
>>> phone.update(ph) # 사전 phone의 내용을 ph으로 추가 갱신
>>> phone
{'jim': 1111, 'Joseph': 6584321, 'jack': 1234, 'creaplz': 510}
>>> phone.clear() # 사전의 모든 입력을 제거
>>> phone
{}

3. 심볼 테이블

>>> a = 1
>>> b = 100
>>> name = 'creaplz'
>>> dic = {'Python': 'Good', 'Perl': 'Not Good'}
>>> globals() # 전역 영역(모듈 영역)의 심볼 테이블(사전) 얻음
{'a': 1, 'b': 100, 'name': 'creaplz', '__builtins__': <module '__builtin__' (built-in)>, 'dic': {'Python': 'Good', 'Perl': 'Not Good'}, '__name__': '__main__', '__doc__': None}
>>> locals() # 지역 영역의 심볼 테이블 얻음(대화형 최상위 모드에서는 globals()와 별 차이가 없음)
{'a': 1, 'b': 100, 'name': 'creaplz', '__builtins__': <module '__builtin__' (built-in)>, 'dic': {'Python': 'Good', 'Perl': 'Not Good'}, '__name__': '__main__', '__doc__': None}
>>> # 객체의 심볼 테이블
>>> class C:
 x = 10
 y = 20

>>> C.__dict__ # 클래스의 심볼 테이블 얻음
{'y': 20, 'x': 10, '__module__': '__main__', '__doc__': None}
>>> c = C() # 객체 생성
>>> c.a = 100
>>> c.b = 200
>>> c.__dict__ # 클래스 인스턴스의 심볼 테이블 얻음
{'a': 100, 'b': 200}
>>> def f():
 pass
>>> f.a = 1
>>> f.b = 2
>>> f.__dict__ # 함수의 심볼 테이블 얻음
{'a': 1, 'b': 2}


4. 사전을 for문으로 참조하기

>>> D = {'a': 1, 'b': 2, 'c': 3}
>>> for key in D: # 사전 자체 이용
 print key, D[key]


a 1
c 3
b 2

>>> for key, value in D.items():
 print key, value


a 1
c 3
b 2

>>> items = D.items()
>>> items.sort() # 순서 정렬(키 기준)
>>> items
[('a', 1), ('b', 2), ('c', 3)]
>>> for k, v in items:
 print k, v


a 1
b 2
c 3

>>> # 정렬된 결과를 한번만 이용하기
>>> for key, value in sorted(d.items()): # 순서 정렬(키 기준)
 print key, value


one 1
three 3
two 2

>>> for key, value in sorted(d.items(), key=lambda item:item[1]): # 순서 정렬(값 기준)
 print key, value


one 1
two 2
three 3


[출처 : http://creaplz.tistory.com/28]


1. 튜플의 연산

>>> t = () # 공 튜플
>>> t = (1, 2, 3) # 괄호 사용
>>> t = 1, 2, 3
>>> r = (1,)
>>> r = 1, # 괄호는 없어도 콤마(,)는 무조건 있어야 함
>>> t * 2 # 반복
(1, 2, 3, 1, 2, 3)
>>> t + ('PyKUG', 'user') # 연결
(1, 2, 3, 'PyKUG', 'user')
>>> print t[0], t[1:3] # 인덱싱, 슬라이싱
1 (2, 3)
>>> len(t) # 길이
3
>>> 1 in t # 멤버십 테스트
True
>>> t[10] = 100 # 튜플은 변경 불가능(Imumutable) 자료형

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    t[10] = 100
TypeError: 'tuple' object does not support item assignment

>>> t = (1, 2, 3)
>>> u = t, 4, 5 # 중첩
>>> u
((1, 2, 3), 4, 5)
>>> x, y = 1, 2 # 복수개의 자료 치환
>>> x, y = y, x # swap
>>> x, y
(2, 1)

2. 패킹과 언팩킹

>>> # 튜플
>>> t = 1, 2, 'hello' # 팩킹
>>> x, y, z = t # 언팩킹
>>> # 리스트
>>> a = ['foo', 'bar', 4, 5] # 팩킹
>>> [x, y, z, w] = a # 언팩킹

3. 리스트와의 공통점과 차이점
  1) 공통점
    - 임의의 객체를 저장할 수 있다
    - 시퀀스 자료형(인덱싱, 슬라이싱, 연결, 반복 지원)
  1) 차이점
    - 문자열과 같은 변경 불가능(Immutable) 시퀀스형이다. 따라서 아이템을 변경하려면 슬라이싱과 연결(Concatenation)을 이용해야 한다.
    - 메쏘드를 가지지 않는다.
  3) 상호 변환

>>> T = (1, 2, 3, 4, 5)
>>> L = list(T)
>>> L[0] = 100
>>> L
[100, 2, 3, 4, 5]
>>> T = tuple(L)
>>> T
(100, 2, 3, 4, 5)

4. 튜플 사용하는 경우
  1) 함수에 있어서 하나 이상의 값을 리턴하는 경우

>>> def calc(a, b):
 return a+b, a-b 

>>> x, y = calc(5, 4)

  2) 문자열 포매팅

>>> print 'id : %s, name : %s' % ('creaplz', 'hong')
id : creaplz, name : hong

  3) 튜플에 있는 값들을 함수 인수로 사용할 때(가변 인수)

>>> args = (4, 5)
>>> calc(*args)
(9, -1)

  4) 고정된 값을 표현

>>> d = {'one': 1, 'two': 2}
>>> d.items()
[('two', 2), ('one', 1)]

[출처 : http://creaplz.tistory.com/27]


1. 리스트의 일반 연산

>>> l = [] # 공 리스트
>>> l = [1, 2, "Great"]
>>> print l[0], l[-1] # 인덱싱
1 Great
>>> print l[1:3], l[:] # 슬라이싱
[2, 'Great'] [1, 2, 'Great']
>>> L = range(10)
>>> L[::2] # 확장 슬라이싱
[0, 2, 4, 6, 8]
>>> l * 2 # 반복
[1, 2, 'Great', 1, 2, 'Great']
>>> l + [3, 4, 5] # 연결
[1, 2, 'Great', 3, 4, 5]
>>> len(l) # 길이
3
>>> 4 in L # 멤버십 테스트
True
>>> l[1] = l[1] + 23 # 변경
>>> l
[1, 25, 'Great']
>>> l[0:2] = [5, 10]  # 교체(슬라이스에서는 양쪽변의 크기가 달라도 됨)
>>> l
[5, 10, 'Great']
>>> l[0:2] = [] # 공 리스트를 이용한 삭제
>>> l
['Great']
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del a[0] # del을 이용한 삭제
>>> del a[1:]
>>> a
[1]
>>> a = range(4)
>>> del a[::2]
>>> a
[1, 3]
>>> a = [123, 1234]
>>> a[1:1] = ['spam', 'ham'] # 슬라이스 삽입
>>> a
[123, 'spam', 'ham', 1234]
>>> a[:0] = a # 제일 앞에 추가
>>> a
[123, 'spam', 'ham', 1234, 123, 'spam', 'ham', 1234]
>>> a = range(4)
>>> a
[0, 1, 2, 3]
>>> a[::2]
[0, 2]
>>> a[::2] = range(0, -2, -1) # 교체(확장 슬라이스에서는 양쪽변의 개수가 맞아야 함)
>>> a
[0, 1, -1, 3]
>>> a[::2] = range(3)

Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    a[::2] = range(3)
ValueError: attempt to assign sequence of size 3 to extended slice of size 2


2. 중첩 리스트
>>> L = [1, ['a', ['x', 'y'], 'b'], 3]
>>> L[0]
1
>>> L[1]
['a', ['x', 'y'], 'b']
>>> L[1][1]
['x', 'y']
>>> L[1][1][1]
'y'

3. 리스트의 메쏘드들
>>> s = [1, 2, 3]
>>> s.append(5) 
# 리스트 마지막에 추가
>>> s
[1, 2, 3, 5]
>>> s.insert(3, 4) # 3 위치에 4 삽입
>>> s
[1, 2, 3, 4, 5]
>>> s.index(3) # 값 3의 위치는?
2
>>> s.count(2) 
# 값 2의 개수는?
1
>>> s.reverse() 
# 순서 뒤집기(리턴 값 없음)
>>> s
[5, 4, 3, 2, 1]
>>> s.sort() 
# 정렬(리턴 값 없음)
>>> s
[1, 2, 3, 4, 5]
>>> s.remove(3) 
# 3 삭제(제일 처음 것 1개만 삭제됨)
>>> s
[1, 2, 4, 5]
>>> s.extend([6, 7]) # 리스트를 추가
>>> s
[1, 2, 4, 5, 6, 7]
>>> s.pop() 
# 기본적으로 마지막 자료 pop
7
>>> s
[1, 2, 4, 5, 6]
>>> s.pop(0) 
# 위치 지정 가능
1
>>> s
[2, 4, 5, 6]


4. 튜플 혹은 리스트의 리스트 반복 참조하기

>>> lt = [('one', 1), ('two', 2), ('three', 3)]
>>> for t in lt:
 print 'name=', t[0], 'num=', t[1]


name= one num= 1
name= two num= 2
name= three num= 3

>>> for t in lt:
 print 'name=%s, num=%s' % t


name=one, num=1
name=two, num=2
name=three, num=3

>>> for name, num in lt:
 print name, num


one 1
two 2
three 3

5. 원하는 순서대로 정렬하기

>>> L = [1, 5, 3, 9, 8, 4, 2]
>>> newList = L.sort() # 리턴되는 값이 없음(자체 변경)
>>> print newList
None
>>> L # 자체 변경됨
[1, 2, 3, 4, 5, 8, 9]
>>> cmp(1, 2)
-1
>>> cmp(5, 2)
1
>>> cmp('abc', 'abc')
0
>>> def mycmp(a1, a2):
 return cmp(a2, a1)

>>> L = [1, 5, 3, 2, 4, 6]
>>> L.sort(mycmp) # 비교 함수 변경
>>> L
[6, 5, 4, 3, 2, 1]
>>> L = [1, 6, 3, 8, 6, 2, 9]
>>> L.sort(reverse=True) # 역순 정렬 지원
>>> L
[9, 8, 6, 6, 3, 2, 1]
>>> L = ['123', '34', '56', '2345']
>>> L.sort()
>>> L
['123', '2345', '34', '56']
>>> L.sort(key=int) # 비교 키를 지원
>>> L
['34', '56', '123', '2345']
>>> # sorted 내장 함수
>>> L = [1, 6, 3, 8, 6, 2, 9]
>>> newList = sorted(L) # L은 변경 내용이 없고, 새로운 리스트가 리턴
>>> newList
[1, 2, 3, 6, 6, 8, 9]
>>> newList = sorted(L, reverse=True)
>>> newList
[9, 8, 6, 6, 3, 2, 1]
>>> L = ['123', '34', '56', '2345']
>>> sorted(L, key=int)
['34', '56', '123', '2345']
>>>
 # reversed 내장 함수
>>> newList = reversed(L) 
# L은 변경 내용이 없고, 반복자(Iterator)가 리턴
>>> newList
<listreverseiterator object at 0x00DAACF0>
>>> for ele in reversed(L):
 print ele


2345
56
34
123

6. 리스트 내장

>>> # 10보다 작은 정수 중 홀수의 제곱만 리턴하는 예
>>> L = [k * k for k in range(10) if k % 2]
>>> L
[1, 9, 25, 49, 81]
>>> # 2의 배수와 3의 배수 중 두수의 합이 7의 배수가 되는 두 수의 곱의 리스트 만드는 예
>>> [(i, j, i*j) for i in range(2, 100, 2)
 for j in range(3, 100, 3)
 if (i+j) % 7 == 0]
[(2, 12, 24), (2, 33, 66), (2, 54, 108), (2, 75, 150), (2, 96, 192), (4, 3, 12), (4, 24, 96), (4, 45, 180), (4, 66, 264), (4, 87, 348), (6, 15, 90), (6, 36, 216), (6, 57, 342), (6, 78, 468), (6, 99, 594), (8, 6, 48), (8, 27, 216), .. 출력 생략.. , (98, 63, 6174), (98, 84, 8232)]
>>> # 리스트 내장 효과의 발생자
>>> sum(x*x for x in range(10)) # 메모리를 내장하는 새로운 리스트를 생성 안함
285
>>> (x*x for x in range(10)) # 발생자(Generator) 생성
<generator object at 0x00DC3D78>
>>> sum([x*x for x in range(10)]) # 새로운 리스트를 생성하여 sum 연산
285

7. 순환 참조 리스트

>>> GNU = ['is not Unix']
>>> GNU.insert(0, GNU)
>>> GNU

[[...], 'is not Unix']
>>> GNU[0]
[[...], 'is not Unix']
>>> GNU[0][0]
[[...], 'is not Unix']
>>> GNU[0][0][0]
[[...], 'is not Unix']

8. 순차적인 정수 리스트 만들기

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(5, 15)
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> range(5, 15, 2)
[5, 7, 9, 11, 13]
>>> range(0, -10, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
 
>>> sun, mon, tue, wed, thu, fri, sat = range(7)

9. 배열 표현

>>> mat = [[0] * 4] * 3
>>> mat
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> mat[0][0] = 100
>>> mat
[[100, 0, 0, 0], [100, 0, 0, 0], [100, 0, 0, 0]]
>>> mat = [[0]*4 for x in range(3)]
>>> mat
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> mat[0][0] = 100
>>> mat
[[100, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

※ arrary,  NumPy 모듈 사용하여 고수준의 행렬 표현 및 연산 가능

[출처 : http://creaplz.tistory.com/26]

1. 시퀀스 자료형 : 문자열, 리스트, 튜플
  1) 인덱싱(indexing) : [k]

>>> s = 'abcdef' # 문자열
>>> l = [100, 200, 300] # 리스트
>>> s[0]  # 참조
'a'
>>> s[1]
'b'
>>> s[-1]
'f'
>>> l[1]
200
>>> l[1] = 900 # 치환

  2) 슬라이싱(Slicing) : [s:t]

>>> s = 'abcdef'
>>> l = [100, 200, 300]
>>> s[1:3] # 1번 위치와 3번 위치 사이를 나타냄
'bc'
>>> s[1:] # 1부터 끝까지
'bcdef'
>>> s[:] # 처음부터 끝까지
'abcdef'
>>> s[-100:100] # 범위를 넘어서면 범위 내의 값으로 자동 처리
'abcdef'
>>> l[:-1] # 맨 오른쪽 값을 제외하고 모두
[100, 200]
>>> s[::2] # 2칸 단위로
'ace'
>>> s[::-1] # 거꾸로
'fedcba'

  3) 연결하기(Concatenation) : +

>>> s = 'abc' + 'def'
>>> s
'abcdef'
>>> L = [1, 2, 3] + [4, 5, 6]
>>> L

[1, 2, 3, 4, 5, 6]

  4) 반복하기(Repetition) : *

>>> s = 'Abc'
>>> s * 4
'AbcAbcAbcAbc'
>>> L = [1, 2, 3]
>>> L * 2
[1, 2, 3, 1, 2, 3]

  5) 멤버십 테스트(Membership Test) : in

>>> t = (1, 2, 3, 4, 5)
>>> 2 in t
True
>>> 10 not in t
True
>>> 'ab' in 'abcd' # 문자열인 경우 부분 문자열 확인 가능
True

  6) 길이 정보 : len

>>> l = [1, 2, 3]
>>> len(l)

3

2. 문자열 정의
  1) 한 줄 문자열 : ' or "
  2) 여러 줄 문자열 : ''' or """
  3) 이스케이프 문자

Escape SequenceMeaning
\newline Ignored
\\ Backslash (\)
\' Single quote (')
\" Double quote (")
\a ASCII Bell (BEL)
\b ASCII Backspace (BS)
\f ASCII Formfeed (FF)
\n ASCII Linefeed (LF)
\N{name} Character named name in the Unicode database (Unicode only)
\r ASCII Carriage Return (CR)
\t ASCII Horizontal Tab (TAB)
\uxxxx Character with 16-bit hex value xxxx (Unicode only)
\Uxxxxxxxx Character with 32-bit hex value xxxxxxxx (Unicode only)
\v ASCII Vertical Tab (VT)
\ooo Character with octal value ooo
\xhh Character with hex value hh

  4) 문자열 연산(시퀀스 자료형의 특징 참조)

>>> str1 = 'Firtst String'
>>> str1[0] = 'f' # 변경 불가능(Immutable) 자료형이므로 에러 발생
 

Traceback (most recent call last):
  File "<pyshell#54>", line 1, in <module>
    str1[0] = 'f'
TypeError: 'str' object does not support item assignment

3. 문자열 변경

>>> s = 'spam and egg'
>>> s = s[:5] + 'cheese' + s[5:]
>>> s
'spam cheeseand egg'

4. 문자열 포매팅(Formatting) : %s, %r, %c, %d, %i, %u, %o, %x, %X, %e, %E, %f, %g, %G

>>> format = 'name = %s, age = %s'
>>> format % ('gslee', 24)
'name = gslee, age = 24'
>>> "%s -- %s -- %d -- %f -- %e" % ((1, 2), [3, 4, 5], 5, 5.3, 101.3)
'(1, 2) -- [3, 4, 5] -- 5 -- 5.300000 -- 1.013000e+002'
>>> print '%(이름)s -- %(전화번호)s' % {'이름': '이강성', '전화번호': 5284}
이강성 -- 5284

5. 문자열 메쏘드

>>> # 대ㆍ소문자로 변환 관련 메쏘드
>>> s = 'i like programming.'
>>> s.upper()
'I LIKE PROGRAMMING.'
>>> s.upper().lower()
'i like programming.'
>>> 'I Like Programming'.swapcase()
'i lIKE pROGRAMMING'
>>> s.capitalize()
'I like programming.'
>>> s.title()
'I Like Programming.'
>>> # 검색 관련 메쏘드
>>> s = 'i like programming, i like swimming.'
>>> s.count('like') # 문자열 s에서 'like'라는 부분문자열이 발생한 횟수를 리턴
2
>>> s.find('like') # 'like'의 offset를 리턴(검색)
2
>>> s.find('my') # 찾는 문자열이 없을 경우 -1 리턴
-1
>>> s.rfind('like') # find와 같지만 문자열 s의 뒤쪽부터 탐색
22
>>> s.index('like')
2
>>> s.index('my') # find와 같지만 찾는 문자열이 없을 경우 예외 발생

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    s.index('my')
ValueError: substring not found

>>> s.rindex('like') # index와 같지만 문자열 s의 뒤쪽부터 탐색
22
>>> s.startswith('i like') # i like로 시작하는 문자열인가?
True
>>> s.endswith('swimming.') # swimming.으로 끝나는 문자열인가?
True
>>> s.startswith('progr', 7) # 7번째 문자열이 progr로 시작하는가?
True
>>> s.endswith('like', 0, 26) # 0부터 26번째 위치 사이의 문자열이 like로 끝나는가?
True
>>> # 편집 및 치환 관련 메쏘드
>>> u = ' spam and ham '
>>> u.strip() # 좌우 공백 제거
'spam and ham'
>>> u.rstrip() # 오른쪽 공백 제거
' spam and ham'
>>> u.lstrip() # 왼쪽 공백 제거
'spam and ham '
>>> '   abd   '.strip()
'abd'
>>> '><>abc<><><>'.strip('<>')
'abc'
>>> '><><abc<><><>\n'.strip('<>')
'abc<><><>\n'
>>> u'\u4000\u4001abc\u4000'.strip(u'\u4000')
u'\u4001abc'
>>> u.replace('spam', 'spam, egg') # 'spam'을 'spam, egg'로 변경
' spam, egg and ham '
>>> # 문자열 분리와 결합 관련 메쏘드
>>> u = '  spam and ham    '
>>> u.split() # 공백으로 분리
['spam', 'and', 'ham']
>>> u.split('and') # 'and로 분리
['  spam ', ' ham    ']
>>> t = u.split()
>>> ':'.join(t) # ':' 문자로 결합
'spam:and:ham'
>>> print '\n'.join(t) # 줄 바꾸기로 결합
spam
and
ham

>>> lines = '''first line
second line
third line'''
>>> lines.splitlines() # 라인 단위로 분리
['first line', 'second line', 'third line']
>>> s = 'one:two:three:four'
>>> s.split(':', 2) # 두 번만 분리
['one', 'two', 'three:four']
>>> s.rsplit(':', 1) # 오른쪽부터 처리
['one:two:three', 'four']
>>> # 정렬 관련 메쏘드
>>> u = 'spam and egg'
>>> u.center(60) # 전체 60문자의 가운데에 맞춤
'                        spam and egg                        '
>>> u.ljust(60) # 왼쪽에 맞춤
'spam and egg                                                '
>>> u.rjust(60) # 오른쪽에 맞춤
'                                                spam and egg'
>>> u.center(60, '-') # 공백 대신 '-' 문자로 채움
'------------------------spam and egg------------------------'
>>> '1\tand\t2'.expandtabs() # 탭(\t)을 8자 공백으로 사용
'1       and     2'
>>> '1\tand\t2'.expandtabs(4)
'1   and 2'
>>> # 구성된 문자열의 특성 유무 파악 관련 메쏘드
>>> '1234'.isdigit()
True
>>> 'abcd'.isalpha()
True
>>> '1abc234'.isalnum()
True
>>> 'abc'.islower() # 소문자인가?
True
>>> 'ABC'.isupper()
True
>>> ' \t\r\n'.isspace() # 공백문자인가?
True
>>> 'This Is A Title'.istitle() # 제목 문자열인가?
True
>>> # 채우기 및 자리 맞추기 관련 메쏘드
>>> s = '123'
>>> s.zfill(5)
'00123'
>>> 'goofy'.zfill(6) # 빈 자리는 0으로 채워짐
'0goofy'

6.. string 모듈

>>> import string
>>> d = string.letters + string.digits
>>> d
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
>>> userid = raw_input('your id : ')
your id : creaplz510
>>> for ch in userid:
 if ch not in d:
  print 'invalid user id'
  break

7. 유니 코드

>>> unicode('한글', 'cp949') # 인코딩을 명시적으로 알려주어야 함
u'\ud55c\uae00'
>>> unicode('한글', 'cp949').encode('utf-8') # 'utf-8' 코드로 변환
'\xed\x95\x9c\xea\xb8\x80'
>>> len('한글과 세종대왕')
15
>>> len(unicode('한글과 세종대왕', 'mbcs'))
8
>>> u = unicode('한글과 세종대왕', 'mbcs')
>>> print u[0]

>>> print u[1]

>>> ord('A') # 문자 'A'의 ASCII 코드값
65
>>> chr(65) # 코드 65에 해당하는 문자
'A'
>>> ord(unicode('가', 'cp949'))
44032
>>> hex(ord(unicode('가', 'cp949')))
'0xac00'
>>> print unichr(0xac00)

>>> print unichr(0xd7a3)

8. 문서 문자열 : 도움말 사용
# file : docstring.py
'''
Module __doc__ string
line1
line2
'''
class Ham:
    "Ham class __doc__ string"
    def func(self):
        "Ham class func __doc__ string"
        pass

>>> import docstring
>>> print docstring.__doc__
Module __doc__ string
line1
line2
>>> print docstring.Ham.__doc__
Ham class __doc__ string
>>> print docstring.Ham.func.__doc__
Ham class func __doc__ string


[출처 : http://semicolon238.com/333]


 프로그래밍을 하다보면 여러가지 난관에 부딧친다. 이는 정적인 부분에서도 마찬가지인데, 그난관이란 대체적으로 논리문제 아니면, 내가 만든함정문제 아니면, 자료구조문제이다. 뭐 이 포스팅에서 그렇게까지 크게 이야기할 것은 없고, 오늘 해볼것은 바로 배열의 차원을 속이는 것이다. 뭐 속인다고해도 크게 속일 것은 없고, 1차원과 2차원을 속일 뿐이다.

 그렇다면, 왜 속여야하는가? 그냥 주어진대로 쓰고, 솔직하게 바꾸면 어디가 덧나나? 라고 생각할지 모르지만 천만의 말씀 만만의 콩떡이다. 때론 솔직히 돌아가는 것보다 거짓말을 하는것이 문제해결을 쉽게 만든다. 2차원 배열은 깔끔하고 직관적이지만 어떤 특정한 상황에선 1차원이 더 의미 있을 수도 있다. 물론 그반대경우가 될 수도 있다. 반드시 4x4타일맵을 작성하는데 있어 2차원배열을 종용하는 사람은 유도리가 없다는게 내 생각이다. 항상 특수한 경우를 생각해야되고 어떤게 더 옳을지 생각해보는것은 바로 프로그래머의 몫이다.

 그리고 조금은 다른이야기지만 2차원배열보다 1차원배열이 더 좋은 이유는 오버헤드가 없다는 것이다. 2차원배열은 아무래도 다차원이다 보니 세로의 원소를 읽을때는 메모리를 내부적인 포인터가 꽤 크게 건너뛰어야된다. 그러나 결국 메모리는 선형적이고 1차원배열과 그 쓰임이 비슷하다보니 2차원, 혹은 다차원배열보다는 1차원배열이 훨씬빠르다는 결론에이르게된다. 이는 배열의 크기가 크면클수록 더 커지게된다. 물론 이러한 기법을 이용한다고 해서 크게 성능이 향상되지는 않지만(어차피 2차원을 쓰는것처럼 1차원을 쓰므로) 그래도 순수하게 2차원배열을 쓰는것보다는 약간 빠르게된다. 물론 속도때문에 항상 이렇게 써야된다고 자신을 학대하는것 역시 옳지않은 생각이므로 적절한 상황에서 자신의 판단에 맞추어 제대로 쓰도록 하자. 그럼 지저분한 서론은 집어치우고 당장 본론으로 넘어가보겠다.


int arr[5][3];
 여기에 이렇게 2차원배열 하나가 있다. 왜 쓰는지는 필요없고, 그래봤자 접근과 데이터변경의 용의성인 이유가 대부분이니 넘어가자.


arr[3][2] = 3;
 이런식으로 데이터를 원하는위치에 쉽게 넣을 수 있다. 뭐 여기까진 좋다. 


void array_print(int* arr[]);
 함수가 조금씩 더러워지기 시작한다.


void file_save()
{
    for(int y = 0; y < 5; ++y)
        for(int x = 0; x < 3; ++x)
            fprintf("%d", arr[y][x]);
}
 저장할때도 길어진다. 만일 일반자료형이 아닌, 내부적으로 계산해주어야 되는 경우나 데이터가 여러차원으로 분리되어있을떄는 저장위치 잡기도 애매해진다. 그럼 여기서 정리. 어떻게하면 1차원배열로 2차원처럼 보일 수있을까.


int arr[x * y];
 1차원과 2차원을 곱한것이 결국엔 해당 2차원배열의 원소수이므로, 이 원소수를 한계값으로 두는 1차원배열을 생성한다. 이거라면 여기 올릴만한 문제가 아니잖아! 라고 할텐데.. 조금 더 설명을 들어보시라.


for(int i = 0; i < (x * y); ++i)
    arr[i] = 3;
 값을 넣을때는 이렇게 넣는다. 그러나 그렇게되면 어렵잖아! 라고 말할것이 분명하다. 그렇다. 1차원배열을 2차원처럼 쓰는것은 쉬우나 값을 넣을때 일일히 계산해줘야되는 문제가 있었다. 그렇다면 이를 이렇게 해결해보자.


void set_array_element(int* pArr, int x, int y, int xcount)
{
    pArr[(y * xcount) + x] = 0;
}
 결국에는 현재 2차원배열의 가로사이즈(2차원에서의 x축)을 기반으로 하여 실제적인 1차원의 원소위치를 알아낸다. 획기적인건 아니고 많이 쓰는 방법이지만 아주 깔끔하고, add_data를 사용하는 사람 입장에서는 x,y축을 삽입하여 원하는 원소에 값을집어넣으므로 아주 좋은방법이다. 여기서 xcount를 모르는 사람은 없으리라고본다. 왜냐면 대부분 2차원배열은 맵등의 구조로서 쓰이는데 맵의 가로크기를 생각안하고 짜는 사람은 없기때문이다. 이는 값을 얻을때도 분명히 나타난다.


int get_array_element(int* pArr, int x, int y, int xcount)
{
    return pArr[(y * xcount) + x];
}
 이 함수만 쓴다면 값을 얻어오려는 사람은 2차원배열의 첨자를 기입하여 1차원배열의 값을 얻어온다. 조금 돌아가는 감이 있지만 생각해보면 꽤 깔끔한 방법이다. 그렇다면 이번엔 반대로 2차원을 1차원처럼 속여보자.


int arr[10][5];
여기에 2차원 배열이 있다. 역시 왜 쓰는지는 필요없고 이를 1차원배열처럼 써보자. 


arr[y][x] = 0;
 보통은 이렇게 해서 넣어준다. 아까 1차원배열을 2차원처럼 속일때도 결국엔 원소의 x위치와 y위치를 이용하여 실제원소위치 i를 만들어내었다. 그렇다면 이번엔 반대로 하면 되는 것이다. 원소의 위치 i를 받아서 x와 y를 생성해보자.

void set_array_element(int* arr[], int i)
{
    int x = i % xcount;
    int y = i / xcount;
    arr[x][y] = 0;
}
 깔끔하다. 무엇보다 이 방법이 좋은점은 현재 위치를 입력하면 내부적으로 x와 y를 만들어버렸다는 것이다. 위의 방법과 병합하면, 2차원원소와 1차원원소의 복사가 엄청 쉬워진다.


int arr1[50];
int arr2[10][5];

// 1차원기준으로 1차원과 2차원의 데이터를 서로 옮겨보자.
void swap_array_d1tod2(int* arr1, int* arr2[], int i, int xcount)
{
    int temp = arr1[i];
    arr1[i] = arr2[i/xcount][i%xcount];
    arr2[i/xcount][i%xcount] = temp;
}

// 2차원기준으로 1차원과 2차원의 데이터를 서로 옮겨보자.
void swap_array_d1tod2(int* arr1, int* arr2[], int x, int y, int xcount)
{
    int temp = arr1[(y * xcount) + x];
    arr1[(y * xcount) + x] = arr2[y][x];
    arr2[y][x] = temp;

 매우깔끔하다. 실제로 1차원과 2차원 각각에 반대되는 형태의 원소위치를 넣어도 내부적으로 현재 원소의 위치를 구해내어 결국에는 서로의 값을 바꾸는데 아무런 지장이 없어지는 것이다. 만일 이러한 기법들을 사용하지 않는다면 소스는 훨씬 지저분해지며, 쓸때없이 변수들을 마구잡이로 생성하는 등의 안좋은 우회방법을 사용하기 일쑤일 것이다.

이 기법들은 사실 평소엔 거의 쓸일이 없다. 하지만 2차원배열을 사용하는 텍스쳐나, 게임의 배경맵 등을 짤때는 알아두면 알아둘수록 도움되지 않겠는가? 이런게 다 노하우인 것이다. 별것아닌 팁을 주의깊게 읽어주어 고맙다는 말로서 오늘의 이 포스팅을 마치도록 하겠다.
[출처 : http://bufferoverflow.tistory.com/tag/포인터%20배열%20속도]


우선 배열의 경우에는

시작주소( 배열명칭) + sizeof( 데이타형 ) * 인덱스

와 같은 수식으로 
계산됩니다.


 
여기서   '+' 연산자의 앞부분은 일정하고  뒷부분에서  인덱스만 변하여

계산을 하는데  어셈블리어나 기계어로 번역될 때의 자세한 상태를 보지 아니해도

+ 연산자를 전후로 대략 두개의 피연산자을 동반합니다.

ADD operand1 operand2  이고

operand2는 다시 곱셈에서 얻어지므로 대략  

MUL operand2 operand3  와 같습니다



그리고 포인터에 의한 주소연산에서는  
시작시에  pa를 시작주소를 설정하고나서

이 하나의 피연산자에 대하여만 연산을 하게됩니다.

 

그래서 

INC operand1

기계어로 번역될 때 명령코드도 간단하게되므로 메모리도 절약되지만

덧셈의 연산에 비하여 속도가 차이가 납니다.

굳이 어려운 포인터를 사용하는 이유가 있다고 봅니다.

 

좀더 자세히 설명을 한다면

arr[i] = ?  와 같은 배열문장은 배열의 i 인덱스 요소에 ?라는 값을 대입하라는 

의미입니다. 

값을 대입하기 위하여는 우선 그 주소로 이동하기 위하여 주소연산을 하게됩니다.

 

arr[i] = ? 에서 배열의 '[i]'연산자는  *( arr + i ) = ? 와 같은 표현식으로

간주되는 데  (arr + i )는 arr라는 배열의 이름( 상수의 주소값 )에 

정수를 더하여 그 주소를 계산하라는 의미이고 

'*' 연산자는  arr가 data라는 구조체형의 포인터이므로 그 주소에서 data라는 구조체형의 크기 공간에 있는 내용물을 ? 라는 값으로 채워라는 뜻입니다.

 

여기서 포인터 연산의 정수 덧셈은 arr라는 주소값에 sizeof( data ) * i의

바이트 수를  더하게 됩니다.

매번 i라는 배열요소의 인덱스가 변할 때 마다 내부적으로는

( 시작주소 + 데이타형의 크기 * 인덱스 )라는 주소계산을 합니다.

 

반면에 포인터를 사용하는 경우에는  

*pa = ?; 

pa++; 에서

 

*pa = ? 는 현재의 pa라는 주소에 data형의  크기공간안의 내용을 ? 라는 값으로

채워라는 의미입니다.

pa++; 는 다음  요소에 접근하기 위한 주소연산을 하는 문장입니다.

 

pa++ 연산은 배열의 '[]'연산자를 사용하는 경우와는 달리  

pa + sizeof( data );  와 같은 계산으로 주소값을 계산하게됩니다.

 

즉 현재의 요소가 위치하는 pa라는  주소에  다음 주소로 옮기기 위하여

구조체형의 크기만큼의 바이트수를 더하여 얻는 주소값으로 계산됩니다..

 

기계코드로 번역될 때   sizeof( data ) * i  와 같은 곱셈연산은 많은 시간을

소모하는 것으로 알려져있습니다.

그래서 배열을 사용하는 것은 프로그래머의 입장에서는 사용하기 편하고 쉽게

인식하지만 기계코드를 전환될 때는 후자의 포인터를 사용할 때 만큼의

속도를 얻지 못합니다.

 

물론 소수의 배열요소들에 접근하여 작업하는 경우야 눈에 띄는 

차이를 느끼기 어렵겠지만  방대한 DB에서 어떤 데이타를 검색한다던지

할 때를 생각해보시면 짐작할 수  있을 것입니다.

 

반면에 포인터는 학습하는데 대체로 많은 수고를 요구하거나 디버깅을 할 때

쉽지 아니하지만. 배열을 사용할 때보다도 실행속도에서는 빠른다는 장점이 있으므로

각각의 장단점이 있습니다.

    

보통 포인터 식이라고 할때   arr[i]를   *(arr + i )를 고치는 것을 포인터식이라고

말하기도 하는 데  이것은 내부적으로 는 같은 코드를 생성하므로 같은 표현식으로

보아야하고 순수한 포인터 연산에 의한 코드 작성은 포인터의 단항연산자를 사용하여
 
배열의 요소로 이동하면서 필요한 연산을 

수행하는 경우라고 봅니다.

[출처 : http://bufferoverflow.tistory.com/entry/2차원-배열이-더블포인터인가]


int a[4];

in *p = a;

일때 a[0] == p[0], a[1] == p[1] 이라고 하는 것을 보고

 

in a[3][4]; 일 때

int **p =a;

이고 a[0][0] == p[0][0], a[1][2]== p[1][2]로 알고 계시는 분들이 있으십니다.

혹은 왜 이게 안되는지 궁급해 하십니다.

 

그래서 이런 글을 올립니다. 참고하세요

 

배열의 등가포인터 의미와 생성 그리고 활용에 대하여 다룹니다.

 

자 이런 배열이 있습니다

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

 

이때 배열의 이름 a는 배열의 정의상 배열의 첫요소의 주소입니다.

 

그러면 이는 배열의 첫요소 즉, a[0][0]의 주소 즉, &a[0][0] 일까요?

아니면 &a[0] 일까요? 값만 놓고 보면 둘 다 같은 값이 나옵니다.

그러면 의미상 정확하게 맞는 표현은 무엇일까요?

 

int a[3][4] 배열은 요소 12개 짜리 1차원 배열이 아닙니다.

메모리에는 12개의 숫자가 연속해서 존재하기는 하나 이를

편의를 위하여 2차원 배열처럼 생각하는 거지요

 

고로 int a[3][4]는 요소수 3개 짜리 배열입니다. 즉, 3칸짜리 배열이지요

 

그런데 이 배열은 각 칸칸마다 뭐가 들어 있냐면 정수가 있는것이 아니라

int [4] 즉, int 4개짜리 배열이 들어있는 겁니다.

 

따라서 이 배열의 요소수는 3개이고 이 배열의 첫요소는 a[0] 가 됩니다.

고로 배열명 a == &a[0]라고 해야합니다.

 

그러면 어차피 값은 &a[0][0]나 &a[0]이나 같은데 왜 꼭 &a[0]가 맞다고 할까요?

자 다음을 보시지요

 

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

int *p = a;

int (*q)[4] = a;

int **r = a;

 

세개의 포인터에 배열명 a를 담았습니다. 어느것이 옳을까요?

여기서 옳다는 것은 단지 대입이 되느냐 아니냐가 아니라

컴파일리 에러, 경고 없는 경우입니다.

 

사실 대부분 컴파일러는 int *p = a; 는 경고를 내거나 엄격한 컴파일러는

에러를 냅니다. 고로 에러, 경고를 막으려면 casting을 써서

int *p = (int *)a; 라고 해야 합니다.

 

그러나 두번째 int (*q)[4] = a; 는 에러, 경고 나지 않습니다.

즉, 두개는 완벽히 일치하는 데이터 타입이라는 뜻이지요...

 

세번째 2중 포인터는 int **r = p;는 역시 첫번째와 마찬가지로

타입이 다른 주소가 대입되므로 에러 혹은 경고가 납니다.

정상이려면 int **r = (int **)a; 하셔야 합니다.

 

자 왜 두번째 포인터만 정상일까요?

위에서 얘기 했듯이 int a[3][4] 일때 배열명 a는 &a[0]의 주소인데

a[0]은 a배열의 첫요소 즉, 3칸 짜리 배열에는 int [4]

즉, int 4개 짜리 배열이 들어 있으므로

a[0]는 그 속의 int [4] 배열의 이름이 됩니다.

 

고로 &a[0]는 바꿔 말하면 int [4] 배열의 주소이므로

타입은 int (*)[4] 라고 쓸수 있습니다.

그런데 a == &a[0] 이니 결국 배열 이름은 int (*)[4] 와 같은 타입입니다.

 

그러므로 이러한 주소를 받는 포인터는 int (*q)[4] 로 선언하게되고

int (*q)[4] = a; 가 성립하는 것입니다.

 

그럼 다시 다음에서 봅시다.

 

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

int *p = (int *)a;

int (*q)[4] = a;

int **r = (int **)a;

 

이렇게 강제로라도 a를 대입했다면 이 포인터들로

배열에 있는 a[1][2] 즉,  7을 억세스 하려면 어떻게 할까요?

 

int *p는 현재 값이 정수 즉, 1의 주소를 갖으므로

그리고 정수(int)의 포인터 이므로

printf("%d\n", *(p+6)); 하거나 혹은 [ ] 연산자를 이용하여

printf("%d\n", p[6]); 으로 해야 합니다.

 

그러나 두번째는 int (*q)[4]의 현재값이 {1,2;3,4} 배열의 주소이므로

a[1][2]와 동일한 표현은 q[1][2] 가 됩니다.

이것이 왜 이렇게 되는지는 다차원 배열을 공부하시면 됩니다.

 

즉,

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

에서 a[1][2] 와 같은 값을 억세스 하기 위하여

 

int *p = (int *)a; 로 하면 p[6]  혹은 *(p+6)

int (*q)[4] = a; 로 하면 q[1][2] 로 됩니다.

두 방법중 어떤것이 이해가 쉽습니까?

 

이와같이 배열의 이름을 대입받을 때 에러, 경고, 캐스팅 없이 대입 가능한

포인터를 배열의 등가포인터라고 하고 등가포인터가 배열의 이름을 받으면

그때부터는 포인터가 배열과 표현식이 동일하게 사용가능합니다.

 

물론 그렇다고 이것이 완전히 동일해 지는것은 아닙니다.

배열은 배열이고 포인터는 포인터입니다. 단지 배열명을 갖는 포인터이지요

 

단, 억세스는 간접연산(포인터로 인하여)이므로 억세스 속도는 배열보다 느립니다.

 

그럼 1차원 함수에 넘기는 경우를 보지요...

 

void main(void)

{

   int a[4] = {1,2,3,4};

   func(a);

}

라고 할때 func 함수는 넘겨받은 배열의 이름을 이용하여

배열내의 a[2]인 3을 인쇄한다고 합시다.

 

그럼 어떻게 하나요?

void func(int *p)

{

    printf("%d\n", p[2]);

}

라고 할것입니다.

 

 이 상황을 잘보면

 

(1) main에서 func를 호출하면서 넘긴것은 배열의 이름 a입니다.

1차원 배열이므로 이 이름은 첫요소의 주소 즉, &a[0]  이고 a[0]는 정수 1이므로

int * 타입입니다.

 

(2) 고로 func에서는 이런 배열의 이름과 등가인 int *p로 받는것입니다.

즉, int *p = a; 가 가능하므로 int *로 한것이지요

고로 p는 표현식에서 a 배열과 동일하므로 a[1] ==p[1]이 됩니다.

 

즉, 배열은 전체를 넘기지 못하고 그 첫요소의 주소(배열명)를 넘기므로

 

이를 등가포인터로 받고 활용은 마치 배열인듯이 하는 겁니다.

 

이것이 등가포인터의 활용이지요...

 

그럼 2차원 배열을 넘기면 어떻게 되나요?

 

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

func(a);

에서 a[1][2] 와 값을 인쇄하는 함수는 어떻게 만드나요?

 

(1) int func(int *p)

(2) int func(int (*p)[4]);

(3) int func(**p); 어느것이 옳을까요?

 

만약 1처럼 하면?

 

func(a); 하면 경고가 발생하므로

억지로 캐스팅 하여 func((int *)a); 로 해야 합니다.

그리고 함수는 이렇게 만들지요

 

int func(int *p)

{

    printf("%d\n", p[6]);

    또는

    printf("%d\n", *(p+6));

}

 

그런데 2번처럼 하면 아까 얘기했듯이 int (*p)[4]는 배열명 a 와 등가이므로

int (*p)[4] = a; 가 가능합니다.

 

고로 활용이 마치 a 배열처럼 되므로 다음과 같이 됩니다.

 

int func(int (*p)[4])

{

    printf("%d\n", p[1][2]);

}

 

3번 처럼 단순히 2중포인터라고 생각한다면?

이거 골치아퍼집니다. 아마 세가지 방법에서 제일 골치아프겠네요

한번 직접 해보세요...

 

이때 p[1][2]는 절대 배열이 아닙니다. p는 엄연한 포인터입니다.

그럼 어떤것이 쉽고 정상적인 방법일까요?

 

배열을 넘기고(배열명을 넘기는 거지만) 이를 등가 포인터로 받고

그리고 이를 활용할때는 마치 원래의 배열처럼 하는것 이게 등가포인터를 활용한 겁니다.

 

다음은 2차원 배열을 넘기고  배열요소의 전체 합을 구하는 함수를 설계합니다.

역시 등가 포인터를 활용합니다.

 

결국

 

void main(void)

{

    int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

    printf(""%d\n", sum(a));

}

 

int sum(int (*p)[4])

{

    int a = 0, i, j;

    for(i=0; i<3; i++)

   {

       for(j=0; j<3; j++)

      {

           a += p[i][j];

      }      

   }

  return a;

}

 

다시한번 말씀드리지만

위와 같이 배열을 다른 함수로 넘기거나 다른함수에서 리턴하거나

할때 배열명을 넘기고 받기는 포인터(등가포인터)로 받고

활용은 넘겨준 배열과 동일하게 하는것이 좋습니다.

이런 포인터를 등가포인터라고 합니다.

 

결국 등가포인터를 이용하면 배열, 함수를 함수로 넘기거나 함수 리턴으로 줄 때

매우 쉬워 집니다.

 

참고로 다음은 등가적인 포인터의 예를 보입니다.

 

(1) int a[4]  ==> int *p

(2) int *a[4] ==> int **p

(3) int a[3][4] ==> int (*p)[4]

(4) int a [2][3][4] ==> int (*p)[3][4]

(5) int *(*a[3])(int) ==> int *(**p)(int)

[출처 : http://blog.daum.net/jchern/13756798]


블로그에서 strncpy와 memcpy 중 memcpy가 더 빠르다고 본 기억이 있어서 확인차원에서 구글 codesearch를 빌려 확인

 

 

-------------------------------------------------------------------

                                     STRNCPY

-------------------------------------------------------------------

char *
strncpy(char *dst, const char *src, size_t n)
{
        if (n != 0) {
                char *d = dst;
                const char *s = src;

                do {
                        if ((*d++ = *s++) == 0) {
                                /* NUL pad the remaining n-1 bytes */
                                while (--n != 0)
                                        *d++ = 0;
                                break;
                        }
                } while (--n != 0);
        }
        return (dst);
}
 -------------------------------------------------------------------

strcpy는 assign 연산후에 널인지를 체크하는 비교 연산이 한번더 수행되고

널체크이후 타켓에 남은 size n만큼 0으로 패딩한다.

 

 

 

-------------------------------------------------------------------

                                     MEMCPY

-------------------------------------------------------------------

void *memcpy(void *_dst, const void *_src, unsigned len)
{
        unsigned char *dst = _dst;
        const unsigned char *src = _src;
        while(len-- > 0) {
                *dst++ = *src++;
        }
        return _dst;
}
 -------------------------------------------------------------------

memcpy는 심플하게 len만큼 복사하고 끝...

 

 

정확히 size를 알고 있다면 strncpy보다는 memcpy가 속도상 약간의 이득이 있는것으로 보임..

 

+ Recent posts