-목차-
1. 포인터 변수에 대한 산술(+,-) 연산 및 우선 순위
2. 배열과 포인터
3. 문자열과 포인터
4. 배열 매개 변수와 포인터
5. 문자열 입출력 함수 : gets( )와 puts( )
1. 포인터 변수에 대한 산술(+,-) 연산 및 우선 순위
※ 포인터 변수에 대한 덧셈, 뺄셈 연산
- 포인터 변수에 대해 정수 변수와 유사하게 + , - , ++(increment), -- (decrement) 연산을 할 수 있음. 그러나 정수 변수와는 연산 결과가 다름.
#include <stdio.h>
int main(void)
{
int a = 1, *pa = &a; // int형 변수 a와 a의 포인터 변수 pa
short b = 1, *pb = &b; // short형 변수 b와 b의 포인터 변수 pb
char c = 1, *pc = &c;
double d = 1.0, *pd = &d;
printf("%p, %p, %p, %p\n",
pa, pb, pc, pd);
pa = pa + 1; // +4 = sizeof(int)
pb = pb + 1; // +2 = sizeof(short)
pc = pc + 1; // +1 = sizeof(char)
pd = pd + 1; // +8 = sizeof(double)
printf("%p, %p, %p, %p\n",
pa, pb, pc, pd);
return 0;
}
- 위와 같이 정수 변수와 달리 실제 더해지거나 빼지는 값은 포인터 변수가 참조하는 자료형의 크기에 따라 달라짐 ★★
※ 포인터 변수와 *, ++ : *p++
- dereference 연산자인 * (주소를 통해 그 값에 접근하는 연산자)와 increment 연산자인 ++는 포인터 변수와 함께 자주 쓰이는 대표적인 연산자이다.
- *와 ++는 동일한 우선 순위를 가진다 (둘 다 단항 연산자이므로 산술 연산자와 달리 오른쪽에서부터 왼쪽으로 연산 적용) ★★
ex) ++*pa = ++(*pa); *pa++ = *(pa++)
#include <stdio.h>
int main(void)
{
int a = 1, *pa = &a; // int형 변수 a와 a의 포인터 변수 pa
printf("a : %d, pa : %p\n",a,pa);
++*pa; // pa가 가르키는 변수에 +1을 해줌
printf("a : %d, pa : %p\n",a,pa);
*pa++; // pa값에 +1해줌
printf("a : %d, pa : %p\n",a,pa);
a += *--pa; // a에 pa값에 -1해준 것을 더해줌
printf("a : %d, pa : %p\n",a,pa);
return 0;
}
2. 배열과 포인터
※ 배열의 이름과 주소
- 배열의 이름은 배열의 시작 주소를 나타내는 포인터(주소 상수)이다
- 상수이므로 값을 변화시키려는 연산은 모두 Syntax Error가 발생한다
- 배열 이름이 포인터이므로 + 연산 가능
#include <stdio.h>
int main(void)
{
int num[10] = {1,2,3,4,5,6,7,8,9,-1};
// num[1] = num[1] + 1; OK
// num = num + 1; Syntax Error (상수라서!!)
// num++; Syntax Error
// 배열의 시작 주소는 배열의 이름과 같으므로 &num과 num은 동일한 표현이다.
printf("%p, %p\n", num, &num[0]);
printf("%p, %p\n", num+1, &num[1]);
printf("%p, %p\n", num+2, &num[2]);
printf("%p, %p\n", num+9, &num[9]);
return 0;
}
※ 포인터 제2법칙 ★★
- 배열 이름에 n을 더한 것은 첨자에 n을 사용하는 배열 요소의 주소와 같다.
※ 배열의 순회 ★★
- 배열의 첨자 연산은 본질적으로 포인터 연산이기에 상황에 따라 교환하여 사용할 수 있다.
#include <stdio.h>
int main(void)
{
int num[10] = {1,2,3,4,5,6,7,8,9,-1}
int i, *pn;
for (i=0; i < 10; i++){
printf("%d\n", num[i]);
}
for (pn=num; pn < num+10; pn++){
printf("%d\n", *pn);
}
return 0;
}
3. 문자열과 포인터
※ 문자열은 char형 배열
- NULL 문자 ('\0')를 덧붙여 끝을 표시함
※ 문자열과 포인터
#include <stdio.h>
int main(void)
{
char str[] = "123,456,789";
char *pstr = str;
int i = 0;
printf("%p, %p, %p\n"
,str,pstr,&pstr);
while(str[i]) {
putchar(str[i++]);
}
putchar('\n');
while(*pstr) {
putchar(*pstr++);
}
putchar('\n');
return 0;
}
- pstr이 str을 포인팅하게 값 초기화 (&연산자를 사용하지 않아도 됨)
- str과 pstr은 어떻게 다른가? ★★★★
-> str : 포인터 상수, pstr : 포인터 변수
- dereferencing, 첨자연산 적용 시 유사 ★★
-> *(str+1) ~ *(pstr+1), str[1] ~ pstr[1] // 동일
+) 포인터를 이용한 문자열 순회 ex) CSV
① 첨자 연산을 이용
#include <stdio.h>
int main(void)
{
int i; // index
int num = 0, sum = 0; // number
char str[] = "123,456,789";
for (i = 0; str[i]; i++) {
if (str[i] == ',') { // new num
sum += num;
num = 0;
} else { // a digit
num = num*10 + (str[i] - '0');
}
}
sum += num;
printf("Sum of all values in ");
printf(" CSV[%s] : %d", str, sum);
return 0;
}
② 포인터를 이용
#include <stdio.h>
int main(void)
{
char *pstr; // pointer
int num = 0, sum = 0; // number
char str[] = "123,456,789";
for (pstr = str; *pstr; *pstr++) {
if (*pstr == ',') { // new num
sum += num;
num = 0;
} else { // a digit
num = num*10 + (*pstr - '0');
}
}
sum += num;
printf("Sum of all values in ");
printf(" CSV[%s] : %d", str, sum);
return 0;
}
4. 배열 매개변수와 포인터
※ 배열 매개 변수는 본질적으로 포인터 매개 변수이다. ★★
- 배열 형식으로 선언한 매개 변수는 실제로는 주소 값을 가지는 포인터 변수
- 함수 호출 시 배열 이름을 사용하면 배열의 이름이 나타나는 주소값이 (포인터) 매개 변수로 복사됨
- 일반적인 상황에서 배열과 배열의 포인터함수가 같다는건 아님!!!
ex)
#include <stdio.h>
int sum(int n[10]);
int main(void)
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
printf("Array Sum = %d\n",sum(a));
return 0;
}
int sum(int n[10]) { // int n[10]을 int n[] 혹은 int *n으로도 적을 수 있음
int i, s = 0;
for (i = 0; i < 10; i++){
s += n[i];
}
return s;
}
※ 배열 매개 변수 함수 호출에서의 배열 변경
- C 함수는 Call by Value 특성으로 함수 호출에 쓰인 변수가 함수 호출에 의해 값이 변하지 않는 것이 특징이라고 했지만 배열 매개 변수 함수에서 배열 매개 변수는 배열의 포인터 매개 변수이기에 Call by Address Value를 만족한다.
ex)
#include <stdio.h>
#define END_MARK -1
int sum_w_endmark2(int nums[]) {
int i = 0, sum = 0;
while (nums[i] != END_MARK){
sum += nums[i];
nums[i++] = END_MARK;
}
return sum;
}
int main(void) {
int array1[4] = {1,2,3,END_MARK};
int s1;
printf("Array1 [%d, %d, %d]\n",
array1[0], array1[1], array1[2]);
s1 = sum_w_endmark2(array1);
printf("Array1 [%d, %d, %d]\n",
array1[0], array1[1], array1[2]);
printf("Sum of Array1 : %d\n", s1);
return 0;
}
5. 문자열 입출력 함수 : gets( )와 puts( )
※ gets( ) 함수와 puts( ) 함수 ★★
- 한 번에 한 문자열을 입력/출력할 수 있다.
- scanf( ) 혹은 printf( )를 통해 문자열을 입력/출력할 수도 있지만 함수 호출 명령이 복잡하기에 get( )와 puts( )를 사용한다.
※ char *gets(char *str);
- description : 표준 입력으로부터 개행 문자('\n') 혹은 EOF를 만나기 전까지 문자열을 읽어 들여 문자형 포인터 str이 가리키는 기억 장소에 저장하고 포인터 str을 리턴
- param : 입력받은 내용의 문자열이 저장될 char 타입의 포인터
- return : 성공적으로 읽어들이면 str을 반환하고, 실패 혹은 오류 발생시 NULL값 반환
+) str이 가리키는 기억 장소는 입력 값을 쓸 수 있는 유효한 저장 공간이어야 한다
+) 문자열에 개행문자는 추가하지 않고 '\0'값을 마지막에 추가한다
※ int puts(const char *str);
- description : str이 가리키는 문자열을 표준 출력에 쓴 뒤 개행 문자('\n')도 추가적으로 쓴다
- param : 표준 출력에 쓰여질 문자열
- return : 성공적으로 쓰여졌다면 음이 아닌 값이 반환되고 실패했다면 EOF가 반환됨
+) 포인터 변수를 초기화하는 습관을 들이자. (초기화하지 않으면 Garbage Value를 가짐. 안정적인 프로그램 작성을 위해 변수 값을 초기화하는 것이 바람직함)
+) 표준 문자 처리 함수
'CS > C, C++' 카테고리의 다른 글
[C/C++] 09 - 2 구조체 변수와 함수, 포인터 (0) | 2021.12.15 |
---|---|
[C/C++] 09 - 1 구조체 기초 (0) | 2021.11.27 |
[C/C++] 08 - 1 포인터 개념 (0) | 2021.11.12 |
[C/C++] 07 - 3 함수와 모듈러 프로그래밍 (0) | 2021.11.02 |
[C/C++] 07 - 2 표준 라이브러리 함수의 사용 (0) | 2021.10.29 |