록 삼총사와 친구들 II
heap 메모리 영역에 바이트 단위로 메모리를 할당하는 함수입니다. 단지 메모리만 잡을 뿐, 절대로 데이터를 초기화하지 않습니다. 여기부터 저기까지 사용하겠다고 메모리를 확보하기만 한 것입니다. 그래서 사용하기 전에 값을 대입하거나 초기화 해야 합니다. 만약에 메모리가 부족하여 공간이 할당되지 않는 등 문제가 발생하게 되면 널 포인터를 리턴 합니다. 아래에 malloc() 함수를 제대로 사용하는 멋진 예제가 있습니다.
#include <stdio.h>
#include <stdlib.h>
/*
* copyleft (l) 2006 - 2017 programmed by Sinclair
*/
// prototypes
#ifdef __ERROR___
int getIntMem (int * , size_t) ;
#endif
int * getIntMem0(size_t) ;
int new(int ** , size_t) ;
int getMemory(void ** , size_t) ;
int getMemory2(void * , size_t) ;
// 진짜루 멋지게 메모리 동적 할당하기 - 함수로 만들어서 이쁘게 초기화하기..
// 사실 malloc() 함수는 OOP언어에서의 new 연산자와 같은 역할을 한다.
int main() {
int * ip ; // 싱글 포인터니까.. 한번만 초기화하면 된다..
double ** ipp ; // 어랏? 이중 포인터네 당근 두 번 초기화해야겠지?
int i , j , tag ;
#ifdef __JUST_AT_SCHOOL___
ip = (int *) malloc(sizeof(int) * 10) ;
#endif
#ifdef __SCORE30___
if( !( ip = (int*) malloc(sizeof(int) * 10) ) ) {
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
exit(-1) ; // return -1 ;
} // end if
#endif
#ifdef __MEMTEST___
// 이 멋진 코드를 함수로 만들어 사용할 수 있다면 을매나 좋을꼬?
tag = 0 ;
while( !( ip = (int*) malloc(sizeof(int) * 10) ) ) {
// free(ip) ;
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
usleep(300) ; // 300 밀리 초 동안 쉬었다가 다시 잡는다...
if( ++ tag > 5 )
exit(-1) ; // main() 일 경우에만 return -1과 비슷하다
} // end while
// 이러코롬 잡으면 int ip[10] 이런 효과가 난다..
#endif
// int * ip 에 함수로 메모리 동적 할당해 보기
// getIntMem(ip , 10) ; <- X
// new (&ip , 10) ; <- O
// ip = getIntMem0(10) ; <- O 되긴 하죠~ 그러나 너무 없어 보인다는 거~
if( getMemory2(&ip , sizeof(int) * 10) ) { // free(ip) ;
return -1 ;
} // end if
// 이제 int ip[10] ; 와 같은 효과가 난다.
//double ** ipp에 함수로 메모리 동적 할당하기
if( getMemory2(&ipp , sizeof(double*) * 3) ) { // free(ipp) ;
free(ip) ;
return -1 ;
} // end if
// 여기서 한 번 잡고.. 와우~ double*가 세 개나 생겼네?
// *ipp == ipp[0] , *ipp의 타입이 모죠?
for ( i = 0 ; i < 3 ; ++i ) {
if( getMemory2(ipp+i , sizeof(double) * 4) ) {
// == getMemory2(&ipp[i] , sizeof(double) * 4)
// free(ipp[i])
register int j ;
for( j = --i ; j >= 0 ; ) free(ipp[j--]) ;
// for( j = i ; j > 0 ; ) free(ipp[--j]) ;
free(ipp) ;
free(ip) ; // 앞에서 잡혔으니까 여기까지 와 있죠?
return -1 ;
} // end if
} // end for
// 또 한 번 잡으니 이제 double ipp[3][4] ; 와 같은 효과가 난다.
// 매번 잡을 때 마다 이렇게 하는 거 별로 옳지 않아요~
// do something with ipp... 아싸~ 그냥 배열처럼 쓰면 되요~
for ( i = 0 ; i < 3 ; i ++ ) {
for ( j = 0 ; j < 4 ; j++ ) {
ipp[i][j] = (rand() % 100) / 7.0 ;
} // end inner for
} // end outer for
// 오늘날 포인터가 사랑스러운 분들은 이렇게 써 주시오며~
for ( i = 0 ; i < 3 ; i ++ ) {
for ( j = 0 ; j < 4 ; j++ ) {
printf("%f\t" , *(*(ipp+i)+j));
} // end inner for
putchar('\n') ;
} // end outer for
// do something with ip
for ( i = 0 ; i < 10 /* 반다시 꼭꼭!! 위에서 잡은 사이즈 */ ; ++i ) {
printf("%5d" , ip[i] = rand() % 100) ;
} // end for
putchar('\n') ;
free(ip) ; // 여기는 록 삼총사 한번 불렀으니 한번만 자유롭게~~ 저 하늘을~
for (i = 3 ; i > 0 ; ) {
free(ipp[--i]); // for루프로 록 삼총사 불렀으니 똑같이
} // end for
free(ipp) ; // 항상 록 삼총사 부른 역순으로 자유롭게
return 0 ;
} // end main()
#ifdef __ERROR___
int getIntMem (int * ip , size_t size) {
register int tag = 0 ;
while( ! ( ip = (int*) malloc(sizeof(int) * size) ) ) {
// free(ip) ;
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
sleep(1) ; // 1초 동안 쉬었다가 다시 잡는다...
if( ++tag > 5 )
return -1 ;
} // end while
// 이러코롬 잡으면 int ip[10] 이런 효과가 난다고 하는뎅 이거 왜 이래?
return 0 ;
} // end getIntMem()
#endif
// 초기 버전 범용 함수
int getMemory(void ** p , size_t size) {
register tag = 0 ;
while( !( *p = (void*)malloc(size) ) ) {
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
sleep(1) ; // 1초 동안 쉬었다가 다시 잡는다...
if( ++tag > 5 )
return -1 ;
} // end while
return 0 ;
} // end getMemory()
// 완벽 구현 범용 함수~ 모든 포인터에 메모리 다 때려 잡을 수 있는 막강 파워
#define M_USLEEP_TIME 300
#define M_DOING_AGAIN 5
#define M_ERROR_MSG "돈이 엄써서 메모리도 몬사고.. 죄송합니데이.."
int getMemory2(void * p , size_t size) {
register tag = 0 ;
while( !( *(char**)p = (char*) malloc(size) ) ) {
perror(M_ERROR_MSG) ;
// == stderr을 사용하는 puts()
if( ++tag > M_DOING_AGAIN ) return -1 ;
usleep(M_USLEEP_TIME) ; // 0.3ms 동안 쉬었다가 다시 잡는다.
} // end while
return 0 ;
} // end getMemory2()
int new(int ** ip , size_t size) {
register tag = 0 ;
while( !( *ip = (int*) malloc(sizeof(int) * size) ) ) {
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
sleep(1) ; // 1초 동안 쉬었다가 다시 잡는다...
if( ++tag > 5 )
return -1 ;
} // end while
// 이러코롬 잡으면 int ip[size] 이런 효과가 난다..
return 0 ;
} // end new()
int * getIntMem0(size_t size) {
int * ip ;
register tag = 0 ;
while( !( ip = (int*) malloc(sizeof(int) * size) ) ) {
perror("돈이 엄써서 메모리도 몬사고.. 죄송합니데이..") ;
// == stderr을 사용하는 puts()
sleep(1) ; // 1초 동안 쉬었다가 다시 잡는다...
if( ++tag > 5 )
return NULL ;
} // end while
// 이러코롬 잡으면 int ip[size] 이런 효과가 난다..
return ip ;
} // end getIntMem0()
getMemory2() 함수는 어떤 타입이건, 심지어 이중 포인터건 삼중 포인터건 관계없이 모두 메모리 할당 해주는 범용 함수가 되었습니다. 보통 단일 포인터의 경우 메모리 할당 후에 그냥 일차원 배열처럼 사용하거나, 캐스트 연산자를 통해 조금 변형시키면 다양한 다차원 배열처럼 사용할 수도 있습니다. 몇 차원이건 배열들은 모두 메모리를 한 곳에 모아서 할당하기 때문에 가능합니다. 하지만 이중 포인터부터는 대부분 연속된 메모리 공간에 할당할 수 없습니다. 이중 포인터로 할당한 공간을 사용할 때 수식으로는 이차원 배열처럼 사용하는 것이 가능하지만 앞서 이차원 배열을 일차원 배열처럼 사용한 것과 같은 효과를 낼 수는 없습니다. 데이터 묶음 별로 연속된 같은 메모리 공간에 할당된다는 것을 보장할 수 없기 때문입니다. 이런 문제들 때문에 꼭 필요한 경우를 제외하면 단일 포인터로 한번에 잡아서 그 영역을 분할하여 사용하는 것이 보다 효율적이라고 할 수 있습니다. 그리고 단일 포인터에 한번에 동적 할당하는 것 보다 이중 포인터로 분할하여 할당하는 방법이 메모리를 조금 더 사용하게 되며 잦은 함수 호출로 메모리 할당 속도 또한 느려지게 됩니다. 단일 포인터와 이중 포인터의 동적 할당에 대한 내용을 그림으로 살펴 보면 다음과 같습니다.
배열과 단일 포인터 그리고 이중 포인터의 메모리를 비교하는 예제를 작성해 보면 다음과 같습니다.
#include <stdio.h>
#include <stdlib.h>
/*
* copyleft (l) 2006 - 2017 programmed by Sinclair
*/
// prototypes
extern int getMemory2(void * , size_t) ;
extern int getMemory3(register void * , size_t , size_t , size_t) ;
// calloc()함수 부분에 보면 작성되어 있습니다. 그걸 사용하겠습니다.
int main() {
int array[12] = { 1,2,3,4,5,6,7,8,9,10,11,12 } ;
int matrix[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 } ;
int * pointer ;
int ** ppointer ;
int i , j ;
if( getMemory2(&pointer , sizeof(int) * 12) ) {
return -1 ;
} // end if
if( getMemory3(&ppointer , 3 , 4 , sizeof(int)) ) {
free(pointer) ;
return -2 ;
} // end if
puts("&array[i]:array[i]") ;
for( i = 0 ; i < 12 ; i++ )
printf("%u :%5d " , &array[i] , array[i]) ;
putchar('\n') ;
for( i = 0 ; i < 3 ; i++ ) {
for( j = 0 ; j < 4 ; j++ ) {
printf("%u :%5d " , &array[i * 4 + j] ,
((int(*)[4])array)[i][j]) ;
} // end inner for
putchar('\n') ;
} // end outer for
puts("&matrix[i][j]:matrix[i][j]") ;
for( i = 0 ; i < 3 ; i++ ) {
for( j = 0 ; j < 4 ; j++ ) {
printf("%u :%5d " , &matrix[i][j] , matrix[i][j]) ;
} // end inner for
putchar('\n') ;
} // end outer for
for( i = 0 ; i < 12 ; i++ )
printf("%u :%5d " , &matrix[0][i] , ((int*)matrix)[i]) ;
putchar('\n') ;
puts("pointer+i:*(pointer+i)") ;
for( i = 0 ; i < 12 ; i++ )
printf("%u :%5d " , pointer+i , pointer[i] = i * 100) ;
putchar('\n') ;
for( i = 0 ; i < 3 ; i++ ) {
for( j = 0 ; j < 4 ; j++ ) {
printf("%u :%5d " , pointer + i * 4 + j ,
((int(*)[4])pointer)[i][j]) ;
} // end inner for
putchar('\n') ;
} // end outer for
// 마치 이차원 배열처럼 제대로 동작함
puts("*(ppointer+i) + j : *(*(ppointer+i)+j)") ;
for( i = 0 ; i < 3 ; i++ ) {
for( j = 0 ; j < 4 ; j++ ) {
printf("%u :%5d " , ppointer[i] + j , // ==*(ppointer+i)+j
ppointer[i][j] = (i*4+j)*100 ) ;
} // end inner for
putchar('\n') ;
} // end outer for
// 이차원 배열처럼 절대 동작 안 함, 오류발생<<<
for( i = 0 ; i < 12 ; i++ )
printf("%u :%5d " , *(ppointer+i) + j ,
((int*)ppointer)[i]) ;
putchar('\n') ;
free(pointer) ;
for(i = 3 ; i > 0 ; )
free(ppointer[--i]) ;
free(ppointer) ;
} // end main()
#Sinclair #씽클레어 #싱클레어 #씽클레어도씨 #씨언어 #씨프로그래밍 #C언어 #Cprogramming #C_Programming #C #Programming #Clanguage #C_Language