보통 정해지지 않은 크기의 문자열을 저장할 때는 

1차원 메모리를 동적으로 할당 받아 사용합니다. 


이 메모리는 1차원 배열처럼 사용할 수 있지요.

다만, 할당받은 메모리의 위치를 벗어난 곳을 사용하면,

access 위반으로 프로그램이 동작하다가 강제 종료됩니다. (Run-time Error)

프로그래밍 하다보면 많이 발생되는 문제입니다. 

당연한 말이지만, 허가된 공간에서만 접근하고 사용해야합니다.


허가된 공간의 시작 주소는 string이며, 끝나는 지점(주소)은 string + STR_NUM입니다.

다만, 사용할 크기만큼 문자열을 넣어 사용하고 마지막 위치에 NULL을 넣어서

그 끝을 알릴 수 있습니다.


NULL 

 

 

다음처럼 5개의 공간을 할당 받더라도, 두개만 쓰고 3번째에 NULL을 넣어, 그 끝을 알릴 수 있습니다.





이젠

2차원 배열형태로 메모리 할당을 받은 경우를 설명하려 합니다


아래와 같이 1열엔 seoul, 2열엔 olympic, 3열엔 korea가 있고, 마지막열에 NULL로 그 끝을 알리려 합니다.

또한 각 행마다의 단어의 끝을 알리기 위해 seoul NULL, olympic NULL, korea NULL 로 저장합니다.


s

NULL 

 

 

 o

NULL 

 k

NULL 

 

 

 NULL

 

 

 

 

 

 

 


문자열의 경우 끝에 자동으로 NULL이 붙지만, 마지막 열은 일부로 NULL을 사용합니다. 


이 경우 print_strings함수에 단어의 갯수를 알려주지 않더라도, NULL이란 값이 보이면, 마지막 단어라는 점을 알릴 수 있습니다.

(물론 pring_stirng(char ** strs, int word_num) 이렇게 word_num을 직접 함수에 전달할 수 도 있지만요.)


print_strings함수에서는  NULL이 나오기 전까지 단어 출력을 반복합니다.( while(current[i]) )



다시 정리하면, 포인터의 사용시 마지막엔 NULL을 넣음으로...

허가 받지 않은 공간을 모르고 침범하는 오류를 피할 수 있습니다.


1차원 공간 뿐 만아니라 (문자열의 끝)

2차원 공간에서도, NULL을 사용할 수 있습니다. ( 마지막 단어)





Posted by 고무함지
,


포인터 연산자의 경우 다른 연산자와 같이 사용되면, 정말 이해하기 힘들어집니다.

저도 이런 상황에서 항상 잘못 이해해서 오류를 만들곤 했습니다.

 

아래의 예는 사실 이렇게 혼란스럽게 사용하면 안 되지만, 일부로 공부를 위해 극단적인 예를

들어서 설명하려 합니다.

 

1

2

3

4

5

6

7

8

9

int array[5={1020304050};

int * p = array + 3;  // array의 3번째 항목의 주소를 대입. 즉 {40, 50}

   

++*p++;

   

printf("%d \n"*p);

   

// 과연 다음의 값은 어떻게 될까요?

   

Colored by Color Scripter

cs

 

 

여기서 프린트 되는 값은 "50" 입니다.

 

이유를 설명하기 전에, 연산자 우선 순위에 대해 알고 있어야 합니다.

아래의 연산자 우선 순위(c언어 기준) 를 보면, '++(후위증가)' 연산자가 2 순위이며,

'++(전위증가)'와 '*' 우회(역참조)연산자가3순위입니다.

즉 2 순위를 먼저 해석해야 하며, 2 순위의 경우 … 결합 법칙상 왼쪽먼저

해석을 진행합니다. 그러나 p의 왼쪽엔 후위증가 연산자가 없으므로,

P의 오른쪽의 후위증가 연산자를 해석합니다.

그러면, ++*p++ 을 해석의 순서대로 나눠 보면…

 

++*p++ // 1. ++ 후위 증가를 한다.

++*p++ // 2. p++라는 값이 후위 증가가 된다. p는 {40, 50}을 가르키는 포인터이므로,

포인터 값이 증가된다면, { 50 } 을 가리키게 됩니다. 다만…. 후위 증가이므로, 현재라인에서는

변화가 없고, 아래 print문에서 변경사항이 적용됩니다.

 

// 3. 이제 나머지 3순위 연산자를 해석해봅니다. 3순위의 경우 오른쪽에서 왼쪽으로 해석합니다.

++*p++ // 3. *p …p가 가르키는 값이므로, { 40 }이 됩니다. 이 값을 ++ 전위 증가를 합니다.

그러면, 40은 1이 증가됩니다. (포인터의 주소값이 증가되는 것이 아니라, 그 값이… 증가됨)

그러므로, 41이 됩니다.

 

코드에 주석으로 각 라인마다 결과를 보면 아래와 같게 됩니다.

1

2

3

4

5

6

7

int array[5={1020304050};

int * p = array + 3;  // array의 3번째 항목의 주소를 대입. 즉 {40, 50}

   

++*p++;   // p는 {41, 50} 

   

printf("%d \n"*p);  // p는 { 50 } , array[5]는 { 10, 20, 30, 41, 50} 

   

Colored by Color Scripter

cs

 

역시나 그래도 어렵고 복잡하긴 한데,

연산자 우선 순위를 이해하는 것을 공부하기에 좋은 문제 인 듯합니다. ^^

 

< 출처 : wiki https://ko.wikipedia.org/wiki/C%EC%99%80_C%2B%2B%EC%97%90%EC%84%9C%EC%9D%98_%EC%97%B0%EC%82%B0%EC%9E%90 >

Posted by 고무함지
,

Pointer(포인터)를 이용해서, 문자열을 알파벳 순서로 정렬(sort)하는 예제 코드 입니다.


어제 포스팅한 single linked list의 3번째 예제처럼 포인터의 포인터(포인터의 주소)를

전달하여, swap함수에서 두 자료를 교환하고 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// swap 함수 2016/6/1
 
void swap(char **a, char **b);
void sort(char *ary[], int cnt);
 
 
void swap(char **a, char **b)
{
    char *temp;
    temp = *a;
    *= *b;
    *= temp;
}
 
//sort a, b, c...
void sort(char *ary[], int cnt)
{
    int i, j, min;
    for (i = 0; i < cnt - 1; i++)
    {
        min = i;
        for (j = i + 1; j < cnt;j++)
        {
            if (strcmp(ary[min], ary[j]) > 0)
                min = j;
        }
        swap(&ary[min], &ary[i]);
    }
}
 
void main(void)
{
    int i;
    char *arr[] = { "abecd""ddbeeddd""cedvae","hhxxz" };
    sort(arr, 4);
    for (i = 0; i < 4; i++)
    {
        printf("%s, ", arr[i]);
    }
    printf("Good bye\n");
}
 
cs


Posted by 고무함지
,

포인터 사용시 주의해야할 것은 포인터 변수의 크기를 알아 내는 것입니다.

크기를 알아야 자료 복사나, 메모리 할당등을 할 수 있기 때문입니다.


여러가지 예제로 확인해보니, 주의할 점이 보이더군요.


1. 포인터 변수 자체의 크기인 경우


2. 포인터가 가르키는 것이 배열인 경우 그.. 배열의 크기가 됨.


3. 포인터가 가르키는 것이 배열인지, 행 하나인지 주의 할 것



아래 내용으로 어떤 차이들이 있는지 확인해보시기 바랍니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void main(void)
{
    int arr[3][4= { {1,2},{3,4,5},{6,7,8,9} };
    //0행 : 1, 2, 0, 0
    //1행 : 3, 4, 5, 0
    //2행 : 6, 7, 8, 9
    printf(" %d,", arr[2][1]);   
    // 배열 2행 1열의 값은 (0열 다음값인) -> 7
 
    printf(" %d,"*(*(arr+2)+1) );
    // 배열 2행 1열의 값은 역시 -> 7
 
    printf(" %d,"sizeof(arr));
    // arr은 전체 배열의 크기인 3x4x4byte = 48
 
    printf(" %d,"sizeof(arr+0));
    // 이건 arr과 동일한 결과가 나올 것으로 예상했으나, 명시적으로 0번째 행을 
    // 지칭 하기 위해서 + 0 을 하는 것임. 0번 행의 포인터이므로 .. 포인터의 크기는 4byte
 
    printf(" %d,"sizeof(arr+1));  // 역시 포인터의 크기 -> 4
    printf(" %d,"sizeof(arr+2));  // 포인터의 크기   -> 4
    printf(" %d,"sizeof(*(arr+2))); 
    // 포인터가 가르치는 곳(*연산자)이므로... 행의 크기 4x4byte = 16
 
    printf(" %d,"sizeof(*(arr + 2)+1));  
    // 포인터의 포인터가 가르키는 곳(**)이므로... 열의 하나의 크기 -> 4 byte
 
    printf("Good bye");
    // 결과  7, 7, 48, 4, 4, 4, 16, 4,
}
 
cs


Posted by 고무함지
,



아래 에러가 발생하였다. 무슨 상황일까?


"Invalid address specified to RtlValidateHeap"

( 참고로 프로그램 작성중 debug모드로 실행시 발행하는 메시지이며...)


( exe파일을 직접 실행할 경우... 아래의 메시지 팝업 창이 발생합니다.)

"프로그램이름.exe의 작동이 중지되었습니다."


포인터를 선언하고, malloc을 이용하여 heap 메모리 영역에 동적으로 메모리할당을 할 경우...

그 메모리를 다 쓴 경우 해제를 하여, 메모리의 낭비를 줄여야 한다.


이건 동적 메모리의 장점인데... 다만 메모리 관리를 하지 않으면, 실행하다가 메모리 부족으로

프로그램이 일정 시간이 지나면서 죽어버리는 문제가 발생한다.


바로 문제가 나올 수도 있고, 몇시간 또는 며칠이 될수 도 있다.


여하튼... 나도 간혹 실수하는 부분을 적어본다.


오늘의 내용은... 동적으로 할당한 메모리를 free할 때... 오류가 발생하는 상황을 설명하려 한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main(void)
{
    int *currArr = NULL;
    int Arr[5= { 0,1,2,3,4 };
    int index, currSize = 5, newSize = 10;
    currArr = (int*)malloc(sizeof(int)*currSize);
 
#if 0
    currArr = Arr;   // make run time error : "Invalid address specified to RtlValidateHeap"
#else
    strcpy(currArr, Arr); // use string copy instead of address copy
#endif
    
    free(currArr);
    printf("Good ~ ! ");
}
 
cs


위의 내용을 보면, currArr을 동적으로 할당하여 5 x 4byte(int형의 크기) = 20 byte의 크기로 할당하고...

Arr 배열의 주소를 currArr에 넣어줬다. 

그리고 나서... free(currArr)을 할 경우..

다음과 같은 에러가 프로그램이 동작하는 도중에 발생한다. (Runtime Error)


"Invalid address specified to RtlValidateHeap"


이는 아마도... Arr라는 배열까지 free가 되려는 상황 같다.


이를 구분하기 위해 배열만 따로 선언하고, free를 해봤다.


1
2
3
4
5
6
7
8
void main(void)
{
    int *currArr = NULL;
    int Arr[5= { 0,1,2,3,4 };
    
    free(Arr);
    printf("Good ? ");
}

c


이 경우에 free(Arr)을 하는 순간 동일하게 런타임 에러가 발생했다.


이를 보면 이러한 상황을 피하기 위해서는 포인터에 배열의 주소를 대입하기 보단...

배열의 내용을 복사하기로 하는 것이 좋다.



1
2
3
4
5
6
7
8
9
10
11
12
void main(void)
{
    int *currArr = NULL;
    int Arr[5= { 0,1,2,3,4 };
    int index, currSize = 5, newSize = 10;
    currArr = (int*)malloc(sizeof(int)*currSize);
 
    strcpy(currArr, Arr); // use string copy instead of address copy
    
    free(currArr);
    printf("Good result");
}
cs


아까 위의 내용에서 안전한 코드만 다시 정리하였다.


이 시간 이후로...좀 고려할 내용으론... 배열과 포인터간 주소 대입을 한다면, malloc하지 않고

배열로 포인터를 초기화하는 방법도 있겠다.

그리고 물론 배열은 free를 하지 않는다고 나는 알고 있다.



1
2
3
4
5
6
7
8
void main(void)
{
    int *currArr = NULL;
    int Arr[5= { 0,1,2,3,4 };
    currArr = Arr;
    
    printf("Good result");
}
cs


Posted by 고무함지
,