'Language/C'에 해당되는 글 66건

  1. 2012.01.16 multicast(receive)
  2. 2012.01.16 multicast(send)
  3. 2012.01.11 포인터, 배열, 구조체 연산
  4. 2012.01.05 html 태그 제거
  5. 2012.01.05 C로 정규식라이브러리 사용
  6. 2012.01.05 패턴 매칭 ( Pattern Matching )
  7. 2012.01.05 C 정규식
  8. 2011.12.29 access() 파일 존재나 접근 권한을 확인합니다.
  9. 2011.12.02 void형 포인터
  10. 2011.12.02 이중 포인터의 필요성

multicast(receive)

Language/C 2012. 1. 16. 09:09
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAX_LEN  1024

int main(int argc, char *argv[])
{
	int sock;
	int flag_on = 1;
	struct sockaddr_in multicast_addr;
	char message_received[MAX_LEN+1];
	int msgrecv_len;
	struct ip_mreq mc_req;
	char* multicast_ip;
	unsigned short multicast_port;
	struct sockaddr_in from_addr;
	unsigned int from_len;

	if (argc != 3)
	{
		fprintf(stderr, "Usage: %s Multicast_IP Multicast_Port\n", argv[0]);
		exit(1);
	}

	multicast_ip = argv[1];      /* arg 1: multicast ip address */
	multicast_port = atoi(argv[2]);    /* arg 2: multicast port number */

	if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
	{
		perror("socket() failed");
		exit(1);
	}

	/* set reuse port to on to allow multiple binds per host */
	if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag_on,
	sizeof(flag_on))) < 0)
	{
		perror("setsockopt() failed");
		exit(1);
	}

	/* construct a multicast address structure */
	memset(&multicast_addr, 0, sizeof(multicast_addr));
	multicast_addr.sin_family      = AF_INET;
	multicast_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	multicast_addr.sin_port        = htons(multicast_port);

	/* bind to multicast address to socket */
	if ((bind(sock, (struct sockaddr *) &multicast_addr, sizeof(multicast_addr))) < 0)
	{
		perror("bind() failed");
		exit(1);
	}

	/* construct an IGMP join request structure */
	mc_req.imr_multiaddr.s_addr = inet_addr(multicast_ip);
	mc_req.imr_interface.s_addr = htonl(INADDR_ANY);

	/* send an ADD MEMBERSHIP message via setsockopt */
	if ((setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mc_req, sizeof(mc_req))) < 0)
	{
		perror("setsockopt() failed");
		exit(1);
	}

	while(1)
	{
		memset(message_received, 0, sizeof(message_received));
		from_len = sizeof(from_addr);
		memset(&from_addr, 0, from_len);

		/* block waiting to receive a packet */
		if ((msgrecv_len = recvfrom(sock, message_received, MAX_LEN, 0, (struct sockaddr*)&from_addr, &from_len)) < 0)
		{
			perror("recvfrom() failed");
			break;
		}

		printf("Received %d bytes from %s: ", msgrecv_len,
		inet_ntoa(from_addr.sin_addr));
		printf("%s", message_received);
	}

	/* send a DROP MEMBERSHIP message via setsockopt */
	if ((setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &mc_req, sizeof(mc_req))) < 0)
	{
		perror("setsockopt() failed");
		exit(1);
	}
	close(sock);
}

'Language > C' 카테고리의 다른 글

inet_ntoa 64bit 사용시 에러 대처방법  (0) 2012.02.02
FILE, fopen, fprintf, fflush, fclose  (0) 2012.01.30
multicast(send)  (0) 2012.01.16
포인터, 배열, 구조체 연산  (0) 2012.01.11
html 태그 제거  (0) 2012.01.05
:

multicast(send)

Language/C 2012. 1. 16. 09:09
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LEN  1024

int main(int argc, char *argv[])
{
	int sock;
	char message_to_send[MAX_LEN];
	unsigned int send_len;
	char* multicast_ip;
	unsigned short multicast_port;
	unsigned char multicast_ttl=1;
	struct sockaddr_in multicast_addr;

	if (argc != 3)
	{
		fprintf(stderr, "Usage: %s Multicast_IP Multicast_Port\n", argv[0]);
		exit(1);
	}

	multicast_ip = argv[1];       /* arg 1: multicast IP address */
	multicast_port     = atoi(argv[2]); /* arg 2: multicast port number */

	/* create a socket */
	if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
	{
		perror("Socket creation failed");
		exit(1);
	}

	/* set the TTL (time to live/hop count) for the send */
	if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &multicast_ttl, sizeof(multicast_ttl))) < 0)
	{
		perror("setsockopt() failed");
		exit(1);
	}

	memset(&multicast_addr, 0, sizeof(multicast_addr));
	multicast_addr.sin_family      = AF_INET;
	multicast_addr.sin_addr.s_addr = inet_addr(multicast_ip);
	multicast_addr.sin_port        = htons(multicast_port);

	printf("Type the message below (Press Enter to send, ctrl-C to quit):\n");

	memset(message_to_send, 0, sizeof(message_to_send));

	while (fgets(message_to_send, MAX_LEN, stdin))
	{
		send_len = strlen(message_to_send);

		if ((sendto(sock, message_to_send, send_len, 0,
		(struct sockaddr *) &multicast_addr,
		sizeof(multicast_addr))) != send_len)
		{
			perror("Error in number of bytes");
			exit(1);
		}

		memset(message_to_send, 0, sizeof(message_to_send));
	}

	close(sock);

	exit(0);
}

'Language > C' 카테고리의 다른 글

FILE, fopen, fprintf, fflush, fclose  (0) 2012.01.30
multicast(receive)  (0) 2012.01.16
포인터, 배열, 구조체 연산  (0) 2012.01.11
html 태그 제거  (0) 2012.01.05
C로 정규식라이브러리 사용  (0) 2012.01.05
:

포인터, 배열, 구조체 연산

Language/C 2012. 1. 11. 16:42

[포인터 연산]

(일반 배열)
p[i]
*(p+i)

(구조체 배열)
p[i].idx
*(p+i).idx
(p+i)->idx


소스
/*
 ============================================================================
 Name        : pointer.c
 Author      : 
 Version     :
 Copyright   : 
 Description : pointer in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <string.h>
struct mystruct{
char str[10];
int ii;
};

int main(void)
{
struct mystruct mystruct;
struct mystruct* pmystruct;
pmystruct=&mystruct;

strcpy(mystruct.str,"test");
mystruct.ii=45;
printf("mystruct.str: %s\n",mystruct.str);
printf("mystruct: %d\n",mystruct.str);
struct mystruct* str = mystruct.str;
printf("str->ii: %d\n",str->ii);
printf("\n");

printf("mystruct.ii: %d\n",mystruct.ii);
printf("pmystruct->ii: %d\n",pmystruct->ii);
printf("pmystruct[0].ii: %d\n",pmystruct[0].ii);
printf("(pmystruct+0)->ii: %d\n",(pmystruct+0)->ii);
printf("\n");

printf("mystruct: %d\n",mystruct);
printf("&mystruct: %d\n",&mystruct);
printf("pmystruct: %d\n",pmystruct);
printf("\n");

int arrint[5]={1,2,3,4,5};
int* parrint = arrint;
printf("arrint: %d\n",arrint);
printf("arrint[0]: %d\n",arrint[0]);
printf("parrint[0]: %d\n",parrint[0]);
printf("*(parrint): %d\n",*(parrint));
printf("*(arrint): %d\n",*(arrint));

return 0;
}



결과
mystruct.str: test
mystruct: 2293600
str->ii: 45

mystruct.ii: 45
pmystruct->ii: 45
pmystruct[0].ii: 45
pmystruct+0.ii: 45

mystruct: 1953719668
&mystruct: 2293600
pmystruct: 2293600

arrint: 2293552
arrint[0]: 1
parrint[0]: 1
*(parrint): 1
*(arrint): 1

 

'Language > C' 카테고리의 다른 글

multicast(receive)  (0) 2012.01.16
multicast(send)  (0) 2012.01.16
html 태그 제거  (0) 2012.01.05
C로 정규식라이브러리 사용  (0) 2012.01.05
패턴 매칭 ( Pattern Matching )  (0) 2012.01.05
:

html 태그 제거

Language/C 2012. 1. 5. 08:30
$search = array ("'<SCRIPT[^>]*?>.*?'si", // 자바 스크립트 제거 
                    "'<[\/\!]*?[^<>]*?>'si", // HTML 태그 제거 
                    "'<\!\-\-(.*)?\-\->'si", //주석제거 
                    "'([\r\n])[\s]+'", 
                    "'&(quot|#34);'i", // HTML 엔티티 치환 
                    "'&(amp|#38);'i", 
                    "'&(lt|#60);'i", 
                    "'&(gt|#62);'i", 
                    "'&(nbsp|#160);'i", 
                    "'&(iexcl|#161);'i", 
                    "'&(cent|#162);'i", 
                    "'&(pound|#163);'i", 
                    "'&(copy|#169);'i", 
                    "'&#(\d+);'e"); // php로 실행 
   $replace = array ("", "", "", "\\1", "\"", "&", "<", ">", " ", chr(161), chr(162), chr(163), chr(169), "chr(\\1)"); 
   $body = preg_replace($search,$replace,$body);

  1. <>태그형태를 제거하는 정규표현식
String title = title.replaceAll("<(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?>", "");


2. &lt;나 &gt;태그를 제거하는 정규표현식
String title = title.replaceAll("&lt(;)?(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?&gt(;)?", "");


1    <?php  
2   // $document should contain an HTML document.  
3   // This will remove HTML tags, javascript sections  
4   // and white space. It will also convert some  
5   // common HTML entities to their text equivalent.  
6   $search = array ("'<script[^>]*?>.*?</script>'si",  // Strip out javascript  
7                    "'<[/!]*?[^<>]*?>'si",          // Strip out HTML tags  
8                    "'([rn])[s]+'",                // Strip out white space  
9                    "'&(quot|#34);'i",                // Replace HTML entities  
10                    "'&(amp|#38);'i",  
11                    "'&(lt|#60);'i",  
12                    "'&(gt|#62);'i",  
13                    "'&(nbsp|#160);'i",  
14                    "'&(iexcl|#161);'i",  
15                    "'&(cent|#162);'i",  
16                    "'&(pound|#163);'i",  
17                    "'&(copy|#169);'i",  
18                    "'&#(d+);'e");                    // evaluate as php  
19   $replace = array ("",  
20                    "",  
21                    "1",  
22                    """,  
23                    "&",  
24                    "<",  
25                    ">",  
26                    " ",  
27                    chr(161),  
28                    chr(162),  
29                    chr(163),  
30                    chr(169),  
31                    "chr(xxx1)"); // remove the "xxx" - this is just for showing the source  
32   $text = preg_replace($search, $replace, $document);  
33   ?>
 

'Language > C' 카테고리의 다른 글

multicast(send)  (0) 2012.01.16
포인터, 배열, 구조체 연산  (0) 2012.01.11
C로 정규식라이브러리 사용  (0) 2012.01.05
패턴 매칭 ( Pattern Matching )  (0) 2012.01.05
C 정규식  (0) 2012.01.05
:

C로 정규식라이브러리 사용

Language/C 2012. 1. 5. 08:24
     #include <stdio.h>
          #include <regex.h>
          #include <stdlib.h>
          
          void match_print(const char *buf, int start, int end)
          {
                  int i,j;
                  fprintf(stderr,"|");
                  for( i=0;i<end;i++ )
                          if( i >= start )
                                  fprintf(stderr,"%c", buf[i] );
                  fprintf(stderr,"|\n");
          }
          
          int main()
          {
                  int i;
                  int result;
                  int start=0, end=0;
                  char *str = "A WikiWikiWeb is a collaborative hypertext environment, with an emphasis on easy access to and modification of information.";
                  regex_t *compiled;
                  regmatch_t *matchptr;
                  size_t nmatch;
          
                  if( (compiled = (regex_t*)malloc(sizeof(regex_t))) == NULL ) {
                          fprintf(stderr, "regex_t malloc error\n" );
                          exit(-1);
                  }
          
                  if( regcomp( compiled, "[^ ]*t", REG_EXTENDED | REG_ICASE ) != 0 ) {
                          fprintf(stderr, "regcomp error\n" );
                          exit(-1);
                  }
          
                  nmatch = compiled->re_nsub+1;
          
                  if( (matchptr = (regmatch_t*)malloc(sizeof(regmatch_t)*nmatch)) == NULL ) {
                          fprintf(stderr, "regmatch_t malloc error\n" );
                          exit(-1);
                  }
          
                  while( (result = regexec( compiled, str+start, nmatch, matchptr, 0)) == 0 ) {
                          match_print( str, start+matchptr->rm_so, start+matchptr->rm_eo );
                          start += matchptr->rm_eo;
                  }
          
                  regfree( compiled );
          }

'Language > C' 카테고리의 다른 글

포인터, 배열, 구조체 연산  (0) 2012.01.11
html 태그 제거  (0) 2012.01.05
패턴 매칭 ( Pattern Matching )  (0) 2012.01.05
C 정규식  (0) 2012.01.05
access() 파일 존재나 접근 권한을 확인합니다.  (0) 2011.12.29
:

패턴 매칭 ( Pattern Matching )

Language/C 2012. 1. 5. 08:23

16 패턴 매칭 ( Pattern Matching )

GNU C 라이브러리는 두 종류의 형태(pattern)를 조화시키기 위한 도구를 제공한다: 보통의 표현들과 파일-이름 와일드카드. 라이브러리는 또한, 쉘이 하는 방법대로 변수를 확장하고 명령을 참조하고, 단어로 텍스트를 구문 분석하는 그러한 도구들을 제공한다.


16. 1 와일드카드 매칭 ( Winldcard Matching )

이 절은 특별한 문자열에 대응하여 와일드카드(wildcard) 패턴과 어떻게 매치할 것인지를 설명한다. 그 결과는 예, 또는 아니오 의 대답이다: 그 문자열이 패턴과 맞는지 틀린지. . . 여기에 설명된 모든 기호(sysbols)들은 모두 'fnmatch. h/에 선언되어 있다.

함수 : int fnmatch (const char *pattern, const char *string, int flags)

이 함수는 문자열 string 이 패턴 pattern 과 맞는지, 어떤지를 시험한다. 만일 그들이 서로 맞으면 0을 반환하고; 그렇지 않으면 영이 아닌 값 FNM_NOMATCH 를 반환한다. pattern 과 string인수들은 둘다 문자열이다. flags 인수는 세밀한 매칭(matchin)을 하기 위한 플래그 비트들의 조합니다. 정의된 플래그들은 아래를 참조하라.

GNU C 라이브러리에서, fnmatch는 "에러"를 발생시킬 수 없다. 그것은 항상 그 둘이 서로 맞는지에 대한 대답을 반환하기 때문이다. 그렇지만, fnmatch에서 파생된 다른 함수들은 때때로 "에러"를 보고할 것이다. 그들은 FNM_NOMATCH 과는 다른 0이 아닌 값을 반환함으로써 에러를 보고 할 것이다. 이들은 flags 인수에서 사용되는 유용한 플래그들이다.

FNM_FILE_NAME

파일이름과 맞추는 동안, '/' 문자를 특별히 취급하라. 만일 이 플래그가 설정되면, 패턴안의 와이드카드 구성들은 문자열에 있는 '/'과 매치할 수 없다. 그래서, '/'과 매치하기 위한 유일한 방법은 패턴안에 '/'를 명시하는 것이다.

FNM_PATHNAME

이것은 FNM_FILE_NAME 의 다른 이름으로; POSIX. 2로부터 왔다. 우리는 파일 이름으로 "경로이름"을 사용하지 않기 때문에 이 이름을 쓰는 것을 권장하지 않는다.

FNM_PERIOD

만일 문자열의 처음에 '. ' 문자가 나타나면 특별하게 취급하라. 만일 이 플래그가 설정되면, 패턴안의 와일드카드 구성은 첫 번째 문자가 '. '로 시작되는 문자열에서 '. '문자와 매치할 수 없다. 만일 당신이 FNM_PERIOD 와 FNM_FILE_NAME 을 모두 설정해 놓았다면, 문자열의 처음에 나타나는 '. ' 은 물론 '. '문자 다음에 나타나는 '/'문자도 특별한 취급을 한다. (쉘은 FN, _PERIOD 와 FNM_FILE_NAME 플래그를 파일이름과 매치하기 위해서 함께 사용한다. )

FNM_NOESCAPE

패턴안에 있는 '\'문자를 특별하게 취급하지 말아라. 보통, '\'가 바로 뒤에 따르는 문자를 인용하면, 원래 그 문자가 갖는 특별한 의미가 무효화된다. 인용이 가능했을 때, 패턴 '\?'는 패턴안의 의문부호가 보통의 문자처럼 행동하기 때문에 '?'과 매치된다. 만일 당신이 FNM_NOESCAPE을 사용하면, '\'도 보통의 문자가 된다.

FNM_LEADING_DIR

문자열에서 '/'뒤에 나타나는 문자들의 열을 무시하라; 이것은, 패턴과 매치되는 디렉토리 이름으로 시작하는 문자열인지를 시험함을 말한다. 만일 이 플래그가 설정되면, 패턴이 'foo*' 이거나 'foobar' 이면, 문자열 'foobar/frobozz' 과 매치될 것이다.

FNM_CASEFOLD

패턴에 비교되는 문자열의 대, 소문자 구분을 무시하라.


16. 2 Globbing

와일드카드의 전형적인 사용은 디렉토리 안에 있는 파일들과 맞추어서 매치되는 모든 파일들의 리스트를 만들기 위함이다. 이것은 globbing이라고 부른다.

당신은 fnmatch 를 사용해서 하나씩 디렉토리 엔트리들(entries)을 읽고, 테스트함으로써 이런 일을 할 수가 있다. 그렇지만, 속도가 느리다. (그리고 직접 서브디렉토리들(subdirectories)을 다루기 때문에 복잡하다. )

라이브러리는 편리한 와일드카드를 특별하게 사용하도록 하기 위해 glob 함수를 제공한다. glob 과 이 절에 있는 다른 심볼들은 'glob. h'에 선언되어 있다.

16. 2. 1 glob 호출하기

globbing의 결과는 파일 이름들(문자열)의 벡터(vector)이다. 이 벡터를 반환하기 위해서, glob은 구조체인 golb_t 라는 특별한 데이터타입을 사용한다. 당신이 glob에게 구조체의 주소를 주면, glob은 그 구조체의 각 필드를 채워서 당신에게 그 결과를 알린다.

데이터타입 : glob__t

이 데이터타입은 벡터를 가리키는 포인터를 저장한다. 더 자세하게 말하자면, 그것은 벡터의 주소와 그 크기를 갖고있는 레코드이다.

gl_pathc 벡터 안에 있는 요소들의 개수

gl_pathv 벡터의 주소. 이 필드는 char ** 형을 갖는다.

gl_offs gl_pathv 필드 안에 있는 명목상의 주소로부터 구한, 벡터의 첫 번째 실 요소의 offset. 다른 필드들과 달리, 이것은 glob으로부터 나온 출력이 아니라, 항상 glob에서 입력으로 사용된다. 만일 당신이 0이 아닌 offset을 사용하면, 벡터의 시작점으로부터 다른 요소들은 비어있는 채로 왼쪽에 존재한다. ( glob 함수는 널 포인터로 그들을 채운다. ) gl_offs 필드는 당신이 GLOB_DOOFFS 플래그들 사용할 때만 의미가 있다. 그렇지 않다면, offset은 이 필드 안에 무엇이 있던지 상관없이 항상 0이고, 첫 번째 실 요소는 벡터의 시작점에 있다.

함수 : int glob (const char *pattern, int flags, int (*errfunc) (const char *filename, int error-code), glob_t *vector_ptr)

glob 함수는 현재의 디렉토리에서 패턴 pattern을 사용해서 globbing을 한다. 그것은 새로이 할당된 벡터에 그 결과를 넣고, *vector'ptr에 이 벡터의 주소와 크기를 저장한다. flags인수는 비트플래그들의 조합이다; 플래그들에 대한 상세한 정보는 16. 2. 2절 [Flags for Globbing] 를 참조하라.

globbing의 결과는 파일이름들의 문자열이다. glob 함수는 각 결과를 위해서 워드(word) 단위의 문자열을 할당하고, 이들 문자열의 주소를 저장하기 위해서 char ** 형의 벡터를 할당한다. 벡터의 마지막 요소는 널 포인터이다. 이 벡터는 워드 벡터(word vector)라고 불린다. 이 벡터를 반환하기 위해서, glob는 *vector'ptr에 그 주소와 길이를 (널 포인터로 끝나는 것을 세지않은, 요소들의 개수) 저장한다. 보통, glob는 그들을 반환하기 전에 알파벳순으로 파일 이름들을 정렬한다. 당신은 만일 당신이 가능한 한 빨리 그 정보를 얻기를 원한다면 GLOB_NOSORT 플래그를 사용해서 이 기능을 이용하지 않을 수 있다.

만일 당신이 알파벳순으로 파일들을 처리한다면, 보통은 glob가 정렬하도록 하는 것이 좋고, 당신이 만든 응용프로그램을 사용하는 사용자들은 프로그램에 대한 가치를 느낄 것이다.

glob 함수가 성공하면 0을 반환하고, 그렇지 않다면 그것은 다음 에러코드 중 하나를 반환한다.

    GLOB_ABORTED

    디렉토리 개방에서 에러가 있었고, 그리고 당신이 GLOB_ERR 플래그를 사용했거나 errfunc가 0이 아닌 값을 반환했다. GLOB_ERR 플래그와 errfunc 에 대한 설명은 아래를 참조하라.

    GLOB_NOMATCH

    pattern이 현존하는 파일들과 아무 것도 맞지 않는다. 만일 당신이 GLOB_NOCHECK 플래그를 사용하면, 이 에러코드는 결코 나오지 않는다. 왜냐하면 이 플래그는 적어도 한 개의 파일이 패턴과 맞는 것이 있는 것처럼 glob이 가장하도록 하기 때문이다.

    GLOB_NOSPACE

    그 결과를 저장하기 위한 메모리 할당이 불가능하다. 에러가 발생하면, glob는 지금까지 발견한 pattern과 매치되는 것을 *vector`ptr에 정보를 저장한다.

 

16. 2. 2 Globbing 을 위한 플래그들

이 절은 glob 에게 flags 인수로 지정할 수 있는 플래그들을 설명한다. 당신이 원하는 플래그를 선택하고, 비트별 OR 연산자 | 을 사용해서 그들을 조합하라.

GLOB_APPEND

앞서서 호출한 glob이 만들어낸 워드 벡터에 새로운 워드벡터를 붙인다. 이것은 그들 사이를 공백으로 연결해서 여러 워드들을 효율적으로 확장할 수 있다. 붙이는 작업을 하려면, glob 호출동안 구조체 워드 벡터의 내용을 변화시켜서는 안 된다. 그리고 만일 당신이 glob의 처음 호출에서 GLOB_DOOFFS 를 설정하면, 당신이 그 결과들을 붙일 때도 반드시 그것을 설정해야만 한다. gl_pathv 에 저장한 포인터는 당신이 두 번째 glob을 호출한 이후에는 더 이상 유용한 정보가 아님을 기억하라. 왜냐하면, glob이 그 벡터의 위치를 바꾸기 때문이다. 그러므로 항상 매번 glob을 호출한 후에 바로 구조체 glob_t 에서 gl_pathv를 추출하라; 호출을 거쳐 그 포인터를 결코 저장하지 말아라.

GLOB_DOOFFS

워드 벡터의 시작점에 빈 슬롯을 남겨라. gl_offs는 얼마나 많은 슬롯을 남겨야 하는지를 알린다. 빈 슬롯은 널 포인터를 저장한다.

GLOB_ERR

즉시 포기하고 순서대로 읽혀져야만 하는 디렉토리를 읽는데 어떤 어려움이 있다면, 그 에러를 보고하라. 그와같은 어려움 들은 당신이 필요한 접근을 갖지 못한 디렉토리를 포함했기 때문일 것이다. 보통, glob는 그 디렉토리가 무엇이든지, 어떤 에러에도 불구하고, 계속 실행하려 시도한다. 당신은 glob를 호출할 때 에러-처리 함수 errfunc를 정함으로 해서 이것보다 더 많은 제어를 실행할 수 있다. 만일 errfunc 가 널 포인터가 아니라면, glob는 디렉토리를 읽을 수 없을 때 즉시 실행을 멈추지 않고; 대신 다음처럼 두 개의 인수를 사용해서 errfunc 함수를 호출한다:
(*errfunc) (filename, error-code)
filename인수는 glob이 개방할 수 없거나, 읽을 수 없었던 디렉토리의 이름이고, error-code는 glob에서 보고된 에러 값이다. 만일 에러처리 함수가 영이 아닌 값을 반환하면, glob는 즉시 멈춘다. 그렇지 않다면 계속 실행한다.

GLOB_MARK

만일 pattern이 디렉토리 이름과 매치되면, 그것을 반환할 때, 디렉토리의 이름에 '/'를 덧붙여라.

GLOB_NOCHECK

만일 pattern이 어떤 파일 이름과도 매치가 되지 않으면, 매치되는 파일 이름이 있는 것처럼 pattern 그 자체를 반환한다. (보통, pattern이 어느것과도 매치가 안될 때, glob는 매치되는 것이 아무 것도 없음을 반환한다. )

GLOB_NOSORT

파일 이름들을 정렬하지 말아라; 특별한 순서없이 그들을 반환하다. ( 실제로, 그 순서는 디렉토리에 있는 엔트리의 순서에 의존할 것이다. ) 정렬하지 않는 유일한 이유는 시간을 절약하기 위함이다.

GLOB_NOESCAPE

pattern들에 있는 '\' 문자를 특별하게 취급하지 말아라. 보통, '\'가 그 다음에 나타나는 문자들을 인용해서, 그 문자들이 갖는 특별한 기능을 없앤다. 인용(quoting)이 가능할 때, 패턴 '\?'은 오직 문자열 '?'로 매치되는데, 그 이유는 pattern에 있는 의문부호가 보통의 문자처럼 행동하기 때문이다.
만일 당신이 GLOB_NOESCAPE를 사용하면, '\'은 보통의 문자가 된다. glob가 반복적으로 fnmatch 함수를 호출함으로써 그 작업을 한다. fnmatch의 호출에서 FNM_NOESCAPE가 켬으로 해서 GLOB_NOESCAPE 플래그를 취급한다.


16. 3 정규식 매칭 ( Matching )

GNU C 라이브러리는 정규식을 매치하기 위한 두 개의 인터페이스를 제공한다. 하나는 표준 POSIX2 인터페이스이고, 다른 하나는 GNU 시스템이 오랫동안 가지고 있었던 것이다.

두 개의 인터페이스는 헤더파일 'regex. h'에 선언되어 있다. 만일 당신이 _POSIX_C_SOURCE 라고 정의하면, 오직 POSIX. 2의 함수들, 구조체, 그리고 상수들로만 선언되어 진다.

 

16. 3. 1 POSIX 정규식 컴파일

당신이 실제로 정규식을 매치하기 전에, 당신은 그것을 컴파일해야만 한다. 이것은 실제로 우리가 생각하고 있는, 기계 명령어로 바꾸는 그런 실제의 컴파일이 아니라 특별한 데이터 구조체를 생성하는 것을 말한다. 그러나 그것은 당신은 그 패턴을 "실행"이 가능하도록 만들려 하는 목적을 갖고 있다는 점에서 보통의 컴파일과 같다. (컴파일된 일반 표현식을 어떻게 매치하는지에 대한 정보는 16. 3. 3절 [Matching POSIX Regexps] 참조. )

컴파일된 정규식을 위한 특별한 데이터 타입은 아래와 같다.

데이터타입 : regex__t

이것은 컴파일된 정규식을 저장하는 오브젝트의 타입이다. 그것은 실제로 구조체이다. 당신은 당신의 프로그램에서 밑에 보여진 오직 하나의 필드만 가진다.
re_nsub 이 필드는 컴파일된 정규식에 존재하는 괄호로 묶여진 부정규식(subexpressions)의 개수를 저장한다.
그 구조체에는 여러 가지 다른 필드들이 있지만, 우리는 여기서 그들을 설명하지 않겠다. 왜냐하면 다른 필드들은 우리가 임의대로 건드릴 수 없고, 오직 라이브러리 함수에서만 그들을 사용하기 때문이다. 당신이 regex_t 오브젝트를 만든 후에, 당신은 regcomp를 호출함으로써 정규식을 컴파일할 수 있다.

함수 : int regcomp (regex_t *compiled, const char *pattern, int cflags)

regcomp 함수는 문자열과 매치시키는데 사용하는 regexec를 사용할 수 있도록 정규식을 데이터 구조체로 "컴파일"한다. 컴파일된 정규식의 형식은 매칭(matching)에 효율적이게 만들어졌다. regcomp는 *compiled에 그것을 저장한다.
당신은 regex_t 타입의 오브젝트를 할당한 다음 그 주소를 regcomp에게 주어라. 인수 cflags는 정규식의 구문과 의미들을 제어하는 다양한 옵션을 정할 수 있도록 허용한다. 16. 3. 2절 [Flags for POSIX Regexps] 참조. 만일 당신이 REG_NOSUB플래그를 사용한다면, regcomp는 어떻게 부정규식이 실제로 매치되는지를 기록하기 위해 필요한 정보를 컴파일된 정규식에서 생략한다. 이 경우, 당신은 regexec를 호출할 때 matchptr 과 nmatch인수로 0을 사용하는 것이 좋을 것이다. 만일 당신이 REG_NOSUB 를 사용하지 않는다면, 컴파일된 정규식은 어떻게 부정규식을 매치하는지를 기록하는 용량을 갖는다. 또한, regcomp는 얼마나 많은 부정규식 패턴을 가졌는지를 compiled->re_nsub 에 저장하여 당신에게 알린다. 당신은 그것을 부정규식 매치에 대한 정보를 저장할 곳을 할당하기 위해 얼마나 긴 배열을 할당할지를 정하기 위한 값으로 사용할 수 있다.
regcomp는 정규식을 컴파일하는데 성공하면 0을 반환하고; 그렇지 않으면 0이 아닌 에러코드( 밑에 설명된 )를 반환한다. 당신은 그 에러코드의 발생이유를 설명할 에러메시지를 만들기 위해서는 regerror를 사용할 수 있다; 16. 3. 6절 [Regexp Cleanup] 참조. 이것들은 regcomp가 반환할 수 있는 0이 아닌 값들이다.

REG_BADBR

정규식안에 유용하지 않은 `\{. . . \}` 구성이 있었다. 유용한 `\{. . . }\` 구성은 단일한 숫자, 또는 콤마로 분리된 오름차순으로 된 두 개의 숫자 중 하나를 포함해야만 한다.

REG_BADPAT

정규식에 구문에러가 있었다.

REG_BADRPT

`?'나 `*'와 같은 반복 연산자가 나쁜 위치에 있다(선행하는 아무런 부표현식도 없이 ).

REG_ECOLLATE

정규식이 유용하지 않은 대조(collating) 요소를 참조하였다. (문자열 대조를 위해서 현재의 지역에서 정의되지 않은 것. ) 19. 3절 [Locale Categories] 참조. )

REG_ECTYPE

정규식이 유용하지 않은 클래스 이름을 참조하였다.

REG_EESCAPE

정규식이 `\' 으로 끝났다.

REG_ESUBREG

`\digit' 구성에 유용하지 않은 숫자가 있었다.

REG_EBRACK

정규식 안에 균형이 맞지 않는 sqrare brackets([, ])가 있었다.

REG_EPAREN

연장된 정규식이 균형이 맞지 않는 괄호를 갖었거나, 기본 정규식이 균형이 맞지 않는 `\(' 와 `\)'를 가졌다.

REG_EBRACE

정규식이 균형이 맞지 않는 `\{' 와 `\}'을 가졌다.

REG_ERANGE

범위 표현식에서 끝점의 하나가 유용하지 않다.

REG_ESPACE

regcomp가 메모리를 다 써버렸다.

 

16. 3. 2 POSIX 정규식을 위한 플래그들

regcomp로 정규식을 컴파일할 때 피연산자 cflags에서 사용할 수 있는 비트플래그들에 대한 설명이다.

REG_EXTENDED

기본 정규식이 아닌, 연장된 정규식으로 패턴을 취급하라.

REG_ICASE

문자들을 매치할 때 대, 소문자 구분을 무시하라.

REG_NOSUB

match_ptr 배열에 저장한 내용들을 건드리지 말아라.

REG_NEWLINE

문자열에 있는 새줄문자를 여러 개의 라인으로 문자열을 나누는 역할을 하는 것처럼 취급한다, 그래서 `$'은 새줄문자 전에 매치할 수 있고, `^'은 새줄문자 후에 매치할 수 있다. 또한, 새줄문자와 매치하기 위해 `. '을 허용하지 않고, `[^. . . ]'을 허용하지 않는다. 그렇지 않다면 새줄문자는 다른 보통의 문자들처럼 작용한다.

 

16. 3. 3 컴파일된 POSIX 정규식을 매칭하기

일단 당신이 16. 3. 1절 [POSIX Regexp Compilation] 에서 설명된 것처럼 컴파일된 정규식을 가졌다면, 당신은 regexec를 사용해서 문자열과 그것을 매치할 수 있다. 만일 정규식이 `^' 나 `$' 와 같은 고정문자들을 표함하고 있지 않다면, 문자열 안에 어디에서든지 매치되는 것은 성공으로 센다.

함수 : int regexec(regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr [], int eflags)

이 함수는 문자열을 컴파일된 정규식*compiled와 매치하려 시도한다. regexec 는 만일 정규식이 매치되면 0을 반환하고; 그렇지 않다면 0이 아닌 값을 반환한다. 0이 아닌 값들이 무엇을 의미하는지 밑 테이블에 설명해 놓았다.
당신은 0이 아닌 값이 발생하는 이유를 설명할 에러 메시지를 생성하도록 regerror 함수를 사용할 수 있다; 16. 3. 6절 [Regexp Cleanup] 참조. 인수 eflags는 다양한 옵션들을 가능하도록 하는 비트플래그들의 집합인 워드이다. 만일 당신이 문자열이 정규식과 매치된 곳이 실제로 어느 부분인지 또는 그 부표현식은 무엇인지를 알기를 원한다면 인수 matchptr 과 nmatch를 사용하라.
그렇지 않다면 nmatch에는 0을 주고 matchptr에는 NULL을 주어라. 16. 3. 4절 [Regexp Subexpressions] 참조. 당신이 정규식을 컴파일했을 때 그 컴파일된 정규식은 실제로 그것이 현재 위치한 지역에 맞도록 정규식을 매치해야만 한다. regexec 함수는 eflags 인수에서 다음 플래그들을 받아들인다.

REG_NOTBOL

한 라인의 시작이 정해진 문자열로 시작하는지를 신경 쓰지 말아라; 즉, 어떤 텍스트의 앞에 무엇이 선행해야만 한다는 어떤 가정도 만들지 말아라.

REG_NOTEOL

한 라인의 끝이 정해진 문자열로 끝나는지를 신경 쓰지 말아라; 즉, 어떤 텍스트의 뒤에 무엇이 따라와야만 한다는 어떤 가정도 만들지 말아라.

regexec가 반환할 수 있는 0이 아닌 값들에 대한 설명.

    REG_NOMATCH

    패턴이 문자열과 매치되는 것이 없다. 이것은 사실상 에러는 아니다.

    REG_ESPACE

    regexec가 메모리를 다 써버렸다.

 

16. 3. 4 부표현식 ( Subexpressions ) 과 매치한 결과

regexec함수가 패턴의 괄호로 묶인 부문자열을 매치할 때, 그것은 매치하려는 문자열의 부분들을 기록한다. 그것은 구조체 regmatch_t 형인 요소를 가진 배열 안의 offsets 에 저장하여 그 정보를 반환한다. 그 배열(index 0)의 첫 번째 요소는 전체 정규식과 매치된 문자열의 부분을 기록한다. 배열의 다른 요소들은 단 하나의 괄호로 묶인 부표현식과 매치된 부분의 처음과 끝을 기록한다.

데이터타입 : regmatch__t

이것은 당신이 regexec 함수에서 사용하기 위한 배열 matcharray의 데이터타입이다. 그것은 다음과 같은 두 개의 필드들을 갖고 있다.
rm_so 문자열에 있는 부문자열의 시작점의 offset. 그 부분의 주소를 얻기 위하여 문자열에 이 값을 더하라.
rm_eo 문자열에 있는 부문자열의 끝점의 offset

데이터타입 : regoff__t

regoff_t는 signed integer 형의 다른 이름이다. regmatch_t의 필드들은 regoff_t의 형을 갖는다. regmatch_t 요소들은 부표현식의 위치에 대한 것이다; 첫 번째 요소( index 1)는 매치된 첫 번째 부표현식이 어디에 있는지를 기록하고, 두 번째 요소는 두 번째 부표현식을 기록하고, 등등. 부표현식의 순서는 그들이 나타난 순서이다.

당신이 regexec를 호출할 때, 얼마나 긴 matchptr 배열이 있는지, nmatch 인수를 가지고 정해야한다. 이것은 regexec함수에게, 저장하기 위한 얼마나 많은 요소들이 있는지를 알린다. 만일 실제로 정규식이 nmatch보다 더 많은 부표현식을 갖는다면, 당신은 그들의 나머지에 대한 offset정보를 제대로 얻지 못한 것이다. 그러나 이것은 그 pattern이 어떤 특별한 문자열과 매치되는지 또는 매치되지 않는지에 대한 사실을 변경하지 않는다.

만일 당신이 regexec 함수가 매치된 부표현식에 대한 어떤 정보도 반환하기를 원하지 않는다면, 당신은 nmatch에 0을 넣거나, 또는 regcomp로 pattern을 컴파일할 때 REG_NOSUB 플래그를 사용하라.

 

16. 3. 5 부표현식 매치하기의 복잡함

때때로 부표현식은 아무 문자도 없는 부문자열(substring)을 매치한다. 이것은 'f\(o*)\'와 문자열 `fum'을 매치할 때 발생한다. (그것은 실제로는 단지 `f'를 매치한다. ) 이 경우, offsets의 양쪽은 널 부문자열이 발견된 곳을 가리킨다. 이 예에서, offsets은 둘다 1이다.

때때로, 전체 정규식은 전혀 부표현식을 사용하지 않고 매치할 수 있다. 예를 들어, `ba\(na\)*'이 'ba'와 매치할 때, 괄호안에 있는 부표현식은 사용되지 않는다. 이런 일이 발생할 때, regexec는 부표현식을 위한 요소의 필드에 -1을 저장한다.

때때로 전체 정규식을 매치하기는 한 번보다 더 많은 특별한 부표현식을 매치할 수 있다. 예를 들어, `ba\(na\)*'이 문자열 `bananana'와 매치할 때, 괄호안의 부표현식은 세 번 매치된다. 이런 일이 발생할 때, regexec는 보통, 부표현식과 매치된 문자열의 마지막 부분의 offsets을 저장한다. `bananana'의 경우에 이들의 offsets은 6과 8이다.

그러나 마지막으로 매치된 것은 항상 문자열의 마지막에 있는 매치된 것이 아니다. 그것은 매치하기 위한 마지막 기회에서 우선권을 가진 것이라고 하는 것이 더 정확하다. 이것이 의미하는 것은 하나의 부표현식이 다른 것에서 발견됐을 때, 내부 부표현식을 위해 보고된 결과는 밖의 부표현식과의 마지막 매치에서 무슨 일이 일어났는지에 대해 영향을 받는다. 예를 들어, `\(ba\(na\)*s \)*' 을 문자열 `bananas bas '와 매치하는 것을 고려해보자. 첫 번째, 내부 표현식과 실제로 매치된 것은첫 번째 단어의 끝에 가까이 있다. 그러나 다시 두 번째 단어를 고려해보면, 그곳에는 매치되는 것이 아무 것도 없다. regexec는 "na" 부표현식이 사용되지 않은 것으로 보고한다.

다른예로 `\(ba\(na\)*s \|nefer\(ti\)* \)*'을 'bananas nefertiti'와 매치할 때 이 규칙이 적용되는지를 알아보자. "na" 부표현식은 첫 번째 단어에서 매치되는 것이 있지만, 두 번째 단어에서는 매치되는 것이 없다. 다시 한번, 밖의 부표현식의 두 번 반복은 첫 번째를 무시하고, 두 번째 반복에서 "na" 부표현식은 사용되지 않았다, 그래서 regexec는 "na" 부표현식이 사용되지 않았음을 보고한다.

 

16. 3. 6 POSIX Regexp 매치하기 소거

당신이 컴파일된 정규식을 다 사용했을 때, 당신은 regfree를 사용해서 그 저장공간을 해제할 수 있다.

함수: void regfree (regex_t *compiled)

regfree를 호출해서 *compiled가 가리키고 있는 모든 저장영역을 해제할 수 있다. 이것은 이 매뉴얼에서는 설명하지 않았지만, 구조체 regex_t의 다양한 내부적 필드들을 포함하고 있다. regfree는 오브젝트 *compiled 자체를 해제하지 않는다.
당신은 다른 정규식을 컴파일하기 위해 그 구조체를 사용하기 전에 regfree를 사용해서 구조체 regex_t 안에 있는 공간을 항상 해제해야한다. regcomp나 regexec에서 에러가 났을 때, 당신은 에러메시지를 출력하기 위해서 regerror함수를 사용할 수 있다.

함수: size_t regerror (int errcode, regex_t *compiled, char *buffer, size_t length)

이 함수는 에러코드 errcode를 위한 에러메시지 문자열을 생성하고, buffer에서 시작하는 메모리의 length 바이트 안에 그 문자열을 저장한다. 컴파일된 인수를 위해서, regcomp나 regexec에서 작업되었던 같은 컴파일된 정규식 구조체를 공급한다. 선택적으로, 당신은 compiled를 위해서 NULL을 공급할 수 있다; 당신은 여전히 의미 있는 에러메시지를 얻을 것이다, 그렇지만, 그것은 상세하지 않을 것이다.
만일 그 에러메시지가 length 바이트의 길이에 맞을 수 없다면(널 종료문자를 포함해서), 그러면 regerror는 그것은 자른다. regerror 이 저장한 문자열은 심지어 그것이 잘렸을 때라도 널 종료문자를 저장한다. regerror의 반환값은 전체 에러메시지를 저장하기 위해 필요한 최소 길이이다. 만일 이것이 length보다 적다면, 에러메시지는 잘리지 않고, 당신은 그것을 사용할 수 있다. 그렇지 않다면, 당신은 더큰 버퍼를 잡아서 다시 regerror를 호출해야한다. 이곳에 regerror를 사용하는 함수가 있는데, 에러메시지를 위한 버퍼를 항상 동적으로 할당한다.
char *get_regerror (int errcode, regex_t *compiled)
{
size_t length = regerror (errcode, compiled, NULL, 0);
char *buffer = xmalloc (length);
(void) regerror (errcode, compiled, buffer, length);
return buffer;
}


16. 4 쉘-스타일 단어 확장

단어 확장은 단어들로 문자열을 분리하고, 그것을 쉘이 하는 것처럼 변수, 명령, 그리고 와일드카드로 해석하는 것을 의미한다. 예를 들어, 당신이 `ls -l foo. c'라고 쓸대, 이 문자열은 세단어, `ls', '-l', `foo. c'로 분리된다. 이것은 단어 확장의 가장 기본적인 함수이다.

당신이 `ls *. c'라고 쓸대, 이것은 단어 `*. c'가 어느 수의 파일이름들과 대치될 수 있기 때문에 많은 단어들이 될 수 있다. 이것은 와일드카드 확장이 호출되는데, 그것은 또한 단어 확장의 일부분이다. 당신이 당신의 경로를 프린트하기 위해서 `echo $PATH'를 사용할 때, 이것도 또한 단어 확장의 일부분인 변수 치환이 이용된다. 프로그램들은 라이브러리 함수 wordexp를 호출하여서 쉘처럼 단어 확장을 수행할 수 있다.

 

16. 4. 1 단어 확장의 단계

단어 확장이 단어들의 열(suquence)에 적용될 때, 그것은 여기에 보여진 순서를 따라서 변환을 수행한다.

1. 틸드(~) 확장: `~foo'는 `foo'의 홈 디렉토리로 대치된다.
2. 다음, 세 개의 다른 변환들은 왼쪽에서 오른쪽으로, 동등한 결합순서로 적용된다.
  • 변수 치환: 환경변수들은 `$foo'처럼 참조를 위해서 대치된다.
  • 명령 치환: ``cat foo`' 와 그와 동등한 `$(cat foo)'와 같은 것들은 내부 명령을 통해서 출력으로 대치된다.
  • 산술적 확장 : `$(($x-1))'과 같은 것은 산술적 계산의 결과로 대치된다.
3. 필드 분리하기 : 텍스트를 단어로 분리한다.
4. 와일드카드 확장 : `*. c'와 같은 구석은 `. c'로 끝나는 파일이름들의 리스트로 대치된다. 와일드카드 확장은 동시에 전체 단어로 적용되고, 그들 단어들이 있는 0개의 파일, 또는 많은 파일들로 대치한다.
5. 인용 제거: 문자열-인용의 제거, 그들은 적당한 때에 변환을 금지함으로써, 그들의 작업이 수행된다.

이들 변환들에 대한 상세한 정보, 그리고 그들을 사용한 구성들을 어떻게 쓸것인가에 대한 것들은 BASH 매뉴얼을 참조하라.

 

16. 4. 2 wprdexp 호출하기

단어 확장을 위한 모든 함수들, 상수들, 그리고 데이터타입들은 헤더파일 'wordexp. h'에 선언되어 있다. 단어 확장은 단어들(문자열)의 벡터를 생성한다. 이 벡터를 반환하기 위해서, wordexp는 구조체인 wordexp_t라는 특별한 데이터타입을 사용한다. 당신이 그 구조체를 wordexp함수에 주면, 그것은 그 결과를 알리기 위해서 그 구조체의 필드를 채운다.

데이터타입 : wordexp_t

이 데이터타입은 워드 벡터를 가리키는 포인터를 저장한다. 좀 더 자세하게 말하면 워드 벡터의 주소와 그 크기를 기록한다.
we_wordc : 벡터에 있는 요소들의 개수
we_wordv : 벡터의 주소. 이 필드는 char ** 타입을 갖는다.
we_offs : we_wordv 필드에 있는 명목상의 주소로부터 구한 벡터의 첫 번째 실 인수의 offset. 다른 필드들과 달리, 이 필드는 wordexp함수에서 항상 입력으로 사용된다. 만일 당신이 0이 아닌 offset을 사용하면, 벡터의 시작점에서 많은 요소들은 비어있는 왼쪽에 있다. (wordexp함수는 널 포인터로 그들을 채운다. ) we_offs 필드는 당신이 WRDE_DOOFFS 플래그를 사용했을 때만 유용하다. 그렇지 않다면, offset은 이 필드 안에 무엇이 있는지 상관없이 항상 0이고, 첫 번째 실인수는 벡터의 시작점에 있다.

함수: int wordexp (const char *words, wordexp_t *word-vector-ptr, int flags)

단어들로 이루어진 문자열에서 단어 확장을 수행하고, 그 결과를 새로이 할당된 벡터에 넣고, 그리고 이 벡터의 크기와 주소를 *word-vector-ptr에 저장한다. 인수 flags는 비트플래그들의 조합이다; 플래그들에 대한 상세한 정보는 16. 4. 3절 [Flags for Wordexp] 를 참조하라.
당신은, 그들이 인용되지 않았다면 문자열 안에 `|&; <>' 문자들을 사용하지 못한다; 새줄도 마찬가지. 만일 당신이 인용 없이 이들 문자들을 사용한다면, 당신은 WRDE_BADCHAR 에러코드를 얻을 것이다. 그들이 인용되지 않았거나 또는 단어 확장 구성의 부분이 아니라면 괄호나, 쌍으로 이루어진 것들을 사용하지 말아라. 만일 당신이 인용 문자들 `'"`' 을 사용한다면 그들은 균형이 맞는 한 쌍이 되어야 한다. 단어 확장의 결과는 단어들의 열이다.
wordexp함수는 결과로 나온 단어를 위한 문자열을 할당하고, 이들 문자열의 주소를 저장하기 위해서 char ** 타입의 벡터를 할당한다. 벡터의 마지막 요소는 널 포인터이다. 이 벡터는 워드벡터라 불린다. 이 벡터를 반환하기 위해서, wordexp는 그 주소와, 그 길이(종료 널 포인터를 셈하지 않은 요소들의 개수)를 *word-vector-ptr에 저장한다.
만일 wordexp가 성공하면 0을 반환하고, 그렇지 않으면 다음 에러코드 중 하나를 반환한다.

WRDE_BADCHAR

입력 문자열 단어들이 인용이 없는 `|'와 같이 유용하지 않은 문자를 포함하고 있다.

WRDE_BADVAL

입력 문자열이 정의되지 않은 쉘 변수를 참조하였고, 당신이 그와같은 참조를 금하기 위해서 WRDE_UNDEF 플래그를 사용하였다.

WRDE_CMDSUB

입력 문자열이 명령 치환을 사용했는데, 당신이 명령 치환을 금하기 위해서 WRDE_NOCMD 플래그를 사용하였다.

WRDE_NOSPACE

그 결과를 저장하기위한 메모리를 할당하기가 불가능하다. 이 경우, wordexp는 할당할 수 있는 만큼 그 결과의 일부를 저장할 수 있다.

WRDE_SYNTAX

입력 문자열에 구문에러가 있었다. 예를 들어 매치되지 않는 인용 문자가 구문에러이다.

함수: void wordfree (wordexp_t *word-vector-ptr)

*word-vector-ptr이 가리키고 있는 문자열과 벡터를 위한 저장공간이 해제된다. 이것은 *word-vector-ptr 자체를 해제하지 않고, 단지 그것이 가리키고 있는 데이터만을 해제한다.

 

16. 4. 3 단어 확장을 위한 플래그들

이 절은 wordexp에서 사용되는 flags인수로 지정할 수 있는 플래그들을 설명한다. 당신이 원하는 플래그들을 선택하고, 그들을 연산자 |을 사용해서 조합하라.

WRDE_APPEND

바로 전에 호출한 wordexp가 생성한 단어의 벡터에 이번 확장으로부터 생성된 단어들을 붙여라. 이것은 그들 사이가 공백으로 분리되어져서, 여러 단어들을 효율적으로 확장할 수 있는 방법이다. 덧붙이기 위한 일련의 작업에서, 당신은 wordexp의 호출사이에 워드 벡터 구조체의 내용을 갱신하지 말아야만 한다. 그리고, 만일 당신이 wordexp의 첫 번째 호출에 WRDE_DOOFFS를 설정하면 당신은 그 결과들을 붙일 때에도 또한 그것을 설정해야만 한다.

WRDE_DOOFFS

단어들의 벡터의 시작점에 빈 슬롯들을 남겨라. we_offs 필드는 얼마나 많은 슬롯들을 남겨야하는지를 알린다. 빈 슬롯들은 널 포인터를 포함한다.

WRDE_NOCMD

명령 치환을 하지 말아라. 만일 입력이 명령 치환을 요청하면, 에러를 출력한다.

WRDE_REUSE

앞서서 호출한 wordexp에서 만들어진 워드 벡터를 재사용 하라. 새로운 벡터를 할당하는 대신에 wordexp는 이미 존재하고 있는( 필요한 만큼 그것을 크게 만들어서) 벡터를 사용할 것이다. 그 벡터를 이동할지도 모른다는 것을 기억하라, 그래서 오래된 포인터를 저장하고, wordexp를 호출한 후에 그것을 다시 사용하는 것은 안정적이지 않다. 당신은 매번 호출마다 새로운 we_pathv를 추출해야만 한다.

WRDE_SHOWERR

명령 치환에 의해 만들어진 명령으로 실행된 결과로 나온 프린트된 에러메시지를 보여라. 더 정확히, 현재 프로세스의 표준 에러 출력 스트림을 계승하도록 이들 명령들에게 허용한다. 디폴트로, wordexp는 모든 출력을 버린 표준 에러 스트림을 이들 명령들에게 준다.

WRDE_UNDEF

만일 입력이 정의되지 않은 쉘 변수를 참조한다면 에러가 출력된다.

16. 4. 4 wordexp Example

wordexp를 사용해서 여러 문자열을 확장하고, 그 결과들을 쉘 명령을 실행하기 위해 사용하는 예제가 있다. 확장들을 분리하기 위해 사용한 WRDE_APPEND와 wordexp에 의해 할당된 공간을 해제하기 위한 wordfree의 사용도 보여주고 있다.

int
expand_and_execute (const char *program, const char *options)
{
wordexp_t result;
pid_t pid
int status, i;
/* 그 프로그램을 실행하기 위해서 문자열을 확장하라 */
switch (wordexp (program, &result, 0))
{
case 0: /* Successful. */
break;
case WRDE_NOSPACE:
/* 만일 그 에러가 WRDE_NOSPACE라면 아마도 결과의 일부분이 할당되어진다. */
wordfree (&result);
default:
return -1;
/* 어떤 다른 에러 */
}
/* 인수를 위해 정해진 문자열을 확장하라. */
for (i = 0; args[i]; i++)
{
if (wordexp (options, &result, WRDE_APPEND))
{
wordfree (&result);
return -1;
}
}
switch( pid = fork()) {
case -1 :
status = -1;
break;
case 0 : /* 이것은 자식 프로세스이다. 그 명령을 실행하라. */
execv (result. we_wordv[0], result. we_wordv);
exit (EXIT_FAILURE);
defautl : /* 이것은 부모 프로세스이다. 자식이 완수하도록 기다려라 */
if (waitpid (pid, &status, 0) != pid)
status = -1;
}
wordfree (&result);
return status;
}

실제로, wordexp는 서브쉘(subshell)로 실행되기 때문에, 문자열에서 그들 사이를 공백으로 분리하고 쉘 명령 `sh -c'를 사용해서 실행하는 것보다는 빠르다.

'Language > C' 카테고리의 다른 글

html 태그 제거  (0) 2012.01.05
C로 정규식라이브러리 사용  (0) 2012.01.05
C 정규식  (0) 2012.01.05
access() 파일 존재나 접근 권한을 확인합니다.  (0) 2011.12.29
void형 포인터  (0) 2011.12.02
:

C 정규식

Language/C 2012. 1. 5. 08:22

(1) ^ (caret)


라인의 처음이나 문자열의 처음을 표시
예 : ^aa (문자열의 처음에 aa를 포함하면 참, 그렇지 않으면 거짓)

(2) $ (dollar)

라인의 끝이나 문자열의 끝을 표시
예 : aaa$ (문자열의 끝에 aaa를 포함하면 참, 그렇지 않으면 거짓)

(3) . (period)

임의의 한 문자를 표시
예 : ^a.c (문자열의 처음에 abc, adc, aZc 등은 참, aa 는 거짓)
예 : a..b$ (문자열의 끝에 aaab, abbb, azzb 등을 포함하면 참)

(4) [] (bracket)

문자의 집합이나 범위를 나타냄, 두 문자 사이의 범위는 "-" 사용. 
[]내에서 "^"이 선행되면 not을 나타냄

예 : [abc] (a, b, c 중 어떤 문자, "[a-c]."과 동일)
예 : [Yy] (Y 또는 y)
예 : [A-Za-z0-9] (모든 알파벳과 숫자)
예 : [-A-Z]. ("-"(hyphen)과 모든 대문자)
예 : [^a-z] (소문자 이외의 문자)
예 : [^0-9] (숫자 이외의 문자)

(5) {} (brace)

{} 내의 숫자는 직전의 선행문자가 나타나는 횟수,범위를 나타냄
예 : a{3} ('a'의 3번 반복인 aaa만 해당됨)
예 : a{3,} ('a'가 3번 이상 반복인 aaa, aaaa,  ... 등을 나타냄) 
예 : a{3,5} (aaa, aaaa, aaaaa 만 해당됨) 
예 : ab{2,3} (abb와 abbb 만 해당됨) 
예 : [0-9]{2} (두 자리 숫자) 
예 : doc[7-9]{2} (doc77, doc87, doc97 등이 해당) 
예 : [^Zz]{3} (Z와 z를 포함하지 않는 5개의 문자열, abc, ttt 등) 
예 : .{3,4}er ('er'앞에 세 개 또는 네 개의 문자를 포함하는 문자열이므로 Peter, mother 등이 해당)

(6) * (asterisk)

"*" 직전의 선행문자가 0번 또는 여러번 나타나는 문자열
예 : ab*c ('b'를 0번 또는 여러번 포함하므로 ac, abc, abbbc 등) 
예 : * (선행문자가 없는 경우이므로 임의의 문자열 및 공백 문자열) 
예 : .* (선행문자가 "."이므로 하나이상의 문자를 포함하는 문자열) 
예 : ab* ('b'를 0번 또는 여러번 포함하므로 a, accc, abb 등) 
예 : a* ('a'를 0번 또는 여러번 포함하므로 k, kd, a, aa, abb 등) 
예 : doc[7-9]* (doc7, doc777, doc778989, doc 등이 해당) 
예 : [A-Z].* (대문자로만 이루어진 문자열) 
예 : like.* (직전의 선행문자가 '.'이므로 like에 0 또는 하나 이상의 문자가 추가된 문자열이 됨, like, likely, liker, likelihood 등)

(7) + (Plus Sign)

"+" 직전의 선행문자가 1번 이상 나타나는 문자열
예 : ab+c ('b'를 1번 또는 여러번 포함하므로 abc, abcd, abbc 등) 
예 : ab+ ('b'를 1번 또는 여러번 포함하므로 ab, abcc, abb 등) 
예 : [A-Z]+ (대문자로만 이루어진 문자열) 
예 : like.+ (직전의 선행문자가 '.'이므로 like에 하나 이상의 문자가 추가된 문자열이 됨, likely, liker, likelihood 등, 그러나 like는 해당안됨)

(8) ? (question mark)

"?" 직전의 선행문자가 0번 또는 1번 나타나는 문자열
예 : ab?c ('b'를 0번 또는 1번 포함하므로 abc, abcd 만 해당됨)

(9) () (parenthesis)

()는 정규식내에서 패턴을 그룹화 할 때 사용

(10) | (bar)

or를 나타냄
예 : a|b|c (a, b, c 중 하나, 즉 [a-c]와 동일함) 
예 : yes|Yes (yes나 Yes 중 하나, [yY]es와 동일함) 
예 : korea|japan|chinese (korea, japan, chinese 중 하나)
(11) \ (backslash)
위에서 사용된 특수 문자들을 정규식내에서 문자로 취급하고 싶을 때 '\'를 선행시켜서 사용하면됨
예 : filename\.ext ("filename.ext"를 나타냄) 
예 : [\?\[\\\]] ('?', '[', '\', ']' 중 하나)

[출처] [C] 정규식 표현|작성자 엔비라이더


'Language > C' 카테고리의 다른 글

C로 정규식라이브러리 사용  (0) 2012.01.05
패턴 매칭 ( Pattern Matching )  (0) 2012.01.05
access() 파일 존재나 접근 권한을 확인합니다.  (0) 2011.12.29
void형 포인터  (0) 2011.12.02
이중 포인터의 필요성  (0) 2011.12.02
:

access() 파일 존재나 접근 권한을 확인합니다.

Language/C 2011. 12. 29. 08:52

설명

access()는 프로세스가 지정한 파일이 존재하는지, 읽거나 쓰거나 실행이 가능한 지를 확인하는 함수입니다. 만일 지정한 파일이 심볼릭 링크라면 링크의 원본을 체크합니다.

access()의 첫 번째 인자는 파일이나 디렉토리의 전체 이름이며, 두 번째 인자는 체크할 내용을 지정하게 됩니다.

int access(const char *pathname, int mode);

mode 값에는 아래의 상수 값을 지정하여 사용할 수 있습니다.

mode 의미
R_OK 읽기가 가능?
W_OK 쓰기가 가능?
X_OK 실행이 가능?
F_OK 파일이 존재하는가?

 

헤더 unistd.h
형태 int access(const char *pathname, int mode);
인수
char *pathname 파일이나 디렉토리 전체 이름
int mode 검사할 내용
반환
0 가능 또는 파일이 존재함
-1 mode 에 대해 하나 이상 거절되었거나 에러가 있음. 자세한 내용은 errno에 세팅됨

예제

#include <stdio.h>
#include <unistd.h>

int main( void)
{
   char *file_name  = "./main.c";
   if ( 0 == access( file_name, F_OK))
   {
      printf( "%s 파일이 있습니다.\n", file_name);
      
      if ( 0 == access( file_name, R_OK ¦ W_OK))
      {
         printf( "읽거나 쓰기가 가능합니다.\n");
      }
      else
      {
         printf( "읽지 못하거나 내용을 변경할 수 없습니다.\n");
      }
   }
}
]$ ./a.out
./main.c 파일이 있습니다.
읽거나 쓰기가 가능합니다.
]$ chmod 444 main.c
]$ ./a.out
./main.c 파일이 있습니다.
읽지 못하거나 내용을 변경할 수 없습니다.
]$ 

'Language > C' 카테고리의 다른 글

패턴 매칭 ( Pattern Matching )  (0) 2012.01.05
C 정규식  (0) 2012.01.05
void형 포인터  (0) 2011.12.02
이중 포인터의 필요성  (0) 2011.12.02
포인터 배열  (0) 2011.12.01
:

void형 포인터

Language/C 2011. 12. 2. 13:18

10-2-가.void형

포인터형 변수는 선언할 때 반드시 대상체의 타입을 밝혀야 한다. 가리키는 대상체의 타입을 알아야 *연산자로 대상체를 읽을 수 있고 증감 연산자로 전후 이동이 가능하다. 이런 일반적인 포인터에 비해 선언할 때 대상체의 타입을 명시하지 않는 특별한 포인터형이 있는데 이것이 바로 void형 포인터이다. void형 포인터를 선언할 때는 void *타입을 지정한다.

 

 void *vp;

 

이렇게 선언하면 vp 포인터 변수의 대상체는 void형이 되며 이는 곧 대상체가 정해져 있지 않다는 뜻이다. void형은 함수와 포인터 변수에게만 적용되는 타입이므로 일반 변수에는 쓸 수 없다. void i;라는 선언문은 불법이다. 다음은 void형 포인터의 특징들이되 모두 대상체가 정해져있지 않다는 사실에 기인한다.

 

 임의의 대상체를 가리킬 수 있다.

대상체가 정해져 있지 않다는 말은 어떠한 대상체도 가리키지 못한다는 뜻이 아니라 임의의 대상체를 가리킬 수 있다는 얘기와도 같다. 선언할 때 대상체의 타입을 명시하는 일반적인 포인터는 지정한 타입의 대상체만 가리킬 수 있지만 void형 포인터는 어떠한 대상체라도 가리킬 수 있다. 그래서 pi가 정수형 포인터 변수이고, pd가 실수형 포인터 변수이고 vp가 void형 변수일 때 다음 대입문들은 모두 적법하다.

 

vp=pi;

vp=pd;

 

정수형 포인터 pi가 가리키는 번지를 실수형 포인터 pd에 대입하고 싶다면 pd=pi; 대입식을 곧바로 쓸 수 없으며 반드시 pd=(double *)pi;로 캐스팅해야 한다. 대입문의 좌변과 우변의 타입이 같아야만 정상적인 대입이 가능하다. 그러나 void형 포인터는 임의의 대상체를 모두 가리킬 수 있기 때문에 대입받을 때 어떠한 캐스팅도 할 필요가 없다. 좌변이 void형 포인터일 때는 우변에 임의의 포인터형이 모두 올 수 있다. vp는 정수형 변수도 가리킬 수 있고 실수형 변수도 가리킬 수 있는 것이다.

void형 포인터를 좀 더 쉽게 표현하자면 임의의 대상체에 대한 포인터형이다. 대상체가 정수든, 실수든 가리지 않고 메모리 위치를 기억할 수 있다. void형 포인터는 임의의 포인터를 대입받을 수 있지만 반대로 임의의 포인터에 void형 포인터를 대입할 때는 반드시 캐스팅을 해야 한다.

 

pi=(int *)vp;

pd=(double *)vp;

 

만약 이 대입문에서 캐스트 연산자를 생략해 버리면 void *형을 int *형으로 변환할 수 없다는 에러 메시지가 출력된다. 만약 pi=vp; 대입식을 허용한다면 *pi로 이 번지의 정수값을 읽을 때 이 값이 온전한 정수형임을 보장할 수 없을 것이다. 개발자는 vp가 가리키는 곳에 정수가 있다는 것을 확신할 수 있을 때만 캐스트 연산자를 사용해야 한다. 참고로 C++보다 타입 체크가 덜 엄격한 C는 pi=vp 대입을 허용한다.

 *연산자를 쓸 수 없다.

void형 포인터는 임의의 대상체에 대해 번지값만을 저장하며 이 위치에 어떤 값이 들어 있는지는 알지 못한다. 따라서 *연산자로 이 포인터가 가리키는 메모리의 값을 읽을 수 없다. 대상체의 타입이 정해져 있지 않으므로 포인터가 가리키는 위치에서 몇 바이트를 읽어야 할지, 또 읽어낸 비트를 어떤 식으로 해석해야 할지를 모르기 때문이다. 다음 예제를 실행해 보자.

 

  : voidPointer

#include <Turboc.h>

 

void main()

{

     int i=1234;

     void *vp;

 

     vp=&i;

     printf("%d\n",*vp);

}

 

void형 포인터 vp는 정수형 변수 i가 저장된 번지를 대입받았다. 좌변이 void형 포인터이므로 vp=(void *)&i;와 같이 캐스트 연산자를 쓰지 않아도 곧바로 대입할 수 있다. 이 대입문에 의해 vp는 정수형 변수 i가 기억된 번지값을 가지게 될 것이다. 그러나 vp는 대상체가 정수형 변수라는 것을 모르기 때문에 *vp 로 이 번지에 들어있는 값을 읽을 수는 없다. 만약 vp 번지에 저장된 값이 정수형이라는 것을 확실히 알고 있고 이 값을 꼭 읽고 싶다면 다음과 같이 캐스트 연산자를 사용해야 한다.

 

printf("%d\n",*(int *)vp);

 

vp를 잠시 정수형 포인터로 캐스팅하면 *연산자를 사용할 수 있다. 캐스팅된 vp는 정수형 포인터이므로 *연산자는 vp가 가리키는 번지에서 4바이트의 정수를 읽을 수 있다.

포인터 연산자와 캐스트 연산자의 우선 순위는 같으며 결합 순서는 우측 우선이므로 캐스트 연산자가 먼저 수행되어 vp를 정수형 포인터로 바꾸어 놓고 *연산자가 이 위치에서 정수값을 읽는다. 따라서 *((int *)vp)처럼 굳이 괄호를 하나 더 쓸 필요는 없다. 물론 괄호를 싸 놓으면 캐스팅이 먼저 된다는 것을 확실하게 알 수 있어서 안정되 보이기는 한다.

 증감 연산자를 쓸 수 없다.

대상체의 타입이 정해져 있지 않으므로 증감 연산자도 곧바로 사용할 수 없다. 정수값과 바로 가감 연산을 하는 것도 허용되지 않는다. 대상체의 크기를 모르기 때문에 얼마만큼 이동해야 할지를 모르는 것이다. 다음 예제를 실행해 보자.

 

  : voidPointer2

#include <Turboc.h>

 

void main()

{

     int ar[]={1,2,3,4,5};

     void *vp;

 

     vp=ar;

     vp=vp+1;

     printf("%d\n",*(int *)vp);

}

 

vp=vp+1 연산문에서 에러가 발생하는데 +1이 몇 바이트 뒤인지를 결정하지 못하기 때문이다. 대상체의 크기를 모르므로 다음 요소로 이동할 수 없다. void *를 다음 요소로 이동하는 여러 가지 코드의 가능성 여부를 점검해 보자.

 

 vp=vp+1 : 위 예제에서 보다시피 명백한 에러다. 대상체 크기를 모르므로 증가할 양을 결정하지 못한다. 동일한 코드의 축약형인 vp+=1도 마찬가지로 에러로 처리된다.

 vp++ : vp=vp+1과 같은 식이므로 역시 안된다. 따라서 에러로 처리된다.

 vp=(int *)vp + 1 : 가능하다. vp를 잠시 int *로 캐스팅한 후 1을 더하면 4바이트 뒤쪽을 가리키는 int * 타입의 포인터 상수가 된다. void *는 임의의 포인터를 대입받을 수 있으므로 별도의 캐스팅없이 정수형 포인터를 대입받을 수 있다.

 (int *)vp++ : vp를 캐스팅한 후 증가하면 될 것 같지만 결합 순서에 의해 ++이 캐스트 연산자보다 먼저 연산되므로 vp++과 같은 이유로 에러이다.

 ((int *)vp)++ : 캐스트 연산자를 괄호로 싸 먼저 연산되도록 했다. ++연산자의 피연산자는 좌변값이어야 하는데 캐스팅을 하면 좌변값이 아니므로 증가시킬 수 없다. 그러나 Dev-C++에서는 적법한 문장으로 컴파일된다. ++(int *)vp도 가능하다.

 

보다시피 가능한 방법이 있고 그렇지 못한 방법이 있는데 모든 경우에 가능한 코드는 vp=(int *)vp+1 밖에 없다. 이렇게 수정한 후 컴파일해 보면 ar[1]인 2가 출력된다. 나머지는 당장은 가능하다 하더라도 이식에 불리하므로 쓰지 않는 것이 좋다.

void형 포인터의 특징에 대해 간단하게 요약해 보자. 대상체가 정해져 있지 않으므로 임의의 번지를 저장할 수 있지만 *연산자로 값을 읽거나 증감 연산자로 이동할 때는 반드시 캐스트 연산자가 필요하다. 값을 읽거나 전후 위치로 이동하는 기능은 빼고 순수하게 메모리의 한 지점을 가리키는 기능만 가지는 포인터라고 할 수 있다.

'Language > C' 카테고리의 다른 글

C 정규식  (0) 2012.01.05
access() 파일 존재나 접근 권한을 확인합니다.  (0) 2011.12.29
이중 포인터의 필요성  (0) 2011.12.02
포인터 배열  (0) 2011.12.01
char *로 할당된 스트링값  (0) 2011.11.18
:

이중 포인터의 필요성

Language/C 2011. 12. 2. 13:10

10-4-가.이중 포인터

이중 포인터란 포인터 변수를 가리키는 포인터라는 뜻이며 다른 말로 하면 포인터의 포인터라고 할 수 있다. 포인터 변수도 메모리를 차지하고 있으므로 이 변수도 당연히 번지가 있다. 따라서 이 번지를 가리키는 또 다른 포인터 변수를 선언할 수 있는 것이다. 이중 포인터 변수를 선언할 때는 * 구두점을 두 번 연속해서 쓴다.

 

int **ppi;

 

이 선언에서 ppi는 정수형 대상체를 가리키는 포인터 변수의 번지를 가리키는 포인터 변수로 선언되었다. 말이 조금 꼬이는 것 같고 복잡해 보이는데 어째서 저런 변수를 선언할 수 있는지 차근 차근히 풀어 보도록 하자. 다음 두 명제는 앞에서 이미 공부했던 것들인데 이중 포인터를 이해하기 위해 다시 정리해 보자.

 

T *형은 하나의 타입으로 인정된다.

T 변수를 선언할  있으면 T *형도 항상 선언할  있다.

 

정수형 포인터 변수는 다음과 같이 선언한다.

 

int *pi;

 

이 선언문에서 int *라는 표현이 "정수형 포인터"라는 뜻으로 그 자체가 하나의 타입이다. 따라서 다음과 같이 괄호로 묶으면 좀 더 읽기 쉬워지고 뜻이 분명해질 것이다.

 

(int *) pi;

 

두 번째 명제에 의해 T형에 대해 항상 T *형이 가능하므로 int *형에 대한 포인터형을 만들 수 있다. int *형 변수를 가리키는 변수 ppi를 선언하면 다음과 같아진다.

 

(int *) *ppi;

 

이 선언문에서 괄호를 제거하면 최초의 이중 포인터 선언문인 int **ppi;가 된다. 물론 여기서 사용한 괄호는 어디까지나 설명을 위해 쓴 것이지 실제로 타입에 괄호를 쓰면 컴파일 에러로 처리된다. 하지만 typedef문으로 int *형을 별도의 타입으로 정의한 후 이 타입의 변수를 선언할 수는 있다.

 

typedef int *PINT;

PINT pi;

 

보다시피 int *형을 PINT라는 사용자 정의 타입으로 정의하였고 이 타입의 변수 pi를 선언할 수 있다. 포인터 변수 pi를 가리킬 수 있는 포인터 변수를 선언하면 다음과 같다.

 

PINT *ppi;

 

이 선언문에서 PINT를 사용자 정의형으로 풀어 쓰면 int **ppi;가 되는 것이다. 같은 원리로 3중 포인터는 int ***pppi;와 같이 선언하면 되고 5중 포인터나 8중 포인터도 그 수만큼 *를 계속 붙이면 된다. 3중 포인터 이상은 현실적으로 거의 사용되지 않지만 이중 포인터와 원리는 동일하므로 이중 포인터만 이해하면 된다. 이중 포인터는 포인터 변수를 가리키는 변수이므로 이 변수에는 포인터의 포인터값을 대입해야 한다. 즉, 포인터 변수에 &연산자를 붙인 값이면 이중 포인터에 대입할 수 있다. 다음 예제는 이중 포인터의 동작 원리를 보여주는 가장 간단한 예제이다.

 

  : dblPointer

#include <Turboc.h>

 

void main()

{

     int i;

     int *pi;

     int **ppi;

 

     i=1234;

     pi=&i;

     ppi=&pi;

 

     printf("%d\n",**ppi);

}

 

정수형 변수 i에 1234값을 대입해 놓고 이중 포인터로 이 값을 읽는 시범을 보인다. 정수형 포인터 pi가 i의 번지를 가지고 정수형 이중 포인터 변수 ppi에는 pi의 번지를 대입해 놓고 **ppi값을 읽으면 결국 i의 값이 읽혀진다. 이 프로그램에서 각 변수들이 서로를 가리키는 모양은 다음과 같을 것이다.

i는 일반 변수이므로 메모리상에 4바이트를 차지하고 1234가 대입되었다. 정수형 포인터 pi도 일종의 변수이므로 메모리상에 할당되며 그 초기값으로 &i, 즉 i의 번지를 대입받았다. 이 상태에서 pi가 i를 가리키고 있으므로 *pi 연산문으로 i값을 읽을 수 있다. 여기까지는 앞에서 이미 살펴 본 것들이다.

포인터 변수 pi도 일종의 변수이므로 분명히 메모리에 할당될 것이고 따라서 번지를 가지고 있다. pi가 할당되어 있는 번지값인 &pi를 이중 포인터 변수 ppi에 대입했다. 그래서 ppi는 pi를 가리키고 pi는 다시 i를 가리키고 있는 것이다. 이 상태에서 **ppi 연산문으로 값을 읽으면 *(*ppi)=*(pi)=i가 되므로 결국 출력되는 값은 i인 것이다.

ppi가 가리키는 곳에 pi가 있으며 pi가 가리키는 곳에는 i가 있으므로 ppi에 대해 *연산자를 두 번 적용하면 결국 i값이 읽혀진다. 이 예제의 상황에서 다음 수식들은 모두 동등한 대상을 나타낸다.

 

i=*pi=**ppi

&i=pi=*ppi

*&i=*&*pi=*&**ppi

 

* 연산자와 & 연산자는 서로 반대되는 동작을 하는데 이 두 연산자에 의해 가리키고 끄집어 내오다 보면 동등한 수식이 여러 개 생길 수 있다. 그렇다고 해서 &&i=&pi=ppi라는 등식은 성립하지 않는데 &연산자를 두 번 쓰는 것은 적법하지 않다. 왜냐하면 &연산자의 피연산자는 메모리상의 실제 번지를 점유하고 있는 좌변값(lvalue)이어야 하는데 &i는 i가 저장된 번지를 나타내는 포인터 상수일 뿐 좌변값이 아니기 때문이다.

다음 예제는 이중 포인터의 전형적인 활용예인데 포인터를 참조 호출로 전달하여 함수가 포인터를 변경할 수 있도록 한다. main에서 이름을 입력받는 함수를 호출하는데 이 이름의 길이가 DB나 사용자로부터 입력되어 실제 입력받아 보기 전에는 얼마일지를 알 수 없다고 하자. 이럴 경우 함수가 필요한만큼 메모리를 할당해서 할당한 번지를 리턴하도록 해야 한다.

 

  : FuncAlloc

#include <Turboc.h>

 

void InputName(char **pName)

{

     *pName=(char *)malloc(12);

     strcpy(*pName,"Cabin");

}

 

void main()

{

     char *Name;

 

     InputName(&Name);

     printf("이름은 %s입니다\n",Name);

     free(Name);

}

 

main에서 char *형의 변수 Name을 선언하고 이 포인터 변수의 번지, 즉 char **형의 이중 포인터를 InputName 함수로 전달했으며 이 함수는 이중 포인터를 형식 인수 pName으로 대입받는다. Name은 함수 내부에서 값이 결정되는 출력용 인수이기 때문에 호출원에서 초기화하지 않아도 상관없다. InputName 함수는 필요한만큼(예제에서는 12로 가정) 동적으로 메모리를 할당하여 할당된 번지를 pName이 가리키는 번지인 *pName에 대입했다. 여기서 *pName이라는 표현식은 곧 main에서 InputName으로 전달한 실인수 Name을 의미한다. 그리고 할당된 번지에 어떤 문자열을 복사했다.

결국 InputName 함수는 main의 Name 포인터 변수를 참조 호출로 전달받아 Name에 직접 메모리를 할당하고 이 번지에 scanf로 입력받은 이름까지 복사한 것이다. InputName이 리턴되었을 때 Name은 12바이트 길이로 할당된 번지를 가리키며 이 안에는 입력된 이름까지 들어 있으므로 printf로 출력할 수 있고 다 사용한 후 free로 해제하면 된다. 이 문제를 잘못 생각하면 다음과 같이 InputName 함수가 char *형을 받도록 작성할 수도 있다.

 

  : FuncAlloc2

#include <Turboc.h>

 

void InputName(char *pName)

{

     pName=(char *)malloc(12);

     strcpy(pName,"Cabin");

}

 

void main()

{

     char *Name;

 

     InputName(Name);

     printf("이름은 %s입니다\n",Name);

     free(Name);

}

 

언뜻 보기에는 이 코드가 맞는 것 같지만 실제로 컴파일해 보면 경고가 하나 발생하며 실행하면 제대로 동작하지도 않을 뿐더러 죽어 버리기까지 한다. 왜 그런지 다음 그림을 보자.

main에서 Name을 선언하고 초기화되지도 않은 Name의 값(비록 그 값이 번지라 하더라도 어쨌든 값이다.)을 InputName의 pName으로 전달했다. 이 함수는 pName에 메모리를 할당하고 이름 문자열을 복사해 넣지만 pName은 함수의 지역변수일 뿐이지 호출원의 실인수 Name과는 아무런 상관이 없다. 함수가 char *의 값을 전달받으면 이 번지가 가리키는 내용을 변경할 수는 있지만 포인터 자체를 변경해서 호출원으로 돌려줄 수는 없다.

이 코드대로라면 pName에 메모리가 할당되고 이름도 복사되지만 그 결과가 main함수의 Name까지는 전달되지 않는다. main의 Name은 여전히 쓰레기값을 가지고 있으며 이 번지를 잘못 읽거나 할당되지도 않은 영역을 해제하려고 시도하면 죽어 버릴 수도 있다. 뿐만 아니라 지역변수 pName은 함수가 리턴되기 전에 사라지므로 할당된 메모리의 진입점을 잃어 버려 더 이상 이 메모리를 읽을 수 없고 해제할 수도 없는 상태가 되어 버린다.

6장에서 배운 바에 의하면 인수 X의 값을 함수 내부에서 변경하려면 X의 포인터를 넘기는 참조 호출을 해야 한다. 그러므로 InputName에서 char *형의 인수 Name을 변경할 수 있도록 하려면 char *의 포인터인 char **형을 넘겨야 하고 InputName 함수에서는 *pName으로 실인수를 참조해야 하는 것이다. 

'Language > C' 카테고리의 다른 글

access() 파일 존재나 접근 권한을 확인합니다.  (0) 2011.12.29
void형 포인터  (0) 2011.12.02
포인터 배열  (0) 2011.12.01
char *로 할당된 스트링값  (0) 2011.11.18
fopen, open 차이  (0) 2011.11.18
: