배열의 모든 요소에 포인터로 접근하기

 

포인터로 배열에 접근하는 또 다른 이점은 바이트 단위 조작에 있다. 배열이 어떤 형이던 1바이트 단위로 데이터를 조작할 수 있다. 메모리를 바이트 단위 조작이 가능하다는 것은 하드웨어 그 자체를 들여다 보는 것과 같다. 운영체제가 실행되면 모든 하드웨어/소프트웨어 정보들이 메모리에 올라와 있기 때문이다.

 

리누스 토발즈의 말처럼 C는 가장 하드웨어에 근접한 프로그래밍 언어라고 할 수 있다. 세상에 나온지 50년가까이 되는데 아직까지 현역으로 사용되고 있다는 것은, C가 여전히 하드웨어 레벨의 프로그래밍에 필요하다는 것을 말한다.

 

바이트를 다룰 수 있다고 해서 모든 데이터를 마음대로 움직일 수 있다는 것은 아니다.

 

음수나 부동소수점을 다루는 방법은 또 다른 문제다. 그 구조를 먼저 이해해야 하기 때문이다.

 

예제에서는 포인터를 이용해서 배열의 전 요소들에 어떻게 접근하는지 하나의 예를 보여준다.

#include <stdio.h>

void main(){
    int arr1[5] = {0,};

    arr1[0] = 0x11223344;
    arr1[1] = 0x12345678;
    arr1[2] = 0x77553311;
    arr1[3] = 0x43217654;
    arr1[4] = 0x14311341;

    char *ptr = (char *) arr1;
    int length = sizeof(arr1)/sizeof(arr1[0]);

    for(int i=0; i < sizeof(arr1); i++){
        printf("array [%2d] : (%X) ", i , *(ptr+i));
        if((i+1)%4 == 0){
            printf("\n");
        }
    }
}

배열 포인터

32비트 정수형이 5개인 배열에 16진수로 할당한다. 16진수로 할당하는 이유는 1바이트에 16진수 두자리가 대응되기 때문에 메모리를 가장 직관적으로 조작가능한 진법이기 때문이다. 16진수 2승, 256가지 수를 표현가능하다. (0~255 혹은 -128~127) 32비트 정수형을 바이트 단위로 나누면 4바이트이고 16진수의 2승인 256 단위 4개를 조작 할 수 있다. 말이 길지만 메모리 하나하나에 직접 작업한다고 생각하면 된다.

 

인텔 x86계열 CPU의 명령어도 1바이트로 나눠져 있다. 바이트 단위의 명령을 CPU에게 보낸다.

 

예> 0x90 NOP(no operation 아무것도 하지 않음)

 

포인터를 조작하게 되면 컴퓨터의 움직임을 하나하나 까지 이해할 수 있게 되는 것이다. 어셈블리어도 기계어가 바탕이지만 다양한 라이브러리가 있는 C언어의 명령어로 메모리를 조작하는 것은 훨씬 강력할 수 밖에 없다.

 

x86 명령어 리스팅

 

x86 instruction listings - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search List of x86 microprocessor instructions The x86 instruction set refers to the set of instructions that x86-compatible microprocessors support. The instructions are usually part of an e

en.wikipedia.org

포인터로 배열을 합산하기

포인터를 사용해서 배열의 각 요소를 바이트 단위로 접근할 수 있고, 또 값을 바꿀 수도 있다. 이번에는 합산을 해본다.

 

우선 배열을 만들고 누산을 위한 total 변수를 만들어 둔다.

 

for 문에서 포인터를 사용하여 total 에 배열 값을 누산한다. 원리를 알게되면 쉽다.

 

#include <stdio.h>

void main(){

    int score[7] ={70,82,67,58,93,77,62};
    int *ptr = score;
    int total = 0;

    int length = sizeof(score)/sizeof(score[0]);

    for(int i=0; i < length; i++){
        total += *ptr;
        ptr++;
    }
    float average = (float)total/length;
    printf("Total score : %d, Average score : %0.2f ", total,average );
}

 

포인터 배열 

포인터도 여러개를 쓰면 변수관리가 힘들어지니까 배열로 선언할 수 있다.

 

#include <stdio.h>

void main(){
    
    int array[3] = {10,20,30};
    int *ptr[3] = {&array[0], &array[1], &array[2]};

    int length = sizeof(array)/sizeof(array[0]);

    for(int i = 0; i < length; i++){
        printf("%d : %p\n",*ptr[i],ptr[i]);
    }
}

1단계는 포인터와 배열을 1대1로 대응 시켜서 사용하는 방법이다. 큰 이점은 없을 것 같고 배열을 중심으로 돌아가는 느낌이다.

 

 

*다음은 이차원 배열에 포인터를 사용한다. 문법이 조금 더 복잡하긴 하지만 이차원 배열의 메모리에도 문제 없이 접근이 가능하다.

#include <stdio.h>

void main(){
    
    int array[2][3] = {0,};

    array[0][0] = 12;
    array[0][1] = 34;
    array[0][2] = 56;
    
    array[1][0] = 78;
    array[1][1] = 89;
    array[1][2] = 10;
    
    int (*ptr)[3];
    ptr = array;

    printf("%d : %p\n",(*ptr)[0], &(*ptr)[0]);
    printf("%d : %p\n",(*ptr)[1], &(*ptr)[1]);
    printf("%d : %p\n",(*ptr)[2], &(*ptr)[2]);

    printf("%d : %p\n",(*(ptr+1))[0], &(*(ptr+1))[0]);
    printf("%d : %p\n",(*(ptr+1))[1], &(*(ptr+1))[1]);
    printf("%d : %p\n",(*(ptr+1))[2], &(*(ptr+1))[2]);
}

 

함수 포인터

#include <stdio.h>

 int add(int a,int b){
     return a + b;
 }
 int sub(int a,int b){
     return a - b;
 }
 int mul(int a,int b){
     return a * b;
 }
 int div(int a,int b){
     return a / b;
 }
void main(){
    int (*p[4])(int,int) = { &add,&sub,&mul,&div};
    char op[4] = { '+','-','*','/'};

    for (int i=0; i<4; i++){
        printf("%d %c %d = %d\n",7,op[i],3,(*p[i])(7,3));
    }
}

 

함수를 포인터 처럼 사용할 수 있게 해준다. 함수를 줄세워서 루프를 돌릴 수 있다.

 

공유하기

facebook twitter kakaoTalk kakaostory naver band