<void 포인터란?>

void형 포인터는 자료형이 없는 포인터 변수라는 의미이다. 자료형에 제약 받지 않고, 아무 자료형의 주소라도 저정할 수 있는 포인터 변수이다.

다양한 자료형의 주소를 저장할 수 있지만, 주소만 저장할 수 있는 변수이다, 값을 저장하거나 변경할 수 없다는 말이다.

값을 저장하고, 변경하려면 어떻게 해야할까?

강제 형 변환이란것을 해줘야한다.

실습을 통해 알아보자.


<실습>



-결과-


 

 void형 포인터는 모든 자료형의 주소를 저장할 수 있는 포인터변수이며, void형 포인터를 통해 주소가 아닌 값을 참조할 수 없다. 값을 참조하기 위해서는 강제 형 변환이 필요하다.



LIST

<main()함수의 인자>

지금까지 공부했던 것에는 main함수에 인자는 없고 계속 void형이었다.

만약, main함수에는 인자가 있다면 어떻게 될까?

맨 처음 main함수의 인자는 프로그램을 실행할때 문자열을 띄어 쓰기 단위로 배열에 저장된다고 배웠을 것이다앞에 int형이니 어떤 (0 혹은 1)을 운영체제에게 반환해야된다고 배웠다

처음엔 이게 언제 어떻게 왜 사용되는 지도 모르고 배웠을 것이며, 지금부터 그것을알아보자!

main()함수의 인자는 shutdown -t 과 같이 옵션을 지정할때 쓰인다.

명령 행 컴파일러도 옵션을 줘서 내가 원하는 대로 프로그램을 실행 가능하게 할수 있다.

말그대로 옵션을 지정하는데 쓰인다.

무슨말인가? main()함수말고 리눅스 혹은 명령 프롬프트에 명령어를 입력했다고 가정해보자.

예를 들어 shutdown -t now 이런 명령어를 입력했다고 가정하였을 때, shutdown 이라는것은 명령어이며 -tnow는 옵션들이다. 사람들이 임의로 넣어준 옵션이다.

자 이제 main함수로 돌아가 어떻게 작동하는지 알아보자.

int main(int argc, char* argv[]) 이렇게 되어 있다고 하자.

첫번째, int argc는 사용자가 명령 프롬프트에서 입력한인자의 개수를 카운트하고,

두번째 char* argv[]는 명령 프롬프트 창에서 받은 인자를 공백을 기준으로 배열로 반환한다.

 

<실습>




-결과-

위 결과를 보면 test.exe를 실행하기 전에 인자로 -h-a를 넣었다. 그렇게 되면

int main(int argc, char* argv[])에 있는 인자 argc에는 입력한 인자의 개수가 들어가고, char* argv[]에는 배열을 생성해 argv[0]에는 test.exe가 있는경로, argv[1]에는 첫번째 입력한 인자 -h, argc[2]에는 두번째 입력한 인자 -a 이런식으로 저장이 되어 출력된다.

 

이것을 이용해 각 명령어들의 옵션을 지정해주면 어떨까?

간단한 실습을 통해 알아보자


<실습>

 


 

-결과-

해설을 해보면, argv[]라는 배멸에 test.exe뒤에 사용자가 임의로 입력한 옵션들은 다 저장이 된다. 그 중에, 맞는 것만 strcmp를 사용해서 출력하는 코딩이다.

다른 명령어들은 이런식으로 어떤 특정 명령어에 대해서만 반응하고 다른 명령어들은 반응하지 않는 식일 것이다.



LIST

Call by Reference에서 함수에서 주소를 넘겨받아 작업하는 프로그래밍을 하였다

이번에는 함수에서 return 값으로 값이아닌 주소를 반환하는 함수를 만들어보자.

주의할 사항이있다.

주소를 넘겨줄때 지역 변수의 주소를 반환하면 경고가 발생한다. 왜그럴까지역변수는 함수가 종료되면서 동시에 소멸되기 때문이다.

 


<실습>


 

-결과-

지역변수 또는 임시 변수의 주소를 반환하고 있습니다. 라고 경고메시지가 뜬다.

그런데 결과는 잘 출력되고 있다.

 

 

 

하지만, 대단위 프로젝트에서는 치명적인 문제가 될 수 있다. 이런 경고를 없애려면 어떻게해야할까?

바로 정적 변수를 사용하는 것이다.

정적변수... 어느 특정 공간에서 지역변수를 전역변수처럼 사용하고 싶을때 쓰는 변수이다!

 

<실습>

 


 

-결과-




LIST

<Call by Value>

함수의 호출 방식중에서 이번에는 값에 의한 호출에 대해서 알아보자.

값을 의한 호출이 무엇일까? 말 그대로 변수(메모리 공간에 저장된 값) 또는 값을 복사해서 함수를 호출하는 것이다.

ex)

int main()

{

int num1=10;

func(num1);

...

}

        

void func(int x)

{

x=x+1;

}

 

이렇게 메인함수에서 func이라는 함수를 호출 하였을때, 변수 num1의 값을 func함수의 x로 복사하는 것이다.

 

<실습>

 


 

-결과-

 

 


<Call by Reference>

주소에 의한 호출은 주소를 참조해서 함수를 호출하는 것을 의미한다.

ex)

int main()

{

int num1=10;

func(&num1);   // num1의 주소를 전달

...

}

        

void func(int* x)

{

x=x+1;

}

 

<실습>



-결과-



LIST

<포인터 변수의 상수화>

 

포인터 변수를 상수화 시키는 것은 무엇일까?

포인터 변수란, 변수에 주소를 저장하는 것을 의미한다.

그렇다면 이것을 상수화시킨다...? 이것의 의미는 두 가지가 있다.

1. 포인터 변수에 다른 메모리 주소를 저장하지 못하게 한다.




<실습>





-결과-

 


 

 

2. 포인터 변수를 통해 메모리 공간의 값을 변경하지 못하게하고, 직접 접근을 통해서만 변경이 가능하도록 한다.


<실습>

 


-결과-

 


LIST


<문자배열과 포인터>

문자를 배열로 저장하여 사용하는 경우도 있다. 여기서 문자란? 앞에서 배웠듯이 작은 따옴표(' ')로 이루어진 하나의 단어이다.

<실습>

 




-결과-

 




<문자열이란?>

큰 따옴표(" ")내에 포함된 하나 이상의 문자를 말한다. 문자열의 끝에는 종료를 알리는 NULL값 즉, \0이 자동으로 저장된다.

문자열 배열은 문자열을 배열에 저장하는 것을 말한다.



<실습>

 



-결과-

 

 

여기서 분명히 문자열을 넣었는데 배열로 한칸에 한문자씩 존재한다.

배열요소

"ABCD"

\0(null)

 

 

 

   

&arr[0]

&arr[1]

 

 

 

배열의 구조가 다음 표와 같이 구성된 것이 아니라 다음 표와 같이 구성되어 있다.

 

배열요소

'A'

'B'

'C'

'D'

\0(null)

   

&arr[0]

&arr[1]

&arr[2]

&arr[3]

&arr[4]

마치 다음 표와같이 존재한다는 것이다.

 

 

그렇다면 문자열배열을 문자배열로 표현할 수 있을까? 당연히 가능하다.



<실습>

 


 

-결과-

 

여기서 다른점은 널값의 유무이다. 문자열 배열은 항상 마지막에 이 문자열을 종료한다는 null값을 넣는데 문자 배열은 넣지않는다.

 

, 그렇다면 문자열 배열의 서식문자 %s에 대해 잠깐 알아보겠다. %s는 이 문자열을 보고 문자인지 문자열인지 어떻게 알 수 있을까... 바로 \0값때문이다.

%s는 문자열의 시작 주소를 입력받아서 종료 문자까지 문자열을 출력해준다.

 



<실습>

%s를 이용하여 문자열의 시작 주소를 받아서 null값을 만날때 까지 출력해보는 실습을 해보자.

 

-결과-

 

 

<포인터와 문자열>


문자열은 저장될 때, 메모리 공간에 연속적으로 저장되기 때문에 문자열의 시작주소만 알면 그 문자열의 어느부분이든 접근이 가능하였다. %s 서식문자를 이용하면 \0문자를 만나기 전까지 출력도 가능했었다. 모두 주소를 가지고 접근하기 때문이다.

주의할 사항이 잇다. 포인터와 문자열에서 포인터 변수에 문자열을 저장하게 되면 포인터는 배열이 아니라는 점!

예를 들어 char* p = "ABCD"; 이렇게 저장했다고 가정하자. 포인터 변수 p는 단순히 문자열 배열의 시작주소를 저장한 것일 뿐이지, p는 배열이 아니다.




<실습>



char arr[] = "ABCD";에서 문자열 상수 ABCD를 배열 arr에 저장하기 때문에 메모리공간의 주소가 부여되고, 배열의 요소를 통한 메모리 공간의 접근이 가능한 것이다.

하지만, 그 다음줄인 char* p="ABCD"; 같은 경우는 상수 ABCD의 시작주소를 포인터 변수 p에 저장한 것은 맞지만, ABCD가 저장된 메모리 공간은 이름이 없기때문에 각 요소에 접근이 안된다.

 

-결과-

LIST

<포인터 배열이란?>

쉽게말해 주소를 저장하는 배열이다.

구조) int* pointer[3];

int* : 자료형

pointer : 포인터 배열 이름

[3] : 배열 길이

 

포인터 배열의 개념 자체는 쉽다.

우리가 지금까지 배열에 int형 정수나, 실수, 문자를 저장했다면 포인터 배열은 주소를 저장하는 배열이다.

 

<실습>

 



-결과-

LIST

<배열 포인터 변수>

배열을 가리키는 포인터 변수라는 의미이다.

그렇다면 배열 포인터 변수를 사용하는 이유는 무엇일까?

배열에 접근할때도 직접접근이 아닌 포인터로 간접접근을 하기위해선 1차원 배열에는 1차원 포인터 변수를 사용하면 되지만, 2차원 포인터 변수에는 배열 포인터 변수를 사용해야하기 때문이다.

 

구조)

int (*p)[3];      // 2차원 배열을 가리키는 배열 포인터 변수 선언

int : 자료형

(*p) : 배열 포인터 변수 이름

[3] : 열 길이

 

 

<실습>



-결과-

 

 

<실습>


-결과-



LIST

<2차원 배열과 1차원 포인터 변수>

 

이번에는 1차원 포인터 변수에 2차원 배열의 시작주소를 대입해 2차원 배열에 접근하는 방법을 알아보자


<실습>

-결과-

 

 

왜 오류가 날까? 여기서 p2차원 배열의 행과 열을 전혀 알지 못한다.

단지 p가 알고있는 사실은 배열의 첫 주소만 알 뿐이다.

바로 여기서 p 2차원 포인터 변수가 아닌 1차원 배열 포인터 변수라는 사실을 말해준다.

그러므로 p1차원적으로 2차원 배열에 접근하게되는 것이다.

만약 여기서 p1을 더해서 p[0]+1과 같이 한다면 행단위로 움직이는 것이 아니기 때문에 적합하지않다. 1차원 포인터 변수인 p1차원 포인터 변수처럼 행동해야 한다.

, p+i라고 해야지 다음 주소를 알아낸다.

2차원 배열에 저장되어 있는 원소들은 p가 접근하기에 1차원 배열처럼 접근할 것이다.

p+0은 첫번째에 저장되어 있는 원소의 주소, p+1은 두번째에 저장되어 있는 원소의 주소...이렇게 말이다. 왜냐? +1씩 할때마다 행 단위로 움직이는 것이 아니기 때문이다.

1차원 포인터 배열이라는 것을 명심하자

 

<실습>

 


-결과-



LIST

'Programming > C' 카테고리의 다른 글

C언어 - 포인터배열  (0) 2016.05.19
C언어 - 배열 포인터 변수  (0) 2016.05.18
C언어 - 포인터와 1차원 배열  (0) 2016.05.16
C언어 - 함수 포인터  (0) 2016.05.15
C언어 - 포인터(Pointer)  (0) 2016.05.14

이번에는 배열의 개념과 포인터의 개념을 접목시켜 실습을 진행해보겠다

 

<실습>

포인터와 배열을 이용하여 원소를 출력해보자.


 

-결과-

 

1차원 배열에서는 *(array+i) == *&array[i] == array[i]은 값을 표현한다.

 

 

<실습>

 

이번에는 포인터 변수를 통해 1차원 배열 요소의 주소에 접근해보자

 

 

-결과-

 




<주소의 가감산>

 

포인터 연산의 가감산을 이용해 다양한 주소 표현을 실습해보자.


<실습>



-결과-




LIST

'Programming > C' 카테고리의 다른 글

C언어 - 배열 포인터 변수  (0) 2016.05.18
C언어 - 2차원 배열과 1차원 포인터 변수  (0) 2016.05.17
C언어 - 함수 포인터  (0) 2016.05.15
C언어 - 포인터(Pointer)  (0) 2016.05.14
C언어 - 2차원 배열, 주소체계  (0) 2016.05.13

+ Recent posts