공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 


 포인터 

  1. 인터
    • 포인터 변수
    • 포인터 변수의 선언
      * 포인터 변수의 크기
    • 포인터 형(type)
    • 포인터 변수와 관련된 연산자
      1. & 연산자
      2. * 연산자
    • null 포인터
    • 포인터 대상의 const 선언
      * 상수를 가리키는 포인터인 경우
      * 포인터 자체가 상수인 경우

      * 포인터가 상수이면서 상수를 가리키는 경우
  2. 포인터와 배열
    • 포인터 연산
    • 문자열의 선언방식
    • 포인터 배열
      * 문자열 배열
  3. 포인터와 함수
    • 함수의 호출 방식
      * Call-by-value
      * Call-by-reference
    • 함수의 인자로 배열 전달
      * 인자로 배열 전달 시 배열의 크기
  4. 이중 포인터(더블 포인터)
    • 이중 포인터 선언과 사용
    • 포인터 변수 대상의 Call-by-reference
    • 포인터 배열과 포인터 배열의 포인터 type
    • 다중 포인터 변수
  5. 다차원 배열과 포인터
    • 2차원 배열의 이름과, 2차원 배열의 첫 번째 행
    • 2차원 배열의 증감 연산
    • 2차원 배열의 포인터
    • 함수 인자로 2차원 배열 전달
    • 다차원 배열에서 arr[i]와 *(arr + i)
    • 배열 포인터와 포인터 배열
    • main 함수의 인자
      * 인자의 형성 과정
      * char * argv[ ]
  6. 함수 포인터
    • 함수 포인터 변수 선언
  7. void 포인터

 


 

다차원 배열과 포인터

2차원 배열의 이름과, 2차원 배열의 첫 번째 행

#include <stdio.h>

int main(void)
{
	int arr2d[3][3];
	printf("%d \n", arr2d);
	printf("%d \n", arr2d[0]);
	printf("%d \n\n", &arr2d[0][0]);
	
	printf("%d \n", arr2d[1]);
	printf("%d \n\n", &arr2d[1][0]);
	
	printf("%d \n", arr2d[2]);
	printf("%d \n\n", &arr2d[2][0]);
	
	printf("sizeof(arr2d) : %d \n", sizeof(arr2d));
	printf("sizeof(arr2d[0]) : %d \n", sizeof(arr2d[0]));
	printf("sizeof(arr2d[1]) : %d \n", sizeof(arr2d[1]));
	printf("sizeof(arr2d[2]) : %d \n", sizeof(arr2d[2]));
			
	return 0;
}

  위의 예시로 설명하면 ``arr2d``와 ``arr2d[0]``의 출력 결과는 같지만, ``arr2d``는 첫 번째 요소를 가리키면서 배열 전체를 의미하는 반면, ``arr2d[0]``은 첫 번째 요소를 가리키되 1행만을 의미하기 때문에 서로 다른 것이다. ``sizeof`` 연산하면 결과값이 다른 것을 확인할 수 있다.

 

실행 결과

 

 

 

 

2차원 배열의 증감 연산

#include <stdio.h>

int main(void)
{
	int arr1[3][2];
	int arr2[2][3];
	
	printf("arr1 : %p \n", arr1);
	printf("arr1 + 1 : %p \n", arr1 + 1);
	printf("arr1 + 2 : %p \n", arr1 + 2);
	
	printf("\narr2 : %p \n", arr2);
	printf("arr2 + 1 : %p \n", arr2 + 1);
	
	return 0;
}

  2차원 배열 이름을 대상으로 증가 및 감소 연산을 할 경우, 연산 결과는 각 행의 첫 번째 요소의 주소 값이 된다. 이 때문에 2차원 배열을 이루는 요소의 자료형이 동일하더라도 배열의 열 길이가 다르면 포인터 연산의 결과는 달라진다.

 

실행 결과

``arr1``은 열 길이가 ``2``인 2차원 배열이므로 ``sizeof(int) x 2``인 ``8``만큼 주소 값이 증가하고, 

``arr2``은 열 길이가 ``3``인 2차원 배열이므로 ``sizeof(int) x 3``인 ``12(16진수로 C)``만큼 주소 값이 증가하는 것을 확인할 수 있다.

 

 

 

 

2차원 배열의 포인터

[자료형] (*포인터이름) [열길이];

// ex)
int arr[3][4];
int (*ptr)[4];	// 2차원 배열의 포인터

// ex) 다차 배열 포인터인 경우
int* (*ptr1)[5];	// int* 형 변수로 이루어진, 열 길이가 5인 2차원 배열
int*** (*ptr2)[5];	// int***형 변수로 이루어진, 열 길이가 5인 2차원 배열

  2차원 배열의 이름으로 증감 연산을 하면 각 행의 첫 번째 요소로 이동하므로 포인터도 똑같이 증감 연산을 했을 때 각 행의 첫 번째 요소로 이동해야 한다. 따라서 ``int (*ptr)[4]``는 "포인터 ``ptr``이 가리키는 대상의 자료형이 ``int``형 이고 포인터 연산 시 ``sizeof(int) x 4``의 크기만큼 값이 증가 및 감소하는 포인터 형"이라는 의미이다. 열의 개수에 따라 증감 값이 달라지는 것이기 때문에 자료형만 같고 크기가 다른 2차원 배열이라도 열 길이만 같다면 같은 포인터형을 사용할 수 있다. 또한 괄호를 생략하면 포인터 배열이 되므로 주의한다.

 

 

#include <stdio.h>

int main(void)
{	
	int arr1[2][2] = {
				{1, 2},
				{2, 3}
			 };
	int arr2[3][2] = {
				{1, 2},
				{3, 4},
				{5, 6}
			 };
	int arr3[4][2] = {
				{1, 2},
				{3, 4},
				{5, 6},
				{7, 8}
			 };
	
	int (*ptr)[2];
	
	ptr = arr1;	// 열 길이가 같이 때문에 같은 포인터형 사용가능
	printf("** show 2,2 arr1 **\n");
	for(int i = 0 ; i < 2 ; i++)
		printf("%d %d \n", ptr[i][0], ptr[i][1]);
	
	ptr = arr2;	// 열 길이가 같이 때문에 같은 포인터형 사용가능
	printf("** show 3,2 arr2 **\n");
	for(int i = 0 ; i < 3 ; i++)
		printf("%d %d \n", ptr[i][0], ptr[i][1]);
	
	ptr = arr3;	// 열 길이가 같이 때문에 같은 포인터형 사용가능
	printf("** show 4,2 arr3 **\n");
	for(int i = 0 ; i < 4 ; i++)
		printf("%d %d \n", ptr[i][0], ptr[i][1]);
	
		
	return 0;
}

2차원 배열 포인터를 사용하는 예시이다. 열 길이가 모두 같기 때문에 같은 포인터형을 사용할 수 있다.

 

실행 결과

 

 

 

 

함수 인자로 2차원 배열 전달

#include <stdio.h>

void ShowArr2DStyle(int (*arr)[4], int column)
{
	for(int i = 0 ; i < column ; i++)
	{
		for(int j = 0 ; j < 4 ; j++)
			printf("%d", arr[i][j]);
		printf("\n");
	}
	printf("\n");
}

int Sum2DArr(int arr[][4], int column)
{
	int sum = 0;
	for(int i = 0 ; i < column ; i++)
		for(int j = 0 ; j < 4 ; j++)
			sum += arr[i][j];
	
	return sum;
}

int main(void)
{	
	int arr1[2][4] = {1, 2, 3, 4, 5, 6, 7, 8};
	int arr2[3][4] = {1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5};
	
	ShowArr2DStyle(arr1, sizeof(arr1)/sizeof(arr1[0]));
	ShowArr2DStyle(arr2, sizeof(arr2)/sizeof(arr2[0]));
	
	printf("arr1의 합 : %d \n", Sum2DArr(arr1, sizeof(arr1) / sizeof(arr1[0])));
	printf("arr2의 합 : %d \n", Sum2DArr(arr2, sizeof(arr2) / sizeof(arr2[0])));
		
	return 0;
}

  1차원 배열을 인자로 넣었을 때 ``int * param`` 과 ``int param[ ]``이 같았던 것처럼 2차원 배열에서도 ``int (*parr1)[7]``을 ``int parr[ ][7]``로 사용 가능하다. (마찬가지로 매개 변수일 경우에만 서로 대체 가능)

 

 

 

 

다차원 배열에서 arr[i]와 *(arr + i)

arr[2][1] = 4;
(*(arr + 2))[1] = 4;	// arr[2]는 *(arr + 2)이므로 변환가능
*(arr[2] + 1) = 4;	// arr[2]를 A로 치환하면 A[1]이므로 *(A + 1)꼴로 변환가능
*(*(arr + 2) + 1) = 4;	// 3행에서 arr[2]를 *(arr + 2)로 변환

@@arr[i] = *(arr+ i)@@는 1차원 배열뿐만 아니라 다차원 배열에서도 성립한다.

그래서 위의 4개 문장은 전부 동일한 의미이다.

 

// 참고 **arr과 a[0][0]은 동일한 표현이다.
// 변환과정
**arr
**(arr + 0)	// *arr은 *(arr + 0)이므로 변환가능
*(*(arr + 0) + 0) // *(arr + 0)을 A로 치환하면 *A인데, *A는 *(A + 0)으로 변환가능
*(arr[0] + 0)	// *(arr + 0)은 arr[0]이므로 변환가능
arr[0][0]	// arr[0]을 A로 치환하면 *(A + 0)이므로 A[0]

같은 이유로 ``**[배열 이름]``은 ``[배열이름][0][0]``과 동일한 표현이다.

 

#include <stdio.h>

int main(void)
{	
	int a[3][2] = { {1,2}, {3, 4}, {5, 6}};
	
	printf("a[0] : %p \n", a[0]);
	printf("*(a + 0) : %p \n", *(a + 0));
	
	printf("a[1] : %p \n", a[1]);
	printf("*(a + 1) : %p \n", *(a + 1));
	
	printf("a[2] : %p \n", a[2]);
	printf("*(a + 2) : %p \n", *(a + 2));
	
	printf("%d %d \n", a[2][1], (*(a + 2))[1]);
	printf("%d %d \n", a[2][1], *(a[2] + 1));
	printf("%d %d \n", a[2][1], *(*(a + 2) + 1));
	
	return 0;		
}

앞에서 변환한 식이 성립하는지 확인해보는 예제이다.

 

실행 결과

 

 

 

 

배열 포인터와 포인터 배열

int *whoA[4];	// 포인터 배열
int (*whoB)[4];	// 배열 포인터

  소괄호( )의 유무에 따라 포인터 배열과 배열 포인터로 나뉘므로 주의한다. 위의 경우 ``whoA``는 ``int``형 포인터 변수로 이뤄진 ``int``형 포인터 배열이고 ``whoB``는 열 길이가 ``4``인 ``int``형 2차원 배열을 가리키는 용도의 포인터 변수이다.

 

#include <stdio.h>

int main(void)
{	
	int num1 = 10, num2 = 20, num3 = 30, num4 = 40;
	int arr2d[2][4] = {1, 2, 3, 4, 5, 6, 7, 8};

	int * whoA[4] = {&num1, &num2, &num3, &num4};	// 포인터 배열 
	int (*whoB)[4] = arr2d;	// 배열 포인터
	
	printf("%d %d %d %d %\n", *whoA[0], *whoA[1], *whoA[2], *whoA[3]);
	for(int i = 0 ; i < 2 ; i++){
		for(int j = 0 ; j < 4 ; j++){
			printf("%d ", whoB[i][j]);
		}
		printf("\n");
	} 
		
	return 0;
}

배열 포인터와 포인터 배열의 차이점을 보여주는 예시이다.

 

실행 결과

 

 

 

 

main함수의 인자

int main(void) {...}
int main(int argc, char * argv[]) {...}

  보통 ``main`` 함수를 첫 번째 줄처럼 선언하지만 두 번째 줄처럼 정의하는 것도 가능하다. 이렇게 하면 프로그램 실행 시 @@main@@함수로 전달할 인자를 열거할 수 있으며, @@main@@함수 역시 이러한 인자를 전달받을 수 있도록 제한된 형태의 매개변수 선언이 가능하다.

 

 

  ■ 인자의 형성 과정

#include <stdio.h>

int main(int argc, int * argv[])
{	
	int i = 0;
	printf("전달된 문자열의 수 : %d \n", argc);
	
	for(i = 0 ; i < argc ; i++)
		printf("%d번째 문자열 : %s \n", i + 1 , argv[i]);
	
	return 0;		
}

``main``함수에 인자를 넣는 예시이다. 

 

실행 결과
인자의 구성

  위와 같이 실행하면 main함수로 총 4개의 문자열 정보가 전달된다. 첫 번째 인자로는 문자열의 수가, 두 번째 인자로는 문자열 배열이 전달된다. 최종적으로 ``main(4, strArr)``의 형태로 ``main``함수가 호출된다.

  공백이 문자열을 나누는 기준이 되며(마찬가지로 문자열 배열(@@char *@@형 배열)이기 때문에 위의 그림처럼 각 문자열의 끝에 @@NULL(\0)@@문자가 들어가고, 배열의 마지막에도 @@NULL@@이 들어간다. "I Like You"와 같이 @@"@@로 묶으면 공백을 포함하는 문자열을 인자로 전달할 수 있다. 위의 예시에서 첫 번째 문자열(``argv[0]``)인 ``test``는 C 코드가 컴파일되고 난 후 생성된 exe 실행파일이다.(``test.exe``)

 

 

#include <stdio.h>

int main(int argc, int * argv[])
{	
	int i = 0;
	printf("전달된 문자열의 수 : %d \n", argc);
	
	while(argv[i] != NULL)
	{
		printf("%d번째 문자열 : %s \n", i + 1 , argv[i]);
		i++; 
	}	
	
	return 0;		
}

``argv[ ]``배열의 마지막에 ``NULL``이 삽입되는 것을 이용해서 ``argv[ ]``의 모든 문자열을 출력하는 예시이다.

 

실행 결과

``"``로 문자열을 묶으면 공백을 포함하는 문자열을 생성해서 ``main``함수의 인자로 전달하는 것이 가능하다.

 

 

 

  ■ char * argv[ ]

// 아래의 두 문장은 같은 의미
char * argv[]
char **argv	// argv[]는 *(argv + 0)이므로 변환가능

  ``main``함수의 매개변수인 ``* argv[ ]``를 변환하면 ``**argv``가 된다. 이는 @@argv@@가 @@char@@형 더블 포인터 변수이고 @@char@@형 포인터 변수로 이루어진 1차원 배열 이름을 전달받을 수 있는 매개변수라는 의미이다.

 

 

#include <stdio.h>

void ShowAllString(int argc, char * argv[])
{
	int i;
	for(i = 0 ; i < argc ; i++)
		printf("%s \n", argv[i]);
}

int main(int argc, char * argv[])
{	
	char * str[3] = {
				"C Programming",
				"C++ Programming",
				"JAVA Programming"
			};
	ShowAllString(3, str);
	
	return 0;
}

``argc``와 ``argv[ ]``를 이용하는 예시이다.

``argc``에 인자의 개수, ``argv``에 문자형 배열을 넣어서 실행시킨다.

 

실행 결과

 

 

 

'기타 > C' 카테고리의 다른 글

[C] 스트림, 버퍼  (0) 2020.05.27
[C] 함수 포인터, void 포인터  (0) 2020.05.22
[C]이중 포인터(더블 포인터)  (0) 2020.05.12
[C] 포인터와 배열, 함수  (0) 2020.05.07
[C] 포인터  (0) 2020.05.05

+ Recent posts