http://kerneltrap.org/node/4705


아래처럼 define 되는 likely라는 매크로가 있다.

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)



이것은 어떤 기능을 할까?

 if (__builtin_expect (x, 0))
                foo ();

위와같은 코드가 있다고 할 때, 컴파일러는 x가 0이 될 확률이 높다는걸 '알게'된다.

그럼 foo 함수를 실행할 가능성이 적어지고, 컴파일러가 최적화를 할 가능성이 커진다.



구체적인 예를 보자.

int
testfun(int x)
{
        if(x) {
                x = 5;
                x = x * x;
        } else {
                x = 6;
        }
        return x;
}

함수 testfun은 인자 x로 어떤값이 들어올지 모른다. 

레귤러하게 분포된다면, 

x = 6이 될 확률은 1/2^32 이 될 것이다.



그러나, __builtin_expect()을 씀으로써, x는 '거의' 0임을 알려준다.

[kedar@ashwamedha ~]$ cat abc.c
int
testfun(int x)
{
        if(__builtin_expect(x, 0)) {
                              ^^^--- We instruct the compiler, "else" block is more probable
                x = 5;
                x = x * x;
        } else {
                x = 6;
        }
        return x;
}
 
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump  -d abc.o
 
abc.o:     file format elf32-i386
 
Disassembly of section .text:
 
00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 08                mov    0x8(%ebp),%eax
   6:   85 c0                   test   %eax,%eax
   8:   75 07                   jne    11 < testfun+0x11 >
                                ^^^ --- The compiler branches the "if" block and keeps "else" sequential
   a:   b8 06 00 00 00          mov    $0x6,%eax
   f:   c9                      leave
  10:   c3                      ret
  11:   b8 19 00 00 00          mov    $0x19,%eax
  16:   eb f7                   jmp    f < testfun+0xf >

생성된 어셈블러를 보면 x가 0이 아닐 때 점프(jne)를 한다.



[kedar@ashwamedha ~]$ cat abc.c
int
testfun(int x)
{
        if(__builtin_expect(x, 1)) {
                              ^^^ --- We instruct the compiler, "if" block is more probable
                x = 5;
                x = x * x;
        } else {
                x = 6;
        }
        return x;
}
                                                                                                    
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump  -d abc.o
                                                                                                    
abc.o:     file format elf32-i386
                                                                                                    
Disassembly of section .text:
                                                                                                    
00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 08                mov    0x8(%ebp),%eax
   6:   85 c0                   test   %eax,%eax
   8:   74 07                   je     11 < testfun+0x11 >
                                ^^^ --- The compiler branches the "else" block and keeps "if" sequential 
   a:   b8 19 00 00 00          mov    $0x19,%eax
   f:   c9                      leave
  10:   c3                      ret
  11:   b8 06 00 00 00          mov    $0x6,%eax
  16:   eb f7                   jmp    f < testfun+0xf >

반대로, 위와 같은 경우엔 x가 0일 때 점프하게 된다.



Basic Block(코드의 순서)을 어떻게 배치하느냐에 따라서 성능은 달라 질 수있다.

(branch predictor가 없다면,) CPU는 PIPELINE으로 되있기 때문에 우선 branch는 무시하고, 있는 코드를 순서대로 실행하다 branch가 되야되면 파이프라인을 FLUSH시킨다. 그럼 성능이 떨어질 수 밖에 없다.


조그마한 성능도 중요한 임베디드나 커널 코드에 많이 쓰이는듯 하다.















저작자 표시 비영리 변경 금지
신고

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

[C] likely/unlikely macros  (0) 2012.12.14
LNK2005 already defined in LNK1169: one or more multiply defined symbols found  (0) 2012.11.27
다차원 배열  (0) 2012.10.03
#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
Posted by Leo 리오 트랙백 0 : 댓글 0

LNK2005: __invalid_parameter already defined in

LNK1169: one or more multiply defined symbols found


되게 귀찮게한다. 비쥬얼 스튜디오..

gcc같은 경우엔 strength 따지거나 아니면 그냥 warning으로 넘어가는데













저작자 표시 비영리 변경 금지
신고

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

[C] likely/unlikely macros  (0) 2012.12.14
LNK2005 already defined in LNK1169: one or more multiply defined symbols found  (0) 2012.11.27
다차원 배열  (0) 2012.10.03
#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
Posted by Leo 리오 트랙백 0 : 댓글 0

다차원 배열

2012.10.03 18:30 from Program Language/C

array에는 크게 두 가지가 있다.


1.

int arr1[5];


2.

int *arr2;

arr2 = (int*) malloc(sizeof(int)*5);


배열의 데이터가 1번은 스택에 저장되고 2번은 힙에 저장된다.


이건 1차원 배열에선 크게 상관이 없다.


1차원 배열에선 두 가지를 혼용하여 사용하여도 큰 상관은 없지만


2차원 이상 다차원 배열에선 문제가 된다.





2차원 배열을 보면


1.

int arr1[2][5];



2.

int **arr2;

arr2 = (int**) malloc(sizeof(int*) * 2);

arr2[0] = (int*) malloc(sizeof(int)*5);

arr2[1] = (int*) malloc(sizeof(int)*5);

 or

int **arr2;

arr2 = (int**) malloc(sizeof(int*) * 2);

int brr0[5], brr1[5];

arr2[0] = brr0;

arr2[1] = brr1;




우선 type을 보면

1번에서 arr1은 int*형 type을 갖고

2번에서 arr2는 int**형 type을 갖는다.


저장되는 공간을 보면

1번 배열은 2*5 int형 데이터가 스택에 10개가 쫘르륵 배치된다.

2번 배열은 한 줄(5개)이 각각 다른 장소에 지정되고, arr은 int 5개가 저장되있는 각각의 장소를 가르킨다.


컴파일러 입장에서보면

1번은 컴파일 타임에 위치를 알 수 있고,

2번은 실행시간에야 위치를 알 수 있다.

(다음을 봐보자)


액세스 방식을 보면 (1, 3)액세스

1번에서 arr1[1][3]은 arr + 1 * 5 + 3의 값 같은 의미를 갖는다. = *( arr + 1 * 5 + 3 )

2번에선 arr2[1][3]은 arr[1] + 3과 같은 의미를 갖는다. = *( arr[1] + 3 )

우선 arr[1]주소로 가서 두번째 줄 값이 저장된 장소를 알아낸후 (int*)

5개의 값 중 3번째 값을 가져온다. *( arr[1] + 3)


다시 말하면

1번의 arr1은 바로 값이 있는 위치를 알고 있고 (int*)

2번의 arr2는 값이 있는 위치를 알고있는 그 위치를 알고 있는것이다.(int**)



중요한 다른 함수로 배열 값을 넘길 때를 보자.


1.

선언

void boo( int arr1[2][5] ) or void boo( int arr1[][5] )

호출

boo(arr1);


2.

선언

void foo(int **arr2)

호출

foo(arr2)


이런 식으로 선언, 호출을 해야한다.


1번에선 이 배열은 5개씩 묶음이 2개 있다 읽을 때 5개씩 띄어서 읽어라 라고 알려주기 위한 선언이고

2번에선 이 배열은 2차원 포인터 배열이니 읽을 때는 점프 두번해서 읽어라 라고 알려주기 위한 선언이다.


1번에선 점프하면 값을 읽을 수 있지만

2번에서 값을 읽으려면 점프 + 점프 해야 값을 읽을 수 있는 것이다.



결론


1.

int arr[i][j];

이런 2차원 배열은 int*형을 같는다.

도 arr의 자료형은 int* 이지만

1차원은 int*

2차원도 int *

3차원도 int *

4차원도 int *


2.

int **arr;

이런 다이나믹한 배열에선 배열 변수도 같은 차원을 같는다.

1차원은 int*

2차원은 int **

3차원은 int ***

4차원은 int ****


함수하나에서만 데이터를 사용한다면 큰 무리는 없지만,

다른 함수로 데이터를 넘기기 위해선 두 배열의 차이를 알아야한다.



예제를 들자면








저작자 표시 비영리 변경 금지
신고

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

[C] likely/unlikely macros  (0) 2012.12.14
LNK2005 already defined in LNK1169: one or more multiply defined symbols found  (0) 2012.11.27
다차원 배열  (0) 2012.10.03
#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
Posted by Leo 리오 트랙백 0 : 댓글 0


#ifdef A

...

#endif



#ifdef A

...

#else

...

#endif



#ifdef A

...

#else

#ifdef B

...

#else

...

#endif

#endif



#elifdef (else ifdef)는 어떻게 쓰는건지 모르겠음;

#ifdef A

...

#elifdef B

...

#else

...

#endif

저작자 표시 비영리 변경 금지
신고

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

LNK2005 already defined in LNK1169: one or more multiply defined symbols found  (0) 2012.11.27
다차원 배열  (0) 2012.10.03
#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
extern static 정리  (1) 2012.04.25
Posted by Leo 리오 트랙백 0 : 댓글 0

printf debugging

2012.06.19 15:06 from Program Language/C

printf로 디버깅 할때 끝에 \n newline을 넣어도


print가 제대로 안 될때가 있다.


이땐 printf후에


fflush(stdout); 을 해주면 출력하고 버퍼를 비우게된다.


(당연히 속도는 늦어진다.)

저작자 표시 비영리 변경 금지
신고

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

다차원 배열  (0) 2012.10.03
#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
extern static 정리  (1) 2012.04.25
caller 찾기  (0) 2012.01.27
Posted by Leo 리오 트랙백 0 : 댓글 0

sizeof

2012.06.04 12:56 from Program Language/C


main()

{

        int arr[16];

        int* nptr;

        int* ptr;

        ptr = malloc(sizeof(int)*16);

        printf("SIZE arr=%d , nptr=%d, ptr=%d\n", sizeof(arr), sizeof(nptr), sizeof(ptr));

}

 

결과 : SIZE arr=64 , nptr=4, ptr=4






main()

{

        char arr[16];

        char* nptr;

        char* ptr;

        ptr = malloc(sizeof(char)*16);

        printf("SIZE arr=%d , nptr=%d, ptr=%d\n", sizeof(arr), sizeof(nptr), sizeof(ptr));

}

 

결과 : SIZE arr=16 , nptr=4, ptr=4 



배열 크기는 하나크기*배열크기

포인터 크기는 한 Word(32bits, 4bytes)



main()

{

        char arr[16];

        char* nptr;

        char* ptr;

        ptr = malloc(sizeof(char)*16);

        printf("SIZE arr=%d , nptr=%d, ptr=%d\n", sizeof(int[16]), sizeof(0), sizeof(int*));

}

 

 결과 : SIZE arr=64 , nptr=4, ptr=4


0은 null pointer를 의미하는듯.






저작자 표시 비영리 변경 금지
신고

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

#ifdef #else #endif #elifdef  (0) 2012.08.09
printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
extern static 정리  (1) 2012.04.25
caller 찾기  (0) 2012.01.27
printf uint64_t uint32_t  (0) 2011.12.01
Posted by Leo 리오 트랙백 0 : 댓글 0

http://blog.naver.com/entraiger?Redirect=Log&logNo=120122764718



Extern, Static 변수와 함수

Extern, Static 키워드는 변수와 함수에서 쓰일 때 그 의미가 조금 다르다. 형식과 쓰임새가 차이가 있기 때문이다.

 

 

<Extern 전역변수, 지역변수>

extern 변수는 다른 파일에서 변수를 공유해서 쓰기 위해 있는 키워드인데, 전역변수는 키워드를 생략해도 기본으로 extern선언이 되는 성질이 있다.

아래 코드를 보면,

-----------------------

/*main.c*/

#include <stdio.h>

#include "fun1.h"

int a=1; // 변수 선언

void main(){

f();

printf("%d\n",a); // 여기서의 a는 위의 a

}

-----------------------

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

int a; // 또 다른 선언문

void f(){

a = 7;

printf("%d\n",a); //여기서의 a는 바로 위의 a

}

-----------------------

/*fun1.h*/

void f();

-----------------------

결과는: 7

7

-----------------------

fun1.c에서 바꾼 a값이 main.c의 a에도 적용되어있다.

따라서 main.c와 fun1.c의 전역변수 a는 동일한 변수이거나 항상 싱크가 되어있다는 것을 알 수 있다.

여기에서 main.c와 fun1.c의 선언문에 extern을 붙여보자.

(main.c : int a=1; -> extern int a=1;)

(fun1.c : int a; -> extern int a;)

한쪽파일에만 extern키워드를 쓰든 양쪽에 다 쓰든, 모든 경우에서 결과는 같다.

-----------------------

//간략화한 코드//

/*main.c*/

extern int a=1;

/*fun1.c*/

int a;

-----------------------

/*main.c*/

int a=1;

/*fun1.c*/

extern int a;

-----------------------

/*main.c*/

extern int a=1;

/*fun1.c*/

extern int a;

-----------------------

결과는 항상

7

7

-----------------------

이 된다. 즉, 전역변수로 선언할 경우 extern를 넣든 안 넣든 결과는 똑같아 보인다. extern키워드를 생략해도, 전역변수는 기본으로 extern이기 때문일 것이다. 그런데 초기화구문의 extern과 초기화가 없는 extern선언문은 의미에 차이가 있다. 초기화에 쓰이는 extern은 변수를 새로 정의하는 선언문이라는 의미의 extern이고 실제로 메모리를 할당해서 변수를 정의한다. 그런데 초기화가 없는 선언문은 변수를 새로 정의하는 것이 아닌, “다른 파일(혹은 같은 파일 내의 다른 부분)에서 찾아서 있으면 그것을 가져다 쓰겠다”는 ‘참조선언’의 의미를 갖는 extern이다. 그렇다면 어떤 것이 정의선언이고 어떤 것이 참조선언일까? 아래의 경우를 보자. 아래의 경우는 모두 전역변수로 선언된 경우이다.

-----------------------

전역변수 선언문 종류

1. extern int a=1 -> extern변수 정의 및 초기화

2. int a =1; -> extern 변수 정의 및 초기화

3. extern int a; -> extern 변수(새로 정의하지 않고)참조선언

4. int a; -> extern 변수 정의(0으로초기화) 또는 참조선언

-----------------------

4의 경우는 그 쓰임새가 애매하다. 1이나 2와 쓰일 때는 참조선언이 되고, 3과 쓰일 때는 정의구문이 된다.(컴파일러에서 자동으로 처리함) 애매한만큼 지양해야 할 코드이다.

따라서

-----------------------

/*main.c*/

(extern생략가능)int a=1;

/*fun1.c*/

(extern생략가능)int a=2;

-----------------------

같은 식으로 여러번 초기화를 해주면 둘 다 정의 선언문이므로 ‘여러번 정의했다’는 에러가 뜨게 된다. 같은 a를 두 번 정의했으니 에러가 뜨는 것이다. 아까처럼

-----------------------

/*main.c*/

(extern생략가능)int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

(extern생략가능)int a; //a라는 extern변수를 다른 곳에서 찾아서 쓰겠다는 의미

-----------------------

로 코딩을 했을 경우, 에러가 없다. 이 관계는 다음의 예시에서 더욱 명백해진다.

-----------------------

/*main2.c*/

int a=1;

int a;

void main(){

int b=1; (1)

int b; (2)

}

-----------------------

새로운 main2.c파일에서 a는 전역변수, b는 지역변수로 선언되었다. 처음 int a=1;로 선언과 동시에 초기화되었고, 바로 아래줄에서 int a;는 선언이 아닌, “다른 곳에서 a를 찾아본 뒤에 그것을 쓰겠다”는 표현이 되므로 에러가 나지 않는다.(extern int a;의 생략형이라고 이해하면 쉬울 것이다.) 그러나 지역 변수 b의 경우 “여러번 재정의”에러가 뜬다. 잠깐 지역변수부분만 살펴보면, extern키워드를 통해 초기화하는 것이 아예 불가능하다.

-----------------------

void main(){

extern int b=1;

}

-----------------------

처럼 코딩을 하면, “블록 범위를 사용하여 extern변수를 초기화할 수 없습니다.”라는 에러메세지가 뜬다. 블록 범위, 즉 로컬변수선언시엔 무조건 그 블록 범위 내에서만 사용하게 되어있으므로, 다른 곳에서 이 변수를 사용할 수 있도록 하는 extern초기화 구문을 사용 못하게 막아놓은 것이다. 로컬변수를 다른 곳에서 사용하려면, 함수 결과값으로 끄집어내서 사용하는 수 밖에는 없다. 대신 초기화가 아닌 extern참조선언문은 함수 내에서도 전역에서 쓰듯이 사용이 가능하다.

-----------------------

// 간략화한 코드 //

/*main.c*/

(extern생략가능)int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int a; // 다른 곳의 extern변수 a를 찾아 쓰겠다는 의미

}

-----------------------

그런데 지역변수에서 extern변수를 참조선언해서 가져다 쓸 때 주의할 점이 있다. 함수 밖에서 참조선언을 할 때는 참조에 실패하면 컴파일 에러가 나지만, 함수 내에서 참조선언을 하면 참조할 것이 없어도 컴파일 상의 에러가 없다.(헉쓰...) 아래의 코드를 보자.

-----------------------

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

extern int b; //extern변수 b참조선언.

-----------------------

결과: “b의 외부기호를 확인할 수 없다”는 에러.

-----------------------

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int b; //extern변수 b참조선언.

}

-----------------------

결과: no error

-----------------------

아래의 경우처럼 에러가 없으면 나중에 있지도 않은 b변수를 사용할 때 에러가 난다.

-----------------------

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int b; //extern변수 b참조선언.

printf("%d",b); //변수 b 사용

}

-----------------------

결과: “b의 외부기호를 확인할 수 없다”는 에러.

-----------------------

따라서 결국 b를 안전하게 사용하고 싶으면 전역변수로 참조선언 한 뒤에 함수에서 쓰는 것이 좋은 코딩이라는 이야기다. 아래의 코드가 그렇다.

-----------------------

/*main.c*/

int a=1; //extern변수 정의 및 초기화

int b=2; //extern변수 b를 추가해보았다.

/*fun1.c*/

extern int b; //extern변수 b참조선언은 밖에

void fun1(){

printf("%d",b); //변수 b 사용

}

-----------------------

결과: 2

-----------------------

정리하자면, 전역변수는 초기화인 경우 항상 쓰지 않아도 extern으로 정의가 되며, (즉 필연적으로 모든 전역으로 정의되는 변수는 이름 하나당 변수 하나가 최대다. 단, static변수로 만들지 않을 경우에 한해서) 초기화구문이 아닌 경우엔 extern키워드를 쓰면 확실히 참조선언문이 되지만 extern을 생략하면 경우에 따라 참조선언도 되지만 정의가 되어있지 않았다면 정의구문이 되어버린다. 따라서 extern은 기본적으로 생략 가능하지만 헷갈림을 방지하고 코딩의 가독성을 위해서 아래와 같이 여러 파일에서 사용할 때는 extern키워드를 명시해주는 것이 좋다.

-----------------------

/*main.c*/

extern int a=1; // extern변수를 선언하겠다는 의미. 다른 파일에서도 사용하겠다는 뜻.

생략해도 크게 상관은 없다.(외부에서 쓸 일이 있을 땐 생략 삼가)

/*fun1.c*/

extern int a; //다른 곳에 있는 extern변수를 가져다 쓰겠다는 의미.

생략하면 위의 경우와 구분이 어렵다.(쓰는 것을 필수)

-----------------------

덧붙이자면, 아래의 경우는 어떨까?

-----------------------

/*main.c*/

extern int a;

/*fun1.c*/

extern int a;

-----------------------

둘 모두 참조선언문이라 어디에서도 정의가 되어있지 않기 때문에, 외부 기호를 확인할 수 없다는 에러가 뜨는 것은 당연하다.(전문용어로 찾다가 GG) 그럼 아래의 경우는 또 어떨까?

-----------------------

/*main.c*/

int a;

/*fun1.c*/

int a;

-----------------------

둘 중 하나의 구문의 정의 선언문이 되고 나머지가 참조선언문이 되는 것은 확실하다. 그러나 그게 어느것인지는 나도 모르겠다.

 

 

<Static 전역, 지역변수>

아까 전역변수의 경우에는 extern키워드를 생략해도 자동으로 extern선언이 된다고 했다. 그렇다면 한 파일 내에서 전역변수로 선언해서 쓰고는 싶은데 다른 파일에서 참조하는 것은 막고 싶을 경우(전역변수지만 extern은 아닌)는 어떻게 할까? 그럴 땐 static선언을 하면 된다.

-----------------------

/*main.c*/

static int a=1; //static은 생략하면 안 된다.

/*fun1.c*/

int a; // int a=2, 혹은 extern int a=2도 가능

-----------------------

위의 경우, static변수로 선언한 main.c의 a는 main.c파일 안에서만 사용 가능하고 다른 파일에서는 extern키워드로 참조할 수 없다. fun1.c에서 선언한 a는 main.c의 a와는 다른 메모리에 있는 a이다. 이 경우는 fun1.c의 a가 extern변수이고 main.c의 a는 extern변수가 아닌 mian.c에 속박된 static변수가 되는 것이다. 따라서 아래와 같이 쓰면 에러가 난다.

-----------------------

/*main.c*/

static int a=1; //static은 생략하면 안 된다.

/*fun1.c*/

extern int a;

-----------------------

extern int a;에서 참조선언을 했으므로 다른 곳에서 a를 찾아서 가져다 써야할텐데, 유일한 a는 static으로 묶여있으니 참조를 할 수 없다. 그래서 외부기호 확인 불가능 에러가 뜨는 것이다. 지역변수의 경우엔 extern변수 정의가 불가능하므로 static으로 따로 정의할 필요가 없을 것이다.

<extern, static 함수>

함수의 경우 extern, static개념이 간단하다. 함수는 쓰임새에 따라서 외부함수, 내부함수로 나눠 부르지만 사실 전역, 지역의 개념이 없고 굳이 말하자면 모두 전역이기 때문이다.

기본적으로 함수선언은 생략해도 모두 extern이다.

아래 코드를 보자.

-----------------------

/*main.c*/

#include <stdio.h>

void f(); (1)

void main(){

f();

}

-----------------------

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

-----------------------

/*fun1.h*/

void f();

-----------------------

결과: fun1.c

-----------------------

main.c에서 헤더파일을 추가하지도 않았는데, fun1.c의 함수를 가져다 쓴 것을 알 수 있다. fun1.c의 f()함수 선언 자체가 extern으로 되어있고, main.c의 (1)에서도 extern으로 선언해서 다른 파일의 함수를 자동으로 검색해서 call했기 때문이다. 이 점은 위 코드 어느 부분에 extern을 넣어도(심지어 함수정의부분에서도) 똑같은 결과가 나온다는 점에서 알 수 있다. 심지어 (1)을 생략해도 경고가 뜨지만 실행은 된다.(하지만 피해야 할 코딩방법) 함수는 기본적으로 extern으로 선언된다는 것을 알 수 있는 부분이다. 아래와 같이, main.c에서도 같은 이름의 함수 f()를 정의하면 어떻게 될까?

-----------------------

/*main.c*/

#include <stdio.h>

void f();

void main(){

f();

}

void f(){

printf("main.c\n");

}

-----------------------

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

-----------------------

/*fun1.h*/

void f();

-----------------------

결과: 함수 재정의 오류

-----------------------

main.c에서 이미 정의한 f()함수가 자동으로 extern이기 때문에, fun1.c에서 또 정의를 해주면 이것도 자동으로 extern이고, extern함수는 고유해야하기 때문에 같은 함수를 두 번 정의한 것으로 인식해서 에러가 나게 된다. 이것을 막고, 두 함수를 모두 사용하고 싶다면

그 때 static 키워드로 선언을 하면 된다.

-----------------------

/*main.c*/

#include <stdio.h>

static void f(); (1)

void main(){

f();

}

void f(){

printf("main.c\n");

}

-----------------------

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

-----------------------

/*fun1.h*/

void f();

-----------------------

결과: main.c

-----------------------

잘 보면 mian.c의 f()함수는 static으로 선언되었고, fun1.c의 함수는 extern으로 선언되었으므로, main.c에서 실행한 f()함수는 mian.c에서 정의한 함수가 되며 출력도 그렇게 나온다. 반대로 mian.c의 함수를 extern으로, fun1.c의 함수를 static으로 정의해도 결과는 똑같다. 둘 다 static으로 정의해도 마찬가지다. 이유는 생각해보시길...

신고

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

printf debugging  (0) 2012.06.19
sizeof  (0) 2012.06.04
extern static 정리  (1) 2012.04.25
caller 찾기  (0) 2012.01.27
printf uint64_t uint32_t  (0) 2011.12.01
ignoring return value of 'system', declared with attribute warn_unused_result  (0) 2011.11.11
Posted by Leo 리오 트랙백 0 : 댓글 1

caller 찾기

2012.01.27 10:52 from Program Language/C

누가 이 함수를 호출할까??

궁금할땐 
1. grep같은걸로 text로 찾기
2. gdb를 걸어놓고 bt로 확인
3. eclipse같은 툴로 caller확인
4. pointer를 이용해 찾기
#include <linux/kallsyms.h>

#define PRINT_INFO() 
do { 
    printk("%s starting( caller:", __FUNCTION__); 
    print_symbol("%s)n", (unsigned long) __builtin_return_address(0)); 
} while (0)



신고

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

sizeof  (0) 2012.06.04
extern static 정리  (1) 2012.04.25
caller 찾기  (0) 2012.01.27
printf uint64_t uint32_t  (0) 2011.12.01
ignoring return value of 'system', declared with attribute warn_unused_result  (0) 2011.11.11
format not a string literal and no format arguments  (0) 2011.11.11
Posted by Leo 리오 트랙백 0 : 댓글 0
C 언어 표준에서는 자료형의 정확한 사이즈를 정의하지 않고 있다. 예를들어 int는 몇 바이트, long은 몇 바이트와 같이 정의되어 있지는 않고 아래와 같이 최소한의 제약사항을 두고 있을 뿐이다.

char type은 127 이상을 저장할 수 있다
short int type과 int type은 32,767까지 저장할 수 있다
long int
는 2,147,483,647까지 저장할 수 있다

이 규칙은 char가 적어도 8 bit가 되어야 한다는 것과, short int와 int는 적어도 16 bit여야 한다는 것과, long int는 적어도 32 bit가 되어야 한다는 것을 뜻한다. 실제 각 자료형의 사이즈는 implementation마다 다르다.

따라서 C99에서는 int16_t, uint32_t 등과 같이 정확한 사이즈의 타입을 <inttypes.h>에 정의해 놓았다. 그래서 프로그래머는 프로토콜 정의와 같이 정확한 사이즈의 타입이 필요할때 이 타입들을 사용하면 된다.

하지만 이 타입들을 사용할때 printf로 출력하려고 하는 경우 어떤 출력 포멧을 사용해야할지 알 수 없다. 왜냐하면 시스템마다 int64_t의 실제 타입이 다를테니까. 

그래서 이럴때 사용하라고 표준에서 아래와 같은 메크로를 정의해 놓았다.

intN_t int_leastN_t int_fastN_t intmax_t intptr_t
PRIdN PRIdLEASTN PRIdFASTN PRIdMAX PRIdPTR
PRIiN PRIiLEASTN PRIiFASTN PRIiMAX PRIiPTR
uintN_t uint_leastN_t uint_fastN_t uintmax_t uintptr_t
PRIoN PRIoLEASTN PRIoFASTN PRIoMAX PRIoPTR
PRIuN PRIuLEASTN PRIuFASTN PRIuMAX PRIuPTR
PRIxN PRIxLEASTN PRIxFASTN PRIxMAX PRIxPTR
PRIXN PRIXLEASTN PRIXFASTN PRIXMAX PRIXPTR

표 출처: 신성국님의 CFAQs

사용법은 아래와 같다.
uint64_t total = 1000000;
snprintf(buf, sizeof(buf), "TEST: %" PRIu64 " (sec)\n", total);
신고
Posted by Leo 리오 트랙백 0 : 댓글 0



  1. system("ls");
이런식으로 썼을 때 

warning : ignoring return value of 'system', declared with attribute warn_unused_result 이 난다.


한수가 아래와같이 선언되면 warning을 출력한다.
  1. int __attribute__((warn_unused_result))
  2. foo(void)
  3. {
  4.   return -1;
  5. }

따라서, 
  1. int re = system("ls");

warning이 없어진다.












http://studyfoss.egloos.com/tb/5310361




신고
Posted by Leo 리오 트랙백 0 : 댓글 0


  1. char* c;
  2. printf(c);

귀찮아서 위처럼 썼더니

warning : format not a string literal and no format arguments


  1. char* c;
  2. printf("%s",c);
 
신고
Posted by Leo 리오 트랙백 0 : 댓글 0
http://smlweb.cpsc.ucalgary.ca/ 



Context-Free Grammar를 넣는다.
화살표는 ->
하나의 none-terminal rule이 끝나면 .
or는 |
각각 none,terminal은 띄어쓰기로 구분.

그리고 View Vital Statics를 클릭-



first와 follow가 나온다.

LL(1)이 아니고 이유도 나온다. 

LL(1)을 만들기 위해서 transform 클릭



left-recursive와 first가 같아서 LL(1)이 아니라니까

remove left recursion 실행, left-factor 실행


LL(1)완성!



parsing table도 그려준다.






 


 
신고
Posted by Leo 리오 트랙백 0 : 댓글 0
http://forum.falinux.com/zbxe/?mid=C_LIB&listStyle=gallery&page=4&document_srl=408545



설명

자식 프로세스 작업이 끝날 때 까지 대기하며, 자식 프로세스가 종료한 상태를 구합니다.

wait()함수를 실행하면 자식 프로세스가 종료될 때가지 대기하게 됩니다. 만일 자식 프로세스가 정상 종료하여, main()에서 return 으로 값을 반환하거나, 또는 exit()로 값을 반환하며 정상 종료했다면 wait( int *status) 에서 status의 변수 값의 상위 2번째 바이트에 반환 값을 저장합니다.

또는 어떤 시그널에 의해 종료되었다면 최하위 바이트에 시그널 번호가 저장됩니다. 즉,

  8비트 8비트
정상 종료 프로세스 반환 값 0
비정상 종료 0 종료 시킨 시그널 번호
헤더 wait.h
형태 pid_t wait(int *status)
인수 int status 자식 프로세스 종료 상태
반환 pid_t 종료된 자식 프로세스 ID

wait() : 부모 프로세스가 자식 프로세스가 종료했음을 확인하는 함수.
waitpid() : 인자로 프로세스ID를 받음으로써 특정 자식 프로세스의 종료를 기다릴수 있다.

함수원형

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);


※ 자식 프로세스의 상태를 확인하는 매크로들

매크로

세부사항

WIFEXITED(status)

0이 아닌 값을 리턴하면 자식프로세스가 정상종료했다는 뜻이다.

WEXITSTATUS(status)

WIFEXITED(status)매크로를 통하여 자식 프로세스가 정상종료했음을 확인하면 이 매크로를 통하여 종료 코드를 확인할 수 있다. 이 종료 코드는 exit()나 _exit()에서 인자로 주는 값을 말한다. 즉 exit(0)으로 프로그램을 종료했다면 이 0 값이 WIFEXITED 매크로로 알수 있다. 단, 이 매크로는 하위 8비트 값만을 확인하므로 0부터 255까지의 값까지 확인할 수 있다.

WIFSIGNALED(status)

이 매크로가 참이라면 자식 프로세스가 비정상 종료했다는 뜻.

WTERMSIG(status)

SIFSIGNALED(status)매크로가 참일 경우 자식 프로세스를 종료시킨 시그널 번호를 얻는 매크로

WIFSTOPPED(status)

이 매크로가 참이면 자식 프로세스는 현재 멈춰있는(stopped) 상태이다. 다음에서 살펴볼 option인자에 WUNTRACED옵션이 설정되어 있는 경우 자식 프로세스의 멈춤 상태를 알아낼수 있다.

WSTOPSIG(status)

WIFSTOPPED(status)매크로가 참일 경우 자식 프로세스를 멈춤상태로 만든 시그널번호를 얻는다.

WCOREDUMP(status)

시스템에 따라서는 WIFSIGNALED(status)가 참일 경우 자식 프로세스가 core덤프 파일을 생성했는지를 확인하는 이 매크로를 제공해주기도 한다.

※ waitpid()에서 사용하는 pid인자값의 의미

pid의 값

세부사항

pid < -1

pid의 절대값과 동일한 프로세스 그룹ID의 모든 자식 프로세스의 종료를 기다린다.

pid == -1

모든 자식 프로세스의 종료를 기다린다.
만약 pid값이 -1이면 waitpid함수는 wait()함수와 동일하다.

pid == 0

현재 프로세스의 프로세스 그룹ID와 같은 프로세스 그룹ID를 가지는 모든 자식 프로세스의 종료를 기다린다.

pid > 0

pid값에 해당하는 프로세스 ID를 가진 자식 프로세스의 종료를 기다린다.

※ waitpid()에서 사용하는 option인자

인자

인자의 의미

WNOHANG

waitpid()를 실행했을 때, 자식 프로세스가 종료되어 있지 않으면 블록상태가 되지 않고 바로 리턴하게 해준다.

WUNTRACED

pid에 해당하는 자식 프로세스가 멈춤 상태일 경우 그 상태를 리턴한다.
즉 프로세스의 종료뿐 아니라 프로세스의 멈춤상태도 찾아낸다.





시그널 종류

설명

SIGHUP

Hangup을 위한 시그널로 터미널과 시스템 사이에 통신 접속이 끊어졌을 때 터미널에 연결된 프로세스들에게 커널이 보내는 시그널이다.

SIGINT

Interrupt를 위한 시그널로 유저가 인터럽트를 발생시키는 키를 입력했을 때 그와 연결된 프로세스에게 커널이 보내는 시그널이다이 시그널은 프로세스를 종료할 때 많이 사용되는 시그널이다.

SIGQUIT

Quit를 위한 시그널로 유저가 터미널에서 Quit 키를 치면 커널이 프로세스에게 SIGQUIT 시그널을 보낸다.

SIGILL

illegal 명령즉 비정상적인 명령을 수행할 때 OS가 발생시키는 시그널이다.

SIGTRAP

Trace Trap을 위한 시그널로 디버거들이 주로 사용하는 시그널이다.

SIGABRT

Abort 실행시 발생하는 시그널로 Abort는 시스템이 비정상적으로 종료될 때 해당 정보를 남기는 명령이다.

SIGIOT

SIGABRT와 유사한 작업을 수행시 발생하는 시그널이다.

SIGEMT

Emt 명령 실행시 사용되는 시그널이다.

SIGFPE

Floating 포인터 예외사항즉 부동소수점 사용에서 오버플로우나 언더플로우가 발생했을 때 사용되는 시그널이다.

SIGKILL

프로세스가 다른 프로세스를 Kill 시키기 위해 발생하는 시그널이다.

SIGBUS

Bus에러가 발생했을 때 사용되는 시그널이다.

SIGSEGV

메모리 세그먼트 등이 깨졌을 때 발생하는 시그널이다.

SIGSYS

시스템 호출을 할 때 잘못된 인수를 사용하면 발생하는 시그널이다.

SIGPIPE

파이프에서 사용하는 시그널로 아무도 읽지 않는 파이프에 데이터를 출력할 때 발생하는 시그널이다.

SIGALRM

알람 클락 시그널로 해당 타이머가 끝나면 발생되는 시그널이다.

SIGTERM

kill에 의해 프로세스가 종료할 때 발생되는 시그널이다.





신고

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

format not a string literal and no format arguments  (0) 2011.11.11
The Context Free Grammar Checker  (0) 2011.10.12
signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
Posted by Leo 리오 트랙백 0 : 댓글 0
  1.         FILE* file = fopen(FileName,"r");
  2.         if(file==null)
  3.                 perror("error : file open error\n");
  4.  
  5.         int a,b,c;
  6.         for(;~fscanf(file,"%d %d %d\n",&a,&b,&c);)
  7.                 printf("... %d arrives at time %d ... %d seconds\n",b,a,c);
  8.         fclose(file);
신고

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

The Context Free Grammar Checker  (0) 2011.10.12
signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
gcc -D  (0) 2011.01.13
Posted by Leo 리오 트랙백 0 : 댓글 0

exec함수 군

2011.04.19 15:43 from Program Language/C

1)int execl(const char *path, const char* arg0, ..., const char* argn, (char*) 0);
2)int execlp(const char* file, const char* arg0. ..., const char* argn, (char*) 0);
3)int execle(const char* path, const char* arg0, ..., const char* argn, char* const envp[]);
4)int execv(const char* path, char* const argv[]);
5)int execvp(const char* file, char* const argv[]);
6)int execve(const char *path, char* const argv[], char* const envp[]);

함수형태:
exec + (l or v) (+p) (+e)

argument 전달 형태
l : List로 하나하나씩 마지막은 null포인트로 끝을 알려줘야함.
v : Array로 한꺼번에.

실행파일 경로
p: path까지 포함해서 넣어줘야한다.ex)/bin/ls
file : 환경변수 PATH를 찾아서.. 즉,그냥 명령라인에서 쓰는거처럼 ex) ls    ,   ex2) ./a.out

환경변수 포함 여부(아직 부정확함)
e: 환경변수 임이 추가




 

exec함수 군


위쪽 execl 군과
아래쪽 execv 군으로 나눌 수 있고,

첫째 p군과
둘째 file군,
셋째 e군으로 나눌 수 있다.








[출처] fork()와 exec() [펌]|작성자 주주

신고

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

signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
gcc -D  (0) 2011.01.13
PAPI - Performance Application Programming Interface  (0) 2010.12.30
Posted by Leo 리오 트랙백 0 : 댓글 0

freopen

2011.03.31 18:55 from Program Language/C

freopen

 #include <stdio.h>
FILE *freopen(const char *filename, const char *mode, FILE *stream);

 freopen는 열린 스트림을 주어진 파일로 대치한다.  파일 열기의 성공적여부에 관계없이 스트림을 닫는다.  freopen은 stdin, stdout, stderr에 연관된 파일을 변경할 때 유용하다.

  r      읽기 만을 위해 연다.
  w     쓰기 위해 생성한다.
  a     부가, 파일의 끝에서 쓰기를 위해 열기, 또는 파일이 없는 경우에 쓰기를 위해 생성한다.
  r+    파일을 갱신하기 위해 이미 있던 파일을 연다.
  w+   갱신을 위해 새로운 파일을 생성한다.
  a+   부가시키기 위해 열기. 파일 끝부분에서 갱신하기 이해 열거나 새로운 파일을 생성하기 위해 연다.

more..


반환값
 성공적으로 수행을 마친 경우 freopen은 인수 stream을 반환한다.  에러가 발생한 경우에는 널을 반환한다.
 

예제 stdout
 #include <stdio.h>

int main(void)
{
  /* redirect standard output to a file */
  if (freopen("OUTPUT.FIL", "w", stdout) == NULL)
  fprintf(stderr, "error redirecting stdout\n");

  /* this output will go to a file 파일스트림에 써지게된다. */
  printf("This will go into a file.");

  /* close the standard output stream 닫고 파일작성*/
  fclose(stdout);

  //restore stdout to monitor for linux
 freopen("/dev/tty", "w", stdout);
 freopen("CON", "w", stdout);//window CON or CON:
  return 0;
}
 

예제2 stdin
#include <stdio.h>

int main(void)
{
/* redirect standard input to a file */
if (freopen("INPUT.FIL", "r", stdin) == NULL)
fprintf(stderr, "error redirecting stdin\n");


fclose(stdin);
//restore stdin from keyboard(terminal) for linux
freopen("/dev/tty", "r", stdin);
//freopen("CON", "r", stdin);//window CON or CON:
return 0;
}

신고

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

signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
gcc -D  (0) 2011.01.13
PAPI - Performance Application Programming Interface  (0) 2010.12.30
Posted by Leo 리오 트랙백 0 : 댓글 0

gcc -D

2011.01.13 16:35 from Program Language/C






more..






신고

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

signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
gcc -D  (0) 2011.01.13
PAPI - Performance Application Programming Interface  (0) 2010.12.30
Posted by Leo 리오 트랙백 0 : 댓글 0

<strong>Related Sites</strong>



우선 x86 리눅스 기준으로 쓴다.

PAPI를 사용하려면 prefctr패치가 필요한데

리눅스 커널 2.6.31 이후 버전에서는 패치가 깔려있다.

커널 prefctr패치


PAPI소스를 받는다.
#wget http://icl.cs.utk.edu/projects/papi/downloads/papi-4.1.1.tar.gz

압축을 풀고,

src폴더에서 cd src

#./configure

#make

#make test
했을 때 PASS가 뜨면 제대로 설치 된것이다.

기타 에러가 뜨면 prefctr패치가 안되있을 가능성이있다.

#make fulltest
Failed가 뜨는데 아마 하드웨어 자체에서 미지원일것이다.

#make install-all
설치

-설치 끝-


http://www.psc.edu/general/software/packages/papi/examples/
테스트코드를 ipapi 라이브러리를 링크시켜 컴파일 해 본다.
#gcc zero.c -lpapi

#./a.out
신고

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

signal handler 고찰  (0) 2011.06.03
fopen 과 fscanf로 읽어들이기  (0) 2011.05.31
exec함수 군  (0) 2011.04.19
freopen  (0) 2011.03.31
gcc -D  (0) 2011.01.13
PAPI - Performance Application Programming Interface  (0) 2010.12.30
Posted by Leo 리오 트랙백 0 : 댓글 0

티스토리 툴바