c

C 언어 포인터

c 에서 포인터를 통해 직접 데이터의 주소에 접근할 수 있으며, 이러한 특성 때문에 C 언어가 low 하다고 불린다.

1
2
3
4
5
6
int main(void)
{
int num =5; // 정수형 변수 num을 생성한다.
int * pnum = # // 정수형 변수 num을 가리키는 포인터 변수 pnum을 선언한다.
*pnum = 10; // pnum이 가리키는 변수에 10을 저장하라
}

이처럼 특정 자료를 가리키는 자료형을 정의 할 수 있고, 이를 포인터라 한다.

포인터는 특정 자료형을 가리키고 있다.

여기서 포인터는 어떤 변수를 가리키는 그 자체이므로 메모리의 특정 주소값을 가진다.

때문에 포인터를 선언할 때에는 매우 신중해야 하는데 선언 후 아무 값도 할당 하지 않는다면 포인터는 쓰레기 값으로 초기화 되기 때문이다.

1
int *pnum = 399;

가령 위와 같은 코드는 매우 위험한데 이는 pnum이라는 포인터에 399를 할당하고 있고, 우리는 399 의 주소값이 얼마나 중요한 주소인지 전혀 이해하고 있지 못하기 때문이다. 이를 해결하기 위해 반드시 우리는 해당 포인터 값이 이상한 값을 가리키지 않도록 해야 하는데 주로 다음과 같이 널 포인터를 지정해 준다.

1
2
int *ptr2 = 0;
int *ptr2 = NULL;

위와 같이 NULL 혹은 0 으로 초기화 하면 시스템은 이것을 0 번째 주소값으로 인식하지 않고 아무것도 가리키지 않는 포인터를 만들어 낸다.

포인터와 배열의 관계

c 언어에서 배열의 이름은 그 자체로 포인터 이며, 정확히 말하면 배열의 첫번째 요소의 주소값이다.

1
2
3
int arr[3] = {1, 2, 3};
printf("%p", arr); // 주소값이 나온다.
printf("%p", arr[0]); // 위와 같은 주소값이 나온다. 즉, 배열의 주소는 첫번째 값의 주소를 의미한다.

변수 형태의 문자열 vs 상수형태의 문자열

1
2
3
4
5
char str1[] = "my string";
char * str2 = "your string";

str1[0] = "x";// 문자열 변경 성공
str2[0] = "x"; // 문자열 변경 실패

위처럼 모든 문자열 혹은 배열을 선언하면 그 자체는 그 문자열 혹은 배열의 첫번째 요소의 주소값이 된다.

하지만 위에서 처럼 문자열 혹은 배열 자체를 선언하는 경우와 그것의 주소값을 선언하는 경우에 따라 그 값의 변경 가능 유무가 결정이 된다.

위의 예처럼 str1의 경우 문자열 자체를 저장한 변수형 문자열이며, str2의 경우 문자열에 대한 주소값을 저장하는 상수 형태의 문자열이기 때문에 가리키는 값의 내용이 변경될 수 없다.

포인터 변수로 이루어진 배열

다음과 같이 포인터 변수로 이루어진 배열을 선언할 수 있다.

1
2
char * strArr[3] = {"Simple", "String", "Array"};
printf("%s \n",strArr[0]) // Simple

위처럼 “” 로 감싸진 문자열은 그 자체로 주소값을 나타내기 때문에 위와 같은 결과가 나오게 된다.

큰 따옴표로 묶여서 표현되는 문자열은 그 형태에 상관없이 메모리 공간에 저장된 후 그 주소값이 반환된다.

스트림과 데이터의 이동

컴퓨터 프로그래밍에서 입력이란 프로그램을 기준으로 프로그램 안으로 데이터가 흘러들어가는 것이고 출력이란 프로그램을 기준으로 데이터가 나오는 것이다.

우리는 흔히 컴퓨터 프로그램에 입력을 입력하고 문자열이 출력되는 것을 보면서 어떻게 컴퓨터가 모니터에 저런 문자열을 출력할 수 있는지 궁금해 한다.

우리가 입력하는 문자열이 어떻게 모니터에 출력되는 것일까?

우리가 작성하는 프로그램과 모니터는 기본적으로 연결되어 있는 것이 아닌 떨어져 있는 개체이며 둘 사이에 무언가 정보를 주고 받기 위해서는 이들을 연결시켜주는 다리가 필요하다. 이러한 매개물이 바로 스트림 이다.

c 언어 내의 printf 혹은 scarf 와 같은 함수는 운영체제에게 스트림의 형성을 요구하는 것이며 이는 표준 입출력 라이브러리인 stdin, stdout 을 통해 이루어 진다.

단일 문자의 입출력

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(void)
{
int ch1, ch2;

ch1 = getchar(); // 표준 입력 스트림으로부터 하나의 문자를 입력 받아서 반환
ch2 = fgetc(stdin); // 문자를 입력받을 스트림을 지정

putchar(ch1); // 인자로 전달된 문자정보를 stdout으로 표현되는 표준 출력 스트림으로 전송하는 함수
fputc(ch2, stdout); // 문자를 전송할 스트림을 지정할 수 있다. 가령 파일을 대상으로 데이터를 전송한다.

return 0;
}

문자열의 입출력

문자열의 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main(void)
{
char *str = "Simple String";

printf("1, puts test -------- \n ");
puts(str);
puts("So Simple String");

printf("2. fputs test ------ \n");
fputs(str, stdout); // 두번째 인자에서 출력의 대상 선택
printf("\n");
fputs("So Simple String", stdout);
printf("\n");

printf("3. end of main --- \n");
return 0;
}

문자열의 입력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main(void)
{
char str[7];
int i;

for (i = 0; i < 3; i++)
{
fgets(str, sizeof(str), stdin); // str의 사이즈 만큼 문자열을 입력받는다.
printf("Read %d: %s \n", i + 1, str);
}

return 0;
}

표준 입출력과 버퍼

위처럼 표준 입출력 함수를 통해 데이터를 입출력 하는 경우 해당 데이터들은 운영체제가 제공하는 메모리 버퍼 를 중간에 통과하게 된다. 이 메모리 버퍼는 데이터를 임시로 모아두는 메모리 공간이다.

컴퓨터는 왜 이러한 입출력 스트림을 바로 저장하지 않고 입 출력 버퍼에 보관후에 저장하는 것일까?

이는 바로 데이터의 효율성 때문이다.

모니터와 키보드라는 외부 자원을 활용하는 것은 컴퓨터에게는 상당히 무거운 태스크 이며 이를 최소한으로 수행하기 위해 주고 받는 데이터를 모은 다음 한번에 처리하는 것이다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×