안녕하십니까 삽잡이입니다.
오늘도 과감하게 시작합니다.
어제 배운 포인터에 대해 잠시 생각해보고 본론으로 들어가겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* 안보실 분들은 Jumping 하셔도 됩니다. 우선 모든 포인터의 기준은 IDE 환경에서 6 bit OS 설정을 해놓지 않는 한 32 bit 크기를 사용합니다. 즉, 포인터의 크기는 4 byte라는 것이죠. 모든 프로그램에는 데이터의 값과 위치 둘중 하나를 변수에 담을 수 있다고 했습니다. 아무튼... 데이터 타입을 보도록 해봅시다. int 정수형 변수 int* 1차 포인터 int** 2차 포인터 (Pointer to Pointer) 셋은 똑같은 int형 변수라고 할 수 있을까요? 정답은 No 입니다. int형은 4 byte 크기의 정수를 담는 데이터 타입입니다. int*와 같은 경우 int형 변수의 주소값을 담는 데이터 타입입니다. int**는 그렇다면 아시겠죠? */ | cs |
오늘은 바로 문자열에 대해서 배워보도록 하겠습니다.
여러분이 일반적으로 생각하는 문자열은 어떤가요?
문자열이라고 하니까 문자들의 집합 같은데요?
아무튼...
C언어에서는 큰 따옴표("")로 문자열을 표현합니다. (참고로, 작은 따옴표('')는 문자)
C/C++에서는 기본형식으로 문자열 형식이 존재하지 않습니다...
그래서 문자열을 담을 곳이 없습니다...
하지만 문자열을 담을 형식이 필요한데... 문자열 정말 중요한데...
C언어에서 배우는 종류중에 문자열 처리가 가장 어려운 것중에 하나입니다. (물론 다 어렵죠...)
왜냐, 문자열 전용 형식이 존재하지 않기 때문이죠.
하지만, 방법은 있습니다...
프로그램에서 문자열을 다루는 방식 즉, "ABC"라는 문자열을 다루고자 할때!
방법은 두가지가 있습니다.
첫번째, 어떤 문자들을 연속적으로 만들어놓고 끝에 '내가 문자열이다!'라고 표시하기.
두번째, 문자의 숫자를 세고 저장하는 방식
이건 예를 들자면 AAABC 를 3'A''B''C' 이렇게 표현한다고 하는 것이죠.
아무튼... C언어에서는 첫번째 방식을 사용합니다.
우리가 어떻게 어떤 문자를 사용해도
C언어에서는 메모리 상에 보관하고 끝이라는 표시도 보관하고 있습니다.
여러분 못보게... 아하하...
어떻게 하냐면 말이죠... 우선 문자열은 메모리상에 '연속하게'보관됩니다. (배열과 비슷하군요)
그다음에... 중요합니다... C/C++에서 끝을 표시하는 문자!
바로 '\0'(널문자)를 우리 몰래 보관합니다.
(참고로 '\0'은 아스키코드로 0입니다.)
아... 그렇다면 우리가 문자열을 써도 항상 뒤에 '\0'자가 붙어 있던 거구나...
그렇다면 정리해서 말씀드릴 수 있겠습니다.
C/C++에서 정의하는 문자열은 "끝에 널문자를 표현한 연속한 문자들의 집합" 이라고요.
왜 처음에 포인터를 언급했을까요?
문자열을 공부하기 위해서는
포인터를 좀 더 깊숙하게 공부해야할 필요성이 있어 상기시키려는 저의 엄청난 고도의 전략이었습니다.
아하하하하하하하하 (죄송합니다...)
아무튼 정신잡고...
'문자열을 연속적으로 보관한다' + '끝에 \0(널문자)'
이 두가지로 문자열의 길이를 파악할 수 있습니다.
정해진거 없는 문자열을 어디에 저장하느냐... 문자열이라 해봤자 다 정수인데... 으...
C언어의 규칙으로 파악할 수 있습니다.
왜냐 위의 두가지 단서로 파악할 수 있다는 것이죠.
따라서, 문자열은 시작주소만 알면 모든 주소를 찾아낼 수 있습니다.
다른 말로, 문자열의 시작주소만 알면 모든 문자열을 찾아낼 수 있다 이겁니다.
포인터에 대해 조금 더 알게된다면 문자열을 쉽게 배울 수 있습니다.
왜냐 문자열과 포인터는 베스트 프렌드이기 때문이죠...
음... 포인터가 문자열을 위해 존재하는 것은 아니지만,
문자열을 다루고 지원하는데에 있어서 사용할 수 있습니다.
어떻게? 왜?
바로, 같은 데이터 형식의 연속한 메모리 집합인 배열을 사용할때
포인터와 땔래야 땔 수 없는 관계인것 처럼,
배열과 비슷하게 문자열도 연속된 데이터의 묶음이기 때문이죠!
배열과 문자열이 비슷하니 포인터는 둘과 모두 친하게 지낼 수 있을 거 같습니다.
그래서 더더욱 문자열을 이해하기 위해서는 포인터... 그리고 배열의 이해가
절실하게 필요합니다!
C/C++은 예전에 호랑이 담배피던시절...은 오바고... 예전에 만들어졌기때문에
요즘 생긴 언어들과 달리 문자열 형식이 없다고 지금 몇번이나 말하고 있는지...
아하하... 이놈의 했던말 또하는...
아무튼, 배열과 포인터로 문자열의 기능을 대체할 수 있습니다.
포인터 변수는 주소를 보관한다고 했었습니다. 우선, 주소의 연산을 알아야합니다.
문제를 내보도록 하죠...
1 2 3 4 | int n; int* p = &n; //주소는 1000이라고 가정합시다. | cs |
그렇다면 p의 값은 1000입니다. 여기서 문제, p+1의 값은?!
정답은 컴파일에 정의가 되지 않았다면 오류입니다! 이지만
정의가 되어있다는 점~
여기서 아셔야 하는 점은, '주소' + '정수' 는 잘들으세요...
'주소에서 해당 주소를 가지고 있는 변수의 데이터 형식의 크기만큼 증가한다' 입니다.
...
그러니까 위의 문제의 실제 답을 말씀 드리자면,
p+1은 int형 타입의 크기인 4 byte가 한번 증가된 1004라는 것이죠.
즉, '정수를 더하면 그 해당 자료형만큼 건너 뛴다' 라고 이해하시면 됩니다.
일반화 시켜서 말씀드리자면,
N이라는 형식의 주소는 N*형 형식이되는데요 (int형 형식의 주소는 int* 형)
플러스하면 N만큼 건너 뛴다는 것이죠. 또 더하면 또 N만큼 건너 뛰고요...
말이 어렵네요... 이해가 안가신다면... 그냥 예시를 이해해주세요...
어쨋든... 제일 중요한 것은 말입니다!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> void main() { int n =10; int* p = &n; printf("%p %p\n", &n-1, p-1); //n에서 데이터 형식의 크기만큼 한번 감소 printf("%p %p\n", &n, p); // n의 주소 printf("%p %p\n", &n+1, p+1); //n에서 데이터 형식의 크기만큼 한번 증가 printf("%p %p\n", &n+2, p+2); //n에서 데이터 형식의 크기만큼 두번 증가 } | cs |
위의 코드와 같이
정수의 증가/감소에 따라 데이터 형식의 갯수 만큼 증가/감소를 한다는 것입니다.
왜 이런 설명을 했냐면 말입니다...
문자열, 배열과 같이 메모리가 연속적으로 만들어져 있을 때는
위와 같은 것들이 반드시 필요하기 때문입니다.
지금은 포인터만 말씀드렸는데 말입니다, 배열도 예외 없습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> void main() { int arr[4] = {10, 20, 30, 40}; //int형 메모리 4개와 같다. printf("%d %p\n", arr[0], &arr[0]); printf("%d %p\n", arr[1], &arr[1]); //각각 4바이트씩 차이가 난다. printf("%d %p\n", arr[2], &arr[2]); printf("%d %p\n", arr[3], &arr[3]); } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> void main() { int arr[4] = {10, 20, 30, 40}; //int형 메모리 4개와 같다. printf("%d %p\n", arr[0], &arr[0]); printf("%d %p\n", arr[1], &arr[0]+1); //배열의 0번째 인덱스에서 정수 하나씩 증가된다. printf("%d %p\n", arr[2], &arr[0]+2); printf("%d %p\n", arr[3], &arr[0]+3); } | cs |
1 2 | int* p = &arr[0]; | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> void main() { int arr[5] = {10, 20, 30, 40, 50}; int* p = &arr[0]; printf("%d %p\n", arr[0], p); printf("%d %p\n", *(p+1), p+1); printf("%d %p\n", p[2], p+2); printf("%d %p\n", p[3], *(p+3); printf("%d %d\n", p[4], arr[4]); } | cs |
1 2 3 4 5 | int arr[4] = {10, 20, 30, 40}; int* p = arr; //배열의 이름은 배열의 시작주소! //int* p = &arr[0]; | cs |
이렇게 나타낼 수 있겠습니다.
악... 오늘 정말 중요한 것들이 너무너무도 많았습니다...
그만큼 문자열을 안다는 것은
배열과 포인터의 이해도 중요하다는 것입니다.
조금만 더 힘내봅시다.
저한테 하는 말이며 여러분한테도 하는말입니다.
힘있는 자들이여 힘좀 냅시다!
힘없으신 분들은 좀 쉬세요....
뭔 소리야 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
'삽질의 현장 > - C' 카테고리의 다른 글
곱씹어보자 C!_#012_ 삽잡이의 두서없이 막말하는 함수(1) (0) | 2015.06.25 |
---|---|
곱씹어보자 C!_#011_ 삽잡이의 두서없이 막말하는 문자열(2) (0) | 2015.06.24 |
곱씹어보자 C!_#009_ 삽잡이의 두서없이 막말하는 아스키코드 (0) | 2015.06.24 |
곱씹어보자 C!_#008_ 삽잡이의 두서없이 막말하는 C언어의 꽃! 포인터 (0) | 2015.06.23 |
곱씹어보자 C!_#007_ 삽잡이의 두서없이 막말하는 제어문 반복문 (0) | 2015.06.23 |