본문 바로가기

Software

Secure Cordng C - 포인터의 개념과 이해 #2

1-5 포인터와 연산

1. 포인터와 포인터와의 산술 연산

  • 주소는 정수 타입의 상수이므로 근본적으로는 산술 연산 가능
  • 덧셈과 곱셈 그리고 나눗셈에 의한 연산은 할 수 없는데 이는 잘못된 메모리 참조로 인한
    프로세스의 비정상 종료를 최소화하기 위함
  • 피연산자를 포인터로 하는 연산자는 오직 뺄셈만 가능
  • 뺄셈의 결과는 정수 타입의 값이며 이 값은 두 위치 사이의 차(거리)를 의미
#include <stdio.h>

int str_length(const char *str)
{
char *p = str;
while(*p++)
/* nothing */;
return (p - str) -1;
}

void main()
{
printf("length of hello = %d\n", str_length("hello"));
}


2. 포인터와 정수의 산술 연산

  • 피연산자가 포인터와 정수인 경우에는 뺄셈과 덧셈만 가능
  • 포인터와 정수와의 연산 결과는 포인터(주소)이며 그 값은 포인터가 가리키는 객체의 크기의 정수배
#include <stdio.h>

typedef struct {
int x, y;
} Point;

void main()
{
char *pChar = 0;    printf(" pChar + 1 = %d\n", pChar + 1);
char *pShort = 0;    printf(" pShort + 1 = %d\n", pShort + 1);
int *pInt = 0;    printf(" pInt + 1 = %d\n", pInt + 1);    
long *pLong = 0;    printf(" pLong + 1 = %d\n", pLong + 1);
float *pFloat = 0;    printf(" pFloat + 1 = %d\n", pFloat + 1);
double pDouble = 0;    printf(" pDouble + 1 = %d\n", pDouble + 1);
Point *pPoint = 0;    printf(" pPoint + 1 = %d\n", pPoint + 1);
}
  • 이와 같이 연산되는 이유는 배열과 같이 연속된 형태의 메모리에서 다음 원소에 올바르게 접근할 수 있도록 하기 위함



3. 포인터와 산술 연산 정리
  • 포인터 - 포인터 = 정수(Gap)
  • 포인터 + 정수 = 포인터
  • 포인터 - 정수 = 포인터(배열의 시작 주소로 부터, 다음의 원소를 자동으로 계산하기 위해)
주소(Address)란? CPU가 특정 메모리에 접근하기 위해 부여한 양의 정수(0을 포함)

임의의 배열 arr의 인덱스 i가 있다고 가정하자.

arr[i] == *(arr + i) => *(arr + sizeof(*arr) * i)
^------- 컴파일러가 알아서 수행함



1-6 배열 포인터

1. 배열의 이름과 주소 연산자
  • 배열의 이름은 첫 번째 원소의 시작 주소로 해석(상수)



2. 주소 연산자의 유무
  • 배열의 이름에 주소 연산자를 사용하는 것과 하지 않은 것은 완전히 다른 의미
  • 아래의 코드는 컴파일러에 따라 다른 오류를 출력함
    1. C 컴파일러(Visual Studio 2013 기준): warning C4047: '초기화 중' : 'int *의 간접 참조 수준이 'int (*)[3]과(와) 다릅니다.
    2. C++ 컴파일러(Visual Studio 2013 기준): error C2440: '초기화 중' : 'int (*)[3]에서 'int *'(으)로 변환할 수 없습니다.
void main()
{
int arr[3];

int *p1 = arr;    //OK
int *p2 = &arr;    //ERROR
}


3. 배열 포인터(Pointer to Array)

  • 배열 타입의 주소를 저장하는 포인터를 배열 포인터라고 한다.
  • 배열의 타입은 '타입[길이]'
  • 배열 포인터의 선언 방법 : 원소의_타입(*변수명)[길이];
void main()
{
int arr[3];

int *p1 = arr;    //OK

// int *p2 = &arr;    // Warning in C, Error in C++
int(*p2)[3] = &arr;
}


1-7 포인터 배열

1. 포인터 배열

  • 원소의 타입을 포인터로 하는 배열을 의미
#include <stdio.h>

void main()
{
char *msg[] = { "hello", "hi", "bye" };
int i;
for(i = 0; i < 3; i++)
printf("msg[%d] = %s\n", i, msg);
}


2. 배열 포인터의 사용

  • 명령행의 옵션이 main 함수의 배열 포인터로 전달
#include <stdio.h>

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


1-8 2차원 배열과 포인터


1. 2차원 배열과 포인터

  • 2차원 배열은 원소가 1차원 배열인 1차원 배열임
  • 2차원 배열을 가리키는 포인터의 선언은 배열 포인터를 사용해야 함
  • 배열 포인터 선언 방법 : 원소의_타입(*변수명)[열의길이];
  • 2차원 배열에 대한 포인터는 이중 포인터가 아님
void main()
{
int arr1[6] = { 1, 2, 3, 4, 5, 6 };
int *p1 = arr1;

int arr2[2][3] = { { 1, 2, 3 }. { 4, 5, 6 } };
int(*p2)[3] = arr2;
}


2. 2차원 배열을 사용한 함수 호출

  • 포인터를 사용하지 않고 2차원 배열을 함수의 인자로 사용하는 방법
#include <stdio.h>

void display(int arr[][3], int row)
{
int i, j;
for(i = 0; i < row; i++){
for(j = 0; j < 3; j++)
printf("%d ", arr[i][j]);
printf("\n");
}
}

void main()
{
int arr[2][3] = { { 1, 2, 3 }. { 4, 5, 6 } };
display(arr, 2);
}


  • 포인터를 사용한 방법
#include <stdio.h>

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

void main()
{
int arr[2][3] = { { 1, 2, 3 }. { 4, 5, 6 } };
display(arr, 2);
}


3. 2차원 배열에서의 첨자 연산

  • arr[i][j] = (*(arr + i))[j] = *(*(arr + i) + j)


반응형

'Software' 카테고리의 다른 글

Secure Cordng C - Coding Style  (0) 2018.07.20
Secure Cordng C - 포인터의 개념과 이해 #3  (0) 2018.07.18
Secure Cordng C - 포인터의 개념과 이해 #1  (0) 2018.07.09
DSP란 무엇인가?  (1) 2016.11.21
ROM BIOS란?  (0) 2016.05.18