포인터+배열
포인터과 배열에는 관계가 있다
int* ptr;
int num;
ptr = #
이 구문을 보면 ptr은 num을 가리키고 있다.
int arr[3];
//ptr => arr[0]을 가리키고 있는 것이다.
가리킨다는 점에서 두개는 전부다 같다.
*ptr *arr은 가능하다. 변수와 상수의 차이점이라는 것이 존재할뿐.
즉 배열의 이름은 (상수형태)포인터를 말하는 것이다.
int main(void)
{
int arr[3]={0, 1, 2};
printf("배열의 이름: %p \n", arr);
printf("첫 번째 요소: %p \n", &arr[0]);
printf("두 번째 요소: %p \n", &arr[1]);
printf("세 번째 요소: %p \n", &arr[2]);
return 0;
// arr=&arr[i]; //이 문장을 넣으면 컴파일 에러가 나옴: 상수라서 오류
}
이 예시를 보면 arr[3]라는 배열 0,1,2를 초기화 해준 후
각 출력값을 뽑아내면 배열의이름: 00122FF50
첫번째: 00122FF50 , 두번째:0012FF54…이렇게 나오는데
이 것을 보면 배열의 이름은 첫번째 요소의 주소값과 동일하다.
arr은 상수기 때문에 대입연산이 불가능하다. 가리키는 대상을 바꿀수 없다는 것이다.
비교 | 포인터변수 | 배열의이름 |
---|---|---|
이름존재 | 존재함 | 존재함 |
무엇을 나타내고 저장 | 메모리의 주소 값 | 메모리의 주소 값 |
주소값의 변경 | 가능하다 | 불가능하다. |
- 배열요소간 주소 값의 크기는 4byte로 요소가 서로 붙어있다는 것을 알 수 있다.
double arr[1]=> double *상수
다.
1차원 배열의 이름의 포인터형 배열이름 대상 *연산
int arr1[5]; //arr1은 int형 포인터 실수
double arr2[7]; //arr2는 double형 포인터 상수
즉, 1차원 배열이름의 포인터형은 배열의 이름이 가리키는 대상을 기준으로 결정된다.
int main(void)
{
int arr1[3]={1, 2, 3};
double arr2[3]={1.1, 2.2, 3.3};
printf("%d %g \n", *arr1, *arr2); //int %d / double %g
*arr1 += 100; //4byte
*arr2 += 120.5; //8byte
printf("%d %g \n", arr1[0], arr2[0]);
return 0;
}
포인터를 통해서 arr1의 첫번째 1+100을 해서 = 101
포인터를 통해서 arr2의 첫번째 1.1 + 120.5을 해서 = 120.6
+)추가로 생각해보기
int main(void)
{
int arr[3] = { 1,2,3 };
arr[0] += 5;
arr[1] += 7;
arr[2] += 9; //포인터를 대상으로 구성한 셈
...
}
이렇게 하면 될까?
된다. arr은 int형 포인터 변수나 배열의 모든요소를 접근한 것으로
실제로 이렇게 쓸 수 있다. 포인터 변수 ptr을 대상으로 ptr[0], ptr[1]이 가능하다.
즉, 포인터를 배열의 이름처럼 사용할 수도 있다.
int main(void)
{
int arr[3]={15, 25, 35};
int * ptr=&arr[0]; // int * ptr=arr; 과 동일한 문장 //배열 접근가능
printf("%d %d \n", ptr[0], arr[0]); //ptr[0]...도 가능하다.
printf("%d %d \n", ptr[1], arr[1]);
printf("%d %d \n", ptr[2], arr[2]);
printf("%d %d \n", *ptr, *arr);
return 0;
}
이 문장을 보면 포인터 수 변수를 배열의 이름처럼 사용하는 경우는 없지만 가능은 하다..!
포인터에 대한 정확한 지식을 넣고 가자.
포인터 연산
포인터 => *연산과 []연산이 있음.
위에 포인터가 *와 []이 있듯이!
int* P; = 0x01=[1]
P=+1
->0x01+4=0x05
포인터 에서는 0x01을 가리키는 곳에서 +1을 하면 0x02가 아니라
0x05가 된다.
왜? int형이니까 +4byte라서
int arr[3];
int*ptr=&arr; //둘다 타입이 일치되서 대입이 가능함. ptr은 변수고 arr는 상수니까 대입가능.
위 int *ptr = &arr을 거치면
arr[0] => 10 ~
arr[2] => 20 ~
arr[3] => 30 ~
*ptr=10
이라는 것을 넣고 이후 ptr++
를 하면? 4씩 증가
*ptr=20
을 넣어주면 이후 20값 저장 후 ptr++
을 하면? 4씩 증가…
이 상태에서 ptr -=2;를 하면?
2를 감소시키면 sizeof(int)*2만큼 값이 감소됨. (-8)
ptr은 다시 첫번째 요소를 가리키게 됨.
*ptr+=30
을 하면 값이 40이 되면서 arr[n]이 바뀜.
포인터를 대상으로 하는 증가 및 감소 연산
int main(void)
{
int* ptr1 = ...;
int* ptr2 = ...;
ptr1++;
ptr1 += 3;
ptr2 -= 5;
ptr2 = ptr1 + 2;
...
}
연산의 결과를 확인한다면?
int main(void)
{
int * ptr1=0x0010;
double * ptr2=0x0010;
printf("%p %p \n", ptr1+1, ptr1+2);
printf("%p %p \n", ptr2+1, ptr2+2);
//적절하지는 않음
printf("%p %p \n", ptr1, ptr2);
ptr1++; //4증가
ptr2++; //8증가
printf("%p %p \n", ptr1, ptr2);
return 0;
}
이를 위해서 이 예제를 실행시켜보자!
00000014 00000018 //+4 / +8
00000018 00000020 //+8 / +16 // 16진수라서 0x0010+16 = 26이 아닌 0x0020
00000010 00000010 //기본 값 부르기
00000014 00000018 // 증가 후 기본 값 부름
이것을 보면 n x sizeoff(type)만큼 크기 증가/감소 된다는 사실을 알 수 있다.
위에는 int형이니까 값이 1증가할 때 마다 4씩 증가한다.
아래는 double형이니까 값이 1증가할 때 마다 8씩 증가한다.
연산 특성을 이용한 배열접근
int main(void)
{
int arr[3]={11, 22, 33};
int * ptr=arr; // int * ptr=&arr[0]; 과 같은 문장
printf("%d %d %d \n", *ptr, *(ptr+1), *(ptr+2));
printf("%d ", *ptr); ptr++; //후열이라 출력 되고
printf("%d ", *ptr); ptr++;
printf("%d ", *ptr); ptr--;
printf("%d ", *ptr); ptr--;
printf("%d ", *ptr); printf("\n");
return 0;
}
arr[3]배열 값이 11,22,33을 포인터로 지정
각 배열 값을 순서대로 출력한다. (단 ptr++로 후열에 있으니 출력되고 값이 +1되는 것.)
11,22,33
11,22,33,22,11
이 된다.
이 예제를 근거로 연산의 차이를 알 수 있다.
*(++ptr)= 20; //저장된 값 자체를 변경
*(ptr+1)=20; // 값은 변경하지 않음.
int main(void)
{
int arr[3] = { 11,22,33 };
int* ptr = arr;
printf("%d %d %d \n", *ptr, *(ptr + 1), *(ptr + 2));
...
}
즉 arr[i]=*(arr+i)
다.
여기서 ptr은 arr값과 출력 결과가 동일하다.
배열의 이름과 포인터 변수는 상수 변수차이만 있을 뿐 동일하기 때문
문제
문제1: 포인터를 이용한 배열 접근
길이가 5 int형 배열 arr을 선언하고 1,2,3,4,5로 초기화 후
배열의 첫번째 요소를 가리키는 포인터 변수 ptr선언
ptr저장된 값을 증가시키는 연산을 기반으로 배열요소접근
값을 2증가시키고 정상적 증가이루어졌는지 확인하라.
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = arr;
int i;
printf("기본 값은 : %d 입니다 \n", ptr[0]);
for (i = 0; i < 5; i++) //0부터 시작해서 5개까지 1증가
{
*ptr += 2; //true면 2누적
ptr++; //ptr 값 상승 //다음배열로
}
for (i = 0; i < 5; i++) //출력 반복문
printf("%d \n", arr[i]);
return 0;
}
반복문을 통해서 각 배열의 순서대로 접근하면서 2을 누적시킨다.
그리고 출력반복문을 통해서 각 배열을 순차적으로 출력한다.
문제2:문제1에서 ptr 값을 변경시켜서 배열접근하라.
이번에는 값 변경 불가능 하고
덧셈연산하여 값 2씩 증가하는 코드
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = arr;
int i;
printf("기본 값은 : %d 입니다 \n", ptr[0]);
for (i = 0; i < 5; i++) //0부터 시작해서 5개까지 1증가
{
*(ptr+i) +=2 ; // 축약 arr[i] = *(arr+i)
}
for (i = 0; i < 5; i++) //출력 반복문
printf("%d \n", arr[i]);
return 0;
}
기본적인 코드는 문제1의 코드를 따라간다.
포인터변수 ptr의 값을 변경시키지 않고 ptr자체에 덧셈연산을 해야되기에
*ptr +=2;
를 *(ptr+i)+=2
로 변경해준다.
문제3:
길이 5 int형 배열 arr 선언
이를 1,2,3,4,5 초기화
마지막 요소 가리키는 포인터변수 ptr선언
ptr 값을 감소시키는 형태 연산
모든 배열 요소 접근하여 배열에 정수 더하기
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = &arr[4];
int total = 0, i; //포인터 0 ,null 초기화
for (i = 0; i < 5; i++)
total += *(ptr--); //포인터의 감소형 ptr[4] [3] ..
printf("%d", total); //역순으로 5+4+3+2+1로 더해진것
return 0;
}
주의 할점은 마지막 요소를 가리키는 포인터변수 ptr을 선언해야되니까 직접적으로 지시해준다.
&arr[4]
를 지정시켜 마지막 요소를 가리킨다.
그후 ptr 값을 감소시키는 형태의 연산을 진행하니 누적변수를 통해서 포인터 감소형을 해준다.
즉 ptr[4]..[3]..[2]. ..그후 역순으로 합친 값을 출력한다.
문제4:
길이가 6인 int형 arr을 선언
이를 1,2,3,4,5,6으로 초기화
배열 저장된 값 순서가 6,5,4,3,2,1이; 되도록 변경
배열의 앞과 뒤를 가리키는 포인터 변수 두개를 선언해서 활용
int main(void)
{
int arr[6] = { 1,2,3,4,5,6 };
int* sptr = &arr[0];
int* eptr = &arr[5];
int a, b ;
for (a = 0; a < 3; a++) //3번 이후는 역전이라 3개까지만
b = *sptr; //b에 1저장
*sptr = *eptr; // sptr에 6저장
*eptr = b;// eptr에 1저장
sptr += 1; //1누적증가
eptr -= 1; //1누적감소
for (a = 0; a < 6; a++)
printf("%d", arr[a]);
return 0;
}
아까처럼 반대로 뒤집기를 해야되고 위치 변경 후
각각 가리키는 곳이 반대인 포인터 변수 2개 선언 (Start ptr , end ptr)
시작지점을 sptr에 끝지점을 eptr에 지정해줌.
b에 sptr이 가리키는 1값 / sptr에 eptr 가리키는 값 6 / eptr에 b가 가리키는 값 1 을 넣음.
eptr과 sptr이 교환 됨.
그후 sptr에 1누적증가 / eptr에 1누적감소를 시킴.
1. 초기 = {1,2,3,4,5,6}
2. 첫 번째 반복 진행 = {6,2,3,4,5,1}
3. 두 번째 반복 진행 = {6,5,4,3,2,1}
4. 결과 = {6,5,4,3,2,1}
중요) *sptr에 *eptr을 했는데 왜 str+=1하면 1부터 증가인가?
왜냐하면 *sptr = *eptr
을 통해서 값을 ‘복사’된 것…
즉 값을 따온거지 그 자체의 값을 바꾼 것이 아님.
상수형태의 문자열을 가리키는 포인터
포인터는 1.변수 2.배열이 있다.
[문자열 가공]
char str[20] = "abcd"
? str="def" - 불가능
= def를 str이라는 상수에 저장해라 (상수라서 값 변환 불가)
[문자열 비가공 상수형]
char*str = "abcd"
의 경우는 가능하다.
why? []가 없는데? =>메모리 준비할 필요 x
abdc가 메모리 자동활당됨
위쪽 Str은 그 자체로 문자열 전체를 저장한다.
아래 Str은 메모리 상 자동으로 저장된 문자열 첫번째 문자 가르키기.
위는 계속해서 저장된 위치이다.
아래는 다른 위치로 변동이 가능하다는 점이 다르다.
char * str = "Yout Team"
str = "Our team"; //str이 가리키는 대상을 문자열 Out team 변경
//가능은 한데 되도록 작성을 지양하자.
변수형태의 문자열이라는 것이다.
이렇게 쓰면 헷갈리게 되니까 지양하는 것이다.
반대는 상수형태의 문자열
int main(void)
{
char str1[]="My String"; //변수형태
char * str2="Your String"; //상수형태
printf("%s %s \n", str1, str2);
str2="Our String"; // 가리키는 대상 변경
printf("%s %s \n", str1, str2);
str1[0]='X'; //성공
str2[0]='X'; //실패
printf("%s %s \n", str1, str2);
return 0;
}
변수형태와 상수형태에 대한 가리키는 대상 변경을 시도.
성공과 실패로 나뉜다.
이는 컴파일러마다 결과값이 다르긴한데 바꾸기는 가능하다.
하지만, 특정 컴파일러를 사용해야되는 코드작성은 올바르지 못하다.
어디서든 선언할 수 있는 상수형태의 문자열
char *str = "Const String"; (자동할당)
char * str = 0x1234; //이렇게 저장된다. -> 주소값
printf("show your string"); //함수 호출과정에서 선언되는 문자열
printf("0x1234"); //호출을 하면 주소값이다.
//printf함수는 문자열 통쨰로 받는 게 아니라 주소값 전달받음
Whoareyou("Hong"); //whoareyou함수 호출
void whoareyou(char * str) {....}
//실제로 전달되는 주소는 H의 주소값 이다.
포인터 변수로 이뤄진 배열 : 포인터배열
int arr[3]; -> int *arr[3]
이다.
포인터배열의 이해
int * arr1[20]; //길이 20 int형 포인터 배열 arr1
double * arr2[30];//길이 30 doulb형 포인터 배열 arr2
int main(void)
{
int num1=10, num2=20, num3=30;
int* arr[3]={&num1, &num2, &num3}; //5행 선언한 변수로 주소값 초기화
printf("%d \n", *arr[0]);
printf("%d \n", *arr[1]);
printf("%d \n", *arr[2]);
return 0;
}
이 구문을 통해서 *arr[0]을 통해서
각각 값이 10,20,30이 출력된다.
문자열을 저장하는 포인터배열
char*strArr[3];
길이가 3인 char형 포인터배열
int main(void)
{
char * strArr[3]={"Simple", "String", "Array"};
//char형 3개 [3]넣음 각각 Simple = 7byte / String= 7byte / Array=6byte
//메모리 simple -> 0x.. -> [0] -> 0x...
//char strArr={0x1004, 0x1048, 0x2102]
//strArr[0]->simple \0 strArr[1]->String \0 strArr[2] -> Array \0
printf("%s \n", strArr[0]);
printf("%s \n", strArr[1]);
printf("%s \n", strArr[2]);
return 0;
}
char형 이기 떄문에 글자수 +1(\0)을 계산한다. 7/7/6
그러면 메모리를 보면 simple이 0x123124이 되고 이를 [0]이고 이것이 또 0x..가된다.
즉 strArr={0x1104, 0x1048, 0x2102}가 된다.
각각 값은 입력한 값에 \0가 붙은 것으로 저장된다.
포인터배열에 대한것은 여기까지이다.
각 챕터별로 하면 올바른 분량이 나오는 것같다.
다음은 포인터와 함수에 대한 이해이다.