달력

52024  이전 다음

  • 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
프로그램을 짜다 보면 flag용으로만 사용하여 실제로는 1byte는 커녕 1bit만 할당해도 사용하는데 충분한 경우가 있다. 이때 bit 단위로 실제 사용 크기만큼만 할당해서 사용하면 메모리를 아낄 뿐 아니라 해당 변수를 복사할 때 빠를 것으로 생각된다.

물론 함수 내에서 잠깐 사용할 경우에는 int 로 선언해도 무방하고, 다른 모든 곳에서도 int 혹은 char 등으로 선언해도 사용하는데도 된다.

그러니까 아마도 주로 사용하는 struct 내에  Bool과 같이 단순 flag만으로 사용하는 변수가 많을 경우 유용할 것이다.

사용 방법은 간단한다.
변수 뒤에 ' : 크기' 와 같은 방식으로 bit 크기를 설정하면 되며, 주의할 것은 unsigned 를 붙여야 한다는 것이다.
예를 들어 unsigned char a1 : 1; 는 1 bit 크기를 할당한다.

관련해서 테스트 소스를 아래와 같이 작성했다.

#include <stdio.h>

struct Mystr {
 unsigned char a1 : 4;
 unsigned char a2 : 2;
 unsigned char a3 : 1;
};

int main(int argc, char *argv[])
{
 int i;
 struct Mystr mystr = { .a3=1, };

 printf("sizeof(struct Mystr) is %d\n", sizeof(struct Mystr));
 if (mystr.a3 == 1)
  printf(".a3=1 works!\n");

 return 0;
}
그 결과는 다음과 같다.
참고적으로 struct Mystr내 bit 크기가 8보다 큰 9라면 sizeof() 결과는 2가 된다.
그리고 unsigned char가 아닌 char 를 쓰면 '1'이 아닌 '0xFFFF'와 같은 값이 저장되어 문제가 된다.
sizeof(struct Mystr) is 1
.a3=1 works!

'Lang, Tool, Env' 카테고리의 다른 글

Linux version  (0) 2016.02.15
Ubuntu /home 옮기기  (0) 2012.08.04
struct timeval & long long  (0) 2010.03.31
IEEE Floating-Point Format  (1) 2009.09.13
chroot  (0) 2008.04.06
Posted by neodelicious
|

*** printf() options

%hd - half decimal, 즉 short
%f - float와 double
%lf - long double
%5d - 우측 정렬, 빈칸 삽입
%+5d - 부호 표기
%-5d - 좌측 표기
%05d - 우측 표기, 좌측에 숫자'0' 삽입
%.2f - 소수점 이하 출력할 숫자의 개수 제한

0x%x 대신에 %p를 쓰면 자동으로 0x를 붙여준다.
주소 출력시 %p를 이용해 보자.

******************************************

*** 정수형과 실수형의 기본 자료형은 각각 int와 double이다.

따라서 int x = 10 에서는 형변형이 없지만
float y = 10.1 에서는 10.1을 double형으로 인식하여 이를 float로 자동 형변환 한다.

또한 다음의 경우 Error가 아니다.
static a[3];
이는 type을 안 써서 기본형인 int으로 인식하기 때문이다.

*** 기본 함수형태는 int FuncName (void) 이다.

따라서 int f1 (void) 의 경우 호출 전에 선언하지 않아도 인지한다.

******************************************

*** int main ( int argc, char * argc[], char * envp[] )

배열 envp 안에 환경 정보가 저장되어 있다.
이 배열의 마지막은 NULL이다.

******************************************

*** char * strdup (const char *)

입력 string의 메모리를 복사하는데, 또 다른 동적 메모리를 할당하여 복사한다.
추후 메모리 반환을 해줘야 한다.

******************************************

*** 지역변수를 stack에 저장할 때 I/O 크기에 따라 자동으로 메모리 공간을 일부러 띄운다.

char 형, short 형 변수를 각각 선언하면 stack의 높은 주소의 위치부터 차례로 이용하는데,
char 가 1byte이고 short가 2byte일 때 다음과 같은 경우가 될 수 있다.

예1)
8F - char
8E - 없음
8D - short 상위
8C - short 하위

예2) - visual C++ 6.0 실험 결과
8F - 없음
8E - 없음
8D - 없음
8C - char
8B - 없음
8A - 없음
89 - short 상위
88 - short 하위

******************************************

*** prompt 상에서 editor 실행

gedit &
-> gedit text editor 실행
kate &
-> gedit와 다른 text editor

******************************************

*** PC에서 vmware 상의 linux와 기본 OS인 winXP 사이의 ftp 파일 전송 방법
*** 주의 !!! 일부 설정 방법이 생략되어 있음

service vsftpd start
-> linux 內 ftp 서버 활성화

winXP에서 vmware 상이 linux의 IP에 대해서 ftp 연결 시도
ftp [Linux IP]
get, put 혹은 mget, mput 이용 파일 전송

******************************************

*** 틀리기 쉬운 C 문법

#define FLAG  0x02 일때
flag 변수의 bit 2가 set인지 확인하고자 한다면,
if (flag & FLAG) 를 써야 하는데,
if (flag & FLAG != 0) 로 쓰면 != 가 & 보다 우선하므로 틀린다.

val = high << 4 + low;
에서 high를 상위로 하고 low를 하위로 하는 것처럼 오인할 수 있는데,
산술연산자 + 가 먼저 실행되어 잘못된 계산을 하게 된다.

== 은 < , > 보다 우선 순위가 낮아서
n1 > n2 == n2 > n3
의 경우 == 가 나중에 되므로 주의한다.

치환연산자는 관계연산자보다 우선순위가 낮다. (사실 치환연산자는 제일 낮다.)
while ( c = getchar() != '\n' )
에서 != 를 먼저하므로 error이다.


******************************************

*** code 상에 정의되지 않은 값을 compile할 때 정의하는 방법

opPriorErr.C 파일을 cc를 이용하여 compile할 때,
'CASE' 라는 값을 '1'로 설정하려면
다음과 같이 '-D' 이후에 'CASE'를 입력한다.

cc -DCASE=1 opPriorErr.C

'Lang, Tool, Env' 카테고리의 다른 글

IEEE Floating-Point Format  (1) 2009.09.13
chroot  (0) 2008.04.06
[cpp] fstream::getline() 쓸 때 clear()를 꼭 쓰자  (0) 2007.08.04
C 프로그래머가 알아야 할 것들  (1) 2007.05.16
CRC를 통한 error 검출  (0) 2007.05.16
Posted by neodelicious
|
구조체안의 특정 field의 주소를 통해서 그 field를 포함하고 있는 실제 구조체(객체)의 주소를 얻을 수 있는 방법이 있다. 그때 쓰는 매크로가 CONTAINING_RECORD 매크로이다.

우선 CONTAINING_RECORD 매크로처럼 유용한 매크로를 먼저 소개하면, offsetof 라는 매크로가 있다.
이를 이용하면 특정 구조체에서 해당 field의 offset을 알 수 있다.

offsetof 매크로에서 (unsigned long)(&((type *)0)->field)  뜻은, 0을 주어진 type의 시작점이라고 가정하도록 타입캐스팅을 한 후에, 그 type의 field를 엑세스 하여, type의 시작점부터 field 까지의 거리를 구하는 것이다. 즉, 0부터 field까지 offset이 얼마냐를 구하는 것이다.
(바로 위 문단, 3줄은 데브피아 링크의 김희준 님의 글을 조금 수정하여 인용한 것입니다.)

위의 offsetof 매크로의 기능을 그대로 이용한 것이 CONTAINING_RECORD 매크로이다.
이 매크로에서는 구조체 안의 특정 field에 대해서 구조체 시작부터의 offset 값을 구하고,
이를 실제 그 구조체 객체에서 그 field 주소에서 offset 값만큼 작은 주소로 이동한 값을 계산하여
구조체 객체 자체의 주소를 얻는다.

특히 이는 구조체 안에 리스트 구조가 포함되어 있을 때 유용하다.
리스트를 포함하는 구조체의 이름만 안다면 특정 리스트의 시작 주소를 넣어서 그 리스트를 포함하는 실제 구조체 객체의 주소를 얻을 수 있는 것이다.

아래 코드는 직접 실행해서 이를 확인한 것이다.
코드 실행 결과처럼 offsetof와 CONTAINING_RECORD 매크로 둘 다 잘 동작한다.
위에서 설명했듯이 (type *)0에서 0을 시작점으로 하기에,
코드에서 (testStr*)3 라고 해서 3을 시작점으로 할 수도 있음을 확인할 수 있다.
참고적으로 CONTAING_RECORD의 offset 부분에 offsetof 매크로를 넣어서 매크로를 구성할 수도 있다.

끝으로 CONTAING_RECORD 매크로에서 offset 부분 앞에 (unsigned long)을 붙였는데,
(unsigned long *)라고 되어 있는 자료도 봤다.
Visual Studio 6.0에서 테스트 해보니 둘 다 제대로 된 값은 나오는데,
(unsigned long *)라고 했을 경우 incompatible types 의 compile warning이 뜬다.


#include 
#include 

#define offsetof(type, member)   (size_t)&(((type *)0)->member)
#define CONTAINING_RECORD(address, type, field) \
		((type *)((char *)(address) - (unsigned long)(&((type *)0)->field)))

typedef struct _testStr{
	int a;
	char b[6];
	char c;
}testStr, *ptrTestStr;

int main()
{
   ptrTestStr ptr1 = (ptrTestStr)malloc(sizeof(testStr));
   char * bPtr = ptr1->b;

   printf("ptr1		: [%x]\n", ptr1);
   printf("&ptr1->c	: [%x]\n", &ptr1->c);
   printf("offsetof(testStr, c) : [%d]\n\n", offsetof(testStr, c));

   printf("(testStr*)0	: [%d]\n", (testStr*)0 );
   printf("(testStr*)3	: [%d]\n", (testStr*)3 );
   printf("(testStr*)3->c	: [%d]\n\n", &((testStr*)3)->c );

   printf("bPtr : [%x]\n", bPtr);
   printf("CONTAINING_RECORD(bPtr, testStr, b) : [%x]\n\n", \
                              CONTAINING_RECORD(bPtr, testStr, b));

	return 0;
}

사용자 삽입 이미지
Posted by neodelicious
|
C에서 배열은 포인터와 혼용되면서 많은 혼란을 가져오는 특징 중의 하나이다.
그러한 배열을 선언할 때는 그 크기를 지정해 주어야 하며
다만 배열을 선언하면서 동시에 그 값을 채워줄 때는 크기를 지정하지 않아도 되는 것으로 알고 있었다.
그래서 char a[] = "ab"; 는 가능해도 char a[]; 는 에러가 된다.

그런데 구조체 속에 선언한 크기 미지정의 배열은 어떻게 될까? 에러일까?
정답부터 말하면 가능하다.

좀더 말해서 해당 구조체 메모리 할당시 구조체 안에 크기 미지정의 배열에 대한
메모리는 할당되지 않는 것 같다.
이는 구조체에서 이 크기 미지정의 배열 대신 포인터를 넣으면
포인터 크기, 주로 4byte가 잡히는 것과 비교할 때 특이한 점이라 할 수 있다.
이러한 특징을 이용해서 구조체의 가장 끝 필드(Field)값으로 이러한 크기 미지정 배열을 넣고,
이 구조체에 대한 메모리를 할당할 때 구조체 자체의 메모리 크기에 더해서
해당 배열의 타입(type)의 배수 크기로 추가적인 메모리를 잡으면
그 배열 이름과 인덱스(index)로 추가된 메모리를 접근할 수 있다.
이 경우 구조체 메모리를 할당할 때마다 해당 배열의 크기를 원하는 만큼으로 조절할 수 있는 장점이 있다.

아래 코드는 이를 확인하기 위해 작성한 것으로서
특이한 사항은 추가적인 메모리를 할당하던 안 하던 크기 미지정 배열의 주소가
int 변수의 주소값에서 4만큼 큰 주소값이라는 것이다.
메모리는 할당되지 않지만 주소값은 있는 것처럼 보이는 것이다.



#include <stdio.h>
#include <stdlib.h>


typedef struct _T1{
int a;  // 4bytes
char * ch; // 4byte
}T1, *PT1;


typedef struct _T2{
int a;  // 4bytes
char ch[]; // 0byte
}T2, *PT2;


int main()
{
PT1 p1 = (PT1)malloc(sizeof(PT1)+sizeof(char)*5);
PT2 p2 = (PT2)malloc(sizeof(PT2)+sizeof(char)*5);
PT2 p3 = (PT2)malloc(sizeof(PT2));

printf("T1 size : [%d], T2 size : [%d]\n", sizeof(T1), sizeof(T2));
printf("sizeof(int) : [%d]\n\n", sizeof(int));

// error - a->ch[0] = 'A';
p2->ch[0] = 'B';

printf("p1 : [%x], &p1->a : [%x], &p1->ch : [%x]\n\n", p1, &p1->a, &p1->ch);

printf("p2->ch[0] : [%c]\n", p2->ch[0]);
printf("p2 : [%x], &p2->a : [%x]\n", p2, &p2->a);
printf("&p2->ch : [%x], &p2->ch : [%x], &p2->ch[0] : [%x]\n", &p2->ch, p2->ch, &p2->ch[0]);

printf("\np3 : [%x], &p3->a : [%x]\n", p3, &p3->a);
printf("&p3->ch : [%x], &p3->ch : [%x], &p3->ch[0] : [%x]\n", &p3->ch, p3->ch, &p3->ch[0]);

return 0;
}

사용자 삽입 이미지

Posted by neodelicious
|

Verify()

Lang, Tool, Env 2007. 4. 14. 18:32

해당 코드의 값이 참이라고 생각하는데 실제로 그런지 확인하고 갈 때
이용하는 것이 assert() 매크로이다.
관련 설명은 wiki 링크 자료를 참고하길 바란다.
http://en.wikipedia.org/wiki/Assert

그런데 debug 모드에서는 assert() 매크로를 쓸 수 있지만
release 모드에서는 이렇게 할 필요가 없다.
따라서 이를 구분하기 위해서 다음과 같이 verify() 라는 매크로를 쓸 수 있다.

#ifdef DEBUG
#define VERIFY(exp) ASSERT(exp)
#else
#define VERIFY(exp) exp

Posted by neodelicious
|

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

사용자 삽입 이미지

다시 체계적으로 배우는

C 언어 포인터



예전에 도서관에서 빌려다 읽었던 책인데, 오늘 서점에 갔다가 눈에 띄어서 추천(?) 삼아 글을 올려본다.

내 기억으로는 일부는 굳이 그럴 필요가 없는데 권장하는 부분도 있었다.

그런데 대부분 포인터에 대해서 설명을 잘 해 놨던 것으로 기억한다.

<책 겉표지 그림 출처 : yes24>

yes24 링크
Posted by neodelicious
|

fgets() 사용 시 유의점


fgets() 이용할 때 new line('\n')에서 끊어지기는 하나
new line까지 저장되고 Null('\0')이 삽입된다.
따라서 new line이 저장된 부분에 Null을 저장하여 지우는 것이 필요하다.
하지만 만약 버퍼의 크기를 넘어갈 경우
항상 버퍼 크기 -1 크기만큼만 저장되고 마지막에 Null이 삽입된다.
따라서 버퍼 크기 -1 크기만큼 문자열을 입력하고 엔터를 입력하면
new line은 저장되지 않고 마지막에 Null이 삽입되어
이 때는 new line을 없애기 위해 Null을 추가로 삽입하면 안 된다.


그래서 다음과 같이 마지막에 new line이 있는지 여부를 판단하도록 했다.


#define BUFFERSIZE 10
char buf[BUFFERSIZE];
fgets(buf, BUFFERSIZE, stdin);
stringLength = strlen(buf);
if ( buf[stringLength-1] == '\n')
  buf[--stringLength] = NULL;


코드에서 10 byte buf에 대해서
"delicious" 9자를 입력하면 new line 없이 Null이 들어가고
"deliciou" 8자를 입력하면 new line을 Null로서 덮어 없앤다.
물론 9자를 넘어가는 문자열은 모두 저장되지 않고 사라진다.
참고적으로 아무 것도 입력하지 않고 엔터를 입력해도 new line은 저장되고
위의 코드에서는 Null로 덮어 지우게된다.

fgets()에 대한 기본 설명은 아래 링크를 참조하자.

http://www.cplusplus.com/ref/cstdio/fgets.html

Posted by neodelicious
|