추억속의 버그 : C, C++

geekforum의 이미지

프로그램 짜면서 버그한번 없었던 사람은 아마 없겠죠. 아무리 실력있는 사람이라도 초보시절은 있었을 테고 그 실력까지 올라가기 위해 무지하게 많은 버그들을 양산해 냈을 거라는 생각이 듭니다. 마찬가지로, 아무리 좋은 프로그램이라도 버그가 없다고 확신할 수 있는 프로그램 역시 거의 없겠죠.

프로그래머로서 오늘도 버그와 싸우고 있는 여러분들...여러분들은 과연 어떤 버그를 만들어 냈었는지요? 누구나 한번쯤은 양산해 보았을 친근한(?) 버그들이나 여러분의 기억속에 추억으로 남아있는 버그가 있으면 이야기해 보자고요....

* 관리자 코멘트(webmaster):
일단 이번에는 C, C++언어에서 발생했던 버그로 한정합니다. Java등 다른 언어에 대한 버그는 다음에 다시 다루도록 하겠습니다. :-)

cppig1995의 이미지

for(i=0; i>MAX; i++) printf("%d. 반복 출력 예제", i);

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

익명 사용자의 이미지

for(i=0; i< MAX ; i++);
{
some code here;
}

습관적인 ;타이핑 이건 정말 잘안보이더군요.
특히 코드 길이가 길어지면.. .

잘라 버리고 싶은 나의 오른손 새끼 손가락.. ㅋㅋ

익명 사용자의 이미지

VI로 한참을 코딩후..
GCC를 때렸는대...

error..

원인은..

vi편집기 저 뒤로 뒤로 뒤로.....

..
..
..
..
..
................................... 한 이쯤에 ;이게 하나 더 있었다는..

익명 사용자의 이미지

글쓴이:ㅡㅡ;;

첨배울때: 에러가 엄청나게 많이 발생해서
엄두조차 안났다. 결국엔 ; 안붙인 문제였다

Pro*C 에서 다른사람이 짠프로그램이 오동작을
가끔하여.. 사람환장하게 만들었다..
결국 알고보니.. singlebyte 를 singlebute
라고 써놨더라..
이런오타를 프리컴파일러는 잡아내지 못한다.

익명 사용자의 이미지

Pro*C 같은 것들은 에러 처리가 정말 짜증날 정도죠.

ohhara의 이미지

제가 고생했던 버그들 유형 모음. :)

#include

main()
{
{
unsigned char a = '\xf0';
signed char b = '\xf0';
if (a == b)
printf("equal\n");
else
printf("not equal\n");
}

{
unsigned char a = '\xf0';
if (a == '\xf0')
printf("equal\n");
else
printf("not equal\n");
}

{
unsigned int a = 1;
signed int b = -1;
if (a > b)
printf("a is bigger\n");
else if (a < b)
printf("b is bigger\n");
else
printf("a and b are equal\n");
}

{
signed char a0 = -1;
unsigned int a1 = a0;
unsigned char b0 = a0;
signed int b1 = b0;
if (a1 == b1)
printf("equal\n");
else
printf("not equal\n");
}

{
signed char a0 = -1;
unsigned short a1 = a0;
signed int a2 = a1;
signed int b2 = a0;
if (a2 == b2)
printf("equal\n");
else
printf("not equal\n");
}

{
signed char a0 = -1;
unsigned short a1 = a0;
unsigned int b1 = a0;
if (a1 == b1)
printf("equal\n");
else
printf("not equal\n");
}

{
signed char a0 = -1;
signed short a1 = a0;
unsigned int b1 = a0;
if (a1 == b1)
printf("equal\n");
else
printf("not equal\n");
}
}

--
Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net
Alticast http://www.alticast.com
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
--
--
Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net
Alticast http://www.alticast.com
Postech ( Pohang University of Science and Technology ) http://www.postech.edu

Taeho Oh ( ohhara@postech.edu ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Alticast Corp. http://www.alticast.com

opcon의 이미지

저는

먼저 100개 짜리 배열 하나 선언 하고

for 루프를 도는데

strData[i] = "Test";

이렇게 해야 할것을

strData[100] = "Test";

이렇게 해 버려서 자꾸 에러 나더

라고요 그거 못찾아서 하루 종일 헤매고

현상금 까지 걸었던 기억이 있네여 ㅋㅋㅋ

익명 사용자의 이미지

며칠 전에 발견한 팀작업중 다른사람이 만든 버그..
코드가 bool 함수를 쓰면서 리턴값을 검사하지 않고 그냥 void 함수처럼 쓰고 있었다.
난 리턴값을 검사해서 처리하는데,
왜 아무 이상도 없는데, 프로그램이 죽는 것이다. 보니까...

bool 함수이름
{
...
에러나면 return false;
....
....
....
에러나면 retrun false;

return false; <- 헉!!!!!!!!!!
}

몇 개월 전, 정말 심각하게 며칠 동안 고생해서 잡은 버그.. 그것도 아주 우연히...

if(무슨 값>1) SetTimer();

이것은 원래
if(무슨 값==1) SetTimer();
여야 했다.
그냥 아무 생각없이 1 과 같으면이 아닌 그냥 커도 if문이 실행되게 한 것이다.
이것은 정말 엄청난 고통을 가져왔다.
1초간격으로 타임을 뿌려주는데 시간이 정확하게 진행되지 않는 것이었다.
왜냐하면 계속해서 SetTimer()가 호출되고 있었기 때문이다. 헉.......

우리는 OnTimer()상에서 프로세스하는 것이 너무 오래 걸리는 것인 줄알고, 그 프로세스 부분의 알고리즘을 최적화 하고 정말 고통이지 않을 수 없었다.
하지만 나아지지 않았다.
계속해서 이 버그는 못잡고 있었던 어느 날,
갑자기 원작자가 버그를 잡고 있는 내 모니터를 보더니,
"헉 이게 왜 1보다 크다야?"
헉..... 바로 버그 잡았다.

정말 내가 얼마나 소스를 자세히 보았는데...

내 경험에 의하면, 저런 당연할 거 같은 코드에 잘못이 있으면 정말 버그 잡기가 너무 어려웠다.

익명 사용자의 이미지

추억의 버그도 아니고 바로 어제 이 놈 때문에

잠시 황당했던 ...

while(1);
{
if(...){~~~}
else if(....){break;}
else{~~~}
}

copy and paste 의 비애죠 ㅠㅠ..

익명 사용자의 이미지

int a = 0;

int main(void)
{
a = 4;
printf("%d\n", a);
return 1;
}

rom에 쓰여지는 프로그램할때 위와 같이 전역변수를 선언하면 ....롬에 변수가 잡혀서..값이 안바뀝니다..
이것때문에 고생한 적이 있죠...^^
(컴파일러문제 일수도 있고요..)

익명 사용자의 이미지

이런경우에는
Linker 에 도움을 받아 address을 Ram에 Mapping을 하고
초기값을 카피해서 사용합니다..

익명 사용자의 이미지

BC++ 3.0 으로 Programming하던 때였습니다. Graphic Module을 짜고 있었는데 high color mode에서 Pop-Up창을 위한 Memory move 쪽을 만드는데 속도가 안 나와서 inline assembly(DOS Mode)한 300줄 정도 해서 만들었습니다. 그런데 실행만 시키면 무조건 다운되는 것이었습니다. Code를 띄워놓고 만 6시간을 꼼짝도 안하고 쳐다보니 Bug가 보이더군요.....

ldx 0xa0 --> ldx 0xe0

그때는 초보인데다 흥분까지 해서 현상을 보고 원인을 바로 집어내기 힘들었죠.. 하여간 그런 추억이 있습니다. 캬캬캬

익명 사용자의 이미지

아아 C, C++ 한정인데 잘못되었군요... 죄송

익명 사용자의 이미지

FILE *fi;

if( (fi=fopen("babo","r")==NULL) ) {
puts("파일이 안열려유..");
exit( 1 );
}

학부4학년땐가.. 이것땜에 환장할뻔했음..

익명 사용자의 이미지

ㅎㅎㅎㅎ 아주 어렸을적~

이런 난잡한 코드를 짠적이 있었지요..

array선언은 만능인지 알고...

int foo(int size) {
char foostr[size];
return 0;
}

이런실수들 안하셨었나요??? ^^

정적과 동적.. 뭐 한마디로 개념이 없이 살았다라고 보시면 되져.. ㅋㅋㅋㅋㅋ (아이 쪽팔려~^^)

익명 사용자의 이미지

그것을 지원하는 여부는 컴파일러에 의존합니다^^;

gcc같은 경우는 지원하죠.

고로 컴파일러에 따라서 잘못된 코드일 수도 옳은 코드일수도..ㅎㅎ

익명 사용자의 이미지

설마요..
gcc에서 이런식의 동적할당을 지원할리가 없는데요...

어떤 c/c++ 컴파일러도 이런식의 동적할당은 지원안해요~

익명 사용자의 이미지

그런데 이론적으로는 지원하는데 별 문제가 없을 듯 한데요..? 어차피 스택은 함수호출될때 형성되는 것이니까...

익명사용자의 이미지

실행파일에서 스택의 총용량은 고정이고, 가변되지가 않잖아요.
저런방식은 동적메모리 아니던가요?
C++의 new가 저런것을 지원하지만, 스택이 아니고 동적메모리죠.

int foo(int size) {
char foostr[size];
return 0;
}

size가 스택의 용량보다 많이 적을때는 상관없겠지만,
스택과 비슷하거나 함수실행이 스택의 끝부분이라면 당연히 오동작하겠죠.
C99에서 지원하는지 잘은 모르겠지만, 지원한다는것 자체가 이상하군요.
버그 만들라고 장려하는꼴이니깐요.
만약 C99에서 지원한다면 절대 C99를 지원하는 컴파일러는 쓰면 안된다는 결론이 되죠.
혹시 지원되는것이 아니라 선언만하므로 컴파일때 제외되는 코드가 되는것 아닐까요?
그것 이외에는 이해할수가 없는 상황일테니깐요.

char *foo(char *str)
{
char foostr[100];
strcpy(foostr, str);
return foostr;
}

이건 잘알고 있는 버그죠.
그러나 잘 실행될때가 더 많죠.
스택이 겹쳐쓰지기 전에 사용하면 되니깐요.

익명 사용자의 이미지

C99 표준부터는 지원하는걸로 알고 있습니다.

현재 gcc 는 지원하고 VC++6 는 지원을 안하네요..

하지만 VC++6 에서는 가변 배열 처럼 스택에 메모리를 할당할수 있는 함수가 존재합니다.

익명 사용자의 이미지

지원합니다. gcc도 버전에 따라, 또는 운영체제에 따라 지원되는 곳도 있고 지원 안되는 것도 있습니다.
제 친구가 저렇게 코딩했다가 다른 컴퓨터에서 퇴짜 맞은적이 있어서^^;

익명 사용자의 이미지

저랑 비슷한 실수를 하셨군요..

익명 사용자의 이미지

주의 : 이 버그를 따라할시 학교나 직장에서 집단 따돌림을 당할 수 있으니 절대 따라하지 마십시요 ^^:

나의 추억의 버그

if( int == 0 );
{

}

if 문에다 ; 붙여놔서 ... ^^;

난 지금은 코딩안하지만, 참 그때가 그립네요.

Happyhunter의 이미지

동감~~~
프로젝트 하다가 저런 버그 발견 하면 혼자만 알고 있져.. ㅋㄷㅋㄷ
걸리믄.. ㅋㅋ

Happyhunter was here~!!, Have a happy time :-)

gajet_의 이미지

요런 실수들 많이 할 거 같은데요.

if ( i = 100 ) {
...
}

원래는 조건문에 i == 100 으로 쓰려고 했던 건데, 실수로 = 를 하나 빼먹은 거죠...
문법적으로는 정상적인 할당이니까 에러도 발생하지 않는데, 실행해 보면 엉뚱한 결과가 나오구요.

그래서 지금은

상수 == 변수

형태로 쓰는 습관을 들였지요. 실수하면 에러가 발생되도록...

if ( 100 == i ) {
...
}

musiphil의 이미지

"나 같으면 그런 습관을 들일 노력으로 '==' 대신 '='를 쓰는 실수 자체를 안 하도록 하겠다"라고 하는 사람도 본 적 있습니다. :)

어차피 if (상수 == 변수)의 꼴은 if (x == y) 같은 것에는 적용 못 하지 않습니까?

익명 사용자의 이미지

좋은 방법 같네요.

그 습관 괜찮네요 ㅎㅎ

익명 사용자의 이미지

저 역시 같은 경험이...

원인은 간단한데, 찾기까지가 과정이 얼마나 끔찍하던지...

계속해서 결과는 이상하게 나오고, 코드를 보면 아무런 이상이

없는 것처럼 보이고. 그날은 일진이 않좋은지 계속해서 디버거로

돌려도 찾지를 못했다는. 거의 포기할 무렵 운이 좋게 찾았습니다...

익명 사용자의 이미지

요즘 컴파일러는 저렇게 하면 경고가 떠서 좋더라구요... :3

gajet_의 이미지

아.. 그렇군요...

하도 오랜전에 당했던 일이어서 요즘 컴파일러에서 잡아주는 줄도 몰랐네요...^^

익명 사용자의 이미지

비줄씨에는 저거 에러 안뜨던뎅..

글애서 2시간을 헤맨 경험이...캬캬캬캬 ....-_ㅜ

익명 사용자의 이미지

에러는 아니죠 ^^

익명 사용자의 이미지

옷... 저랑 비슷한 경험과 습관이.... ^^;;

익명 사용자의 이미지

이런 오류 경험하신 분들은 흔치 않을 것 같네요. 몇달 전에 ATL로 프로그래밍을 하고 있었습니다. 그런데 어느 부분을 디버깅하고 있는데 계속 수행시키지도 않은 부분이 수행되는 것이었습니다. 그리고 계속 Segmentation Fault가 뜨더군요. 그래서 프로그램의 로직이나 메모리 핸들링이 잘못 되었나 며칠을 고생한 끝에 알아낸 답이 너무 황당하더군요.
일반적으로 VC에서 디버깅을 할때 Watch창에 어떤 변수의 값을 적어 놓지 않습니까? 그런데 실제로 디버거를 돌려서 실행을 시킬 때 VC내의 다른 쓰레드에서 그 부분을 실행시켜 그 값을 알아내는 듯 합니다. 하지만 Watch에는 변수만 들어갈 수 있기때문에 문제가 안되는 듯 하지만 만약 그 변수가 Array일 경우. 또 그 Array의 [] 연산자가 Override되어있을 경우 Override된 함수를 수행시키게 됩니다. 따라서 윈치 않았던 순간에 Watch창에 의해 다른 구분이 실행이 되게 된거죠. 참 재미있습니다. 실험해 보세요.

ex)
CSimpleArray aryTitle;
CSimpleArray aryURL;
int nArySize = 0;
OLECHAR *pStr, *pStr2;
pStr = olestrWideStr;

if (nArySize > 0)
{

CComVector vTitle(nArySize);
if (!vTitle) return E_OUTOFMEMORY;
CComVectorData vdTitle(vTitle);
if (!vdTitle) return E_UNEXPECTED;
CComVector vURL(nArySize);
if (!vURL) return E_OUTOFMEMORY;
CComVectorData vdURL(vURL);
if (!vdURL) return E_UNEXPECTED;

for(int i = 0; i < nArySize; i++)
{
vdTitle[i] = aryTitle[i];
vdURL[i] = aryURL[i];
}
vTitle.DetachTo(ppAryTitle);
vURL.DetachTo(ppAryURL);

*pRetVal = 0;
return S_OK;
}

요럴경우에 if (nArySize < 0)구분에서 watch에 aryTitle[i]가 있다면 메모리 에러가 납니다. 왜냐! VC는 watch창에 있는 aryTitle[i]의 값을 알아내기 위해 그 구분을 다른 쓰레드에서 실행시킵니다. 하지만 그 구분에서는 아직 i라는 값이 할당되어 있지 않은 값이기 때문에 임의의 값이 세팅되게 됩니다. 그럼 override된 CSimpleArray의 []연산자에서 에러가 발생되죠. 재밌죠! ^^;;

익명 사용자의 이미지

저는 요즘에 배우고 있지만, 저가 아주 머리
쥐어짯던 버그(실수)는

class Simple
{
public:
Simple(){);
};
뭐 이런거 였어요;;
콘솔에서 책에서 나온 예제 쓰고있는데 ;;
저 가로를 절대 못보겟더군요; 에러도 이상한데서 나고,
결국 한번 다시 다 치고 찾았다는..-.-;;

익명 사용자의 이미지

앗; 실수^^;;
Simple(){ )
뒤에 세미콜론은 빼야 하는군요:)

Paladin의 이미지

아래 글들은 대개 Syntatic Error/Bug인데, 이런건 뱐 스트로스트럽이 지은 C++ programming language만 제대로 읽었다면 다 해결할 수 있는 문제들고, 게다가 조금의 코딩 집중력만 향상시키면 실수가 다시 되풀이 되지는 않죠.

진짜 찾기 어려운건 Semantic Error/Bug죠.

때로는 어중간한 Bug도 있는데, 예를 들면,

if (조건1) if (조건2) {} else {};
이것을
__ if (조건1)
__ if (조건2) {}
__ else {};
이렇게 Indentation한다고 해서,
__ if (조건1)
__ {if (조건2) {}}
__ else {};
이렇게 되는 것이 아니죠.
이것은 단지
__ if (조건1)
__ if (조건2) {}
__ else {};
이것입니다.
즉 조건1에 대한 else는 없는 것이죠.

8년전에 이것때문에 머리 아파했던 기억이 나는군요. 그 후 구문이 하나만 들어간다하더라도 왠만해서는 Block을 명시적으로 지정합니다.

C를 쓰던 다른 언어를 쓰던 무슨 언어이든지간에 개인적 원칙이 있습니다. "조건이 있는 곳에 Block이 간다"라는 것이죠.

9th Paladin

익명 사용자의 이미지

저는 약간 다른 이유로 조건문에 항상 괄호를 붙입니다. (if 뿐만 아니라 for while 등등) Perl에서는, { }를 항상 붙여야 하기 때문이죠. -_-;; 만약 한줄짜리 조건문이 필요할 경우에는 다음과 같이 합니다.

if ( flag ) { a = 5; }

익명 사용자의 이미지

"조건있는 곳에 Block간다."

저하고 똑같은 신조를 가지고 있군요.

내가 5년전에 팀장하면서 이 규칙이 왜 필요한지 그렇게 설명해도

말 않듣는 친구가 있더군요.

이유는 단 하나. 괄호치는 시간이 아까우니까..

knight2000_의 이미지

저와 같은(정말 같을까?) 되는 이유로 괄호를 치지 않는 친구군요.

저는 디버깅 하는 시간이 아까워서...(어쨌든 시간이 아까우니까)
괄호를 꼭 합니다.

익명 사용자의 이미지

디버깅 및 유지보수는 딴 사람이 할 거니까

자기는 편하다는 생각에서 그렇게 하는 것 같더군요.
(팀 프로젝트의 문제죠..-_-)

Paladin의 이미지

애써 Indentation 해두었는데, 다깨졌군요.

__ 다음에 들어 가는 Tab의 갯수는 차례대로

0
1
0
0
1
0
0
1
2
입니다.

9th Paladin

zzaratra의 이미지

#include

class Foo {
public:
Foo(int x = 1, int y = 2);
void bar(int w = 20);
private:
int z;
};

Foo::Foo(int x = 1, int y = 2) {
z = x + y;
std::cout << z << endl;
}

void Foo::bar(int w = 20) {
std::cout << z + w << endl;
}

int main() {
Foo foo;
foo.bar();

gcc 2.95 에서는 컴파일이 잘 됩니다.
근데..요즘쓰는 gcc3.2 에서는 컴파일 시키면
foo.cpp:11: warning: default argument given for parameter 1 of
`Foo::Foo(int = 1, int = 2)'
foo.cpp:5: warning: after previous specification in `Foo::Foo(int = 1,
int = 2)'

이런 에러가 뜹니다...
즉 메소드 선언할때 값을 정해 주면 막상 그 함수에 대한 내용일때는 쓰면 안됩니다..

예전에 2.95에서는 잘 컴파일 되던것이 3.2 에서는 컴파일 되지 않더라구요...

참고 하세요...

익명 사용자의 이미지

언제나 보면 거의 전부 프로그래머 실수죠. 조그만... --;
요즘엔 버그 있어서 원인 찾는데 금방 잡히면 다행이고 하루 이틀을
지나 사흘 나흘이 지나도 못 잡으면 글자 하나씩 뒤집어 봅니다.
그런 경우 대부분 버그가 아니라 '실수'더라구요... --;
지난 번에는 CPU가 계속 헛지랄을 하길래 한달 넘게 원인을 찾았
는데 알고보니 CPU 클럭이 최소 6Mhz를 넣어야 한다고 매뉴얼 어딘
가 (정말) 조그맣게 써 있더군요. 그 CPU의 이전 버전이 최소 4Mhz
여서 아무 생각없이 4Mhz 넣구 썼는데... --;

두번째 자주했던 실수(요즘엔 그런일 없습니다만)는 VC++에서
ASSERT()나 TRACE()안에 const가 아닌 (이라고 해야 되나?) 코드를
넣는 것... --;

int i ;
...
ASSERT( (++i) == 3 ) ;
...

많은 프로그래머들이 매우 자주 겪고 있는 DEBUG 버전은 잘 돌아
가는데 RELEASE 버전은 안돌아가는 주요 원인이 되는 것을 알고
이젠 저런짓 안하죠. --;

또하나 VC++에서 RELEASE버전 문제를 일으키던 것으로 메시지 맵
에서 ON_MESSAGE() 매크로였나 뭐 그런 녀석이 있는데 이 녀석에게
넘겨주는 함수의 타입이 안 맞으면 컴파일 오류도 없이
DEBUG에서는 잘 실행되다가... RELEASE에 가면 궁시렁댑니다.
RELEASE에서 원인 찾느라고 고생좀 했죠. --; RELEASE에 _DEBUG
정의 없이 DEBUG정보 넣고 디버그해서 TRACE하다가... 별안간
뜨는 에러... 에러가 발생하면 이미 스택은 망가진 상태... --;
뭐, 그런 짓도 좀 있었죠. 근데 ASSERT()나 TRACE()와는 달리
이 문제는 얼마전에도 한번 걸려서 또 된통 당했다는... ㅠ.ㅠ;

버그는 아니지만 위에 어떤 분이 자동 변수 영역의 포인터를
리턴하는 것이 속도가 빠르다고 '최적화'를 말하시길래 생각나는
것이 있군요. (일단 그 분의 말씀은 틀렸죠? 다들 아시죠?)
예전에 8051같은데서 C로 프로그램할때 좀더 빠른 코드 만들려고
이상한 짓 많이 했는데, Effectivc C++을 읽다가 그랬나 하여간
과연 얼마나 빠를까 해서 테스트해봤는데... --; 최적화 안한게
더 빠르더군요. ㅠ.ㅠ; x86의 놀라운 기능(캐쉬,파이프라인,...)
과 그 효능을 체험할 수 있었던 테스트였습니다. --;

-maddog;

Renn의 이미지

누구나 겪을 수 있었던 경험이겠지만, 초기화 하지 않은 변수 덕분에 생기는 버그는 참 암담하더군요.

int a;
...
if (a == ...) ...

아주 흔한 버그죠? 하지만 요즘은 컴파일러가 잡아줍니다.

이런 초기화도 관련되어 있겠지만, 변수가 계속 재활용 되는 곳에서는, 특히 재활용 변수가 많다면 문제가 좀 심각할지도 모르죠.

저 같은 경우는 변수 테이블을 운영하는데 이 테이블을 초기화 해 두는 루틴이 없으니 몇몇 부분에서 말썽을 일으키더군요. 그래서 초기화 모듈을 만드는데 멤버 변수들이 몇백개나 되니 눈이 삥삥;; -_-; 이런건 초기 설계시에 잘 해야 됩니다.;;

버그 아닌 버그인듯한 이야기 하나.
Visual-C++로 프로젝트를 하나 만들어서 컴파일 했습니다. 그 프로그램은 현재 디렉토리에서 config 파일을 읽어옵니다. 기본적으로 실행파일이 생성되는 Debug 폴더에 config 파일을 넣고 돌려보니 파일을 읽을수가 없네요. 도데체 왜 이럴까 하고 수 차례 trace를 걸었지만...
...
알고봤더니 working directory가 설정되어 있지 않아서라는...
역시 버그 처럼 보이는 것도 유저불량입니다. ;-)
--
Seo, Hee-Seung.
http://anitype.net/

macgebi의 이미지

흠.. 이곳에 계신분들 심오한 버그들만 올려놓으셨군요.. ^^

걍 옛날생각나서 제가 겪었던 어이 없는 버그 올려보고 싶어서 가입했습니다.

간단합니다.. 하지만 치명적이었죠

for (int i=0;i {
sum = Func(i);
}

for안의 내용은 임의로 넣었습니다.

왜 문제가 있는지 눈에 잘띄지도 않더군요..

트레이스해서 겨우 찾았습니다.. 이것때메 고생한거 생각하면..

툴이 좋으니까 망정이지.. 거참..
(설마 엠에스꺼 쓰는사람은 여기 글 못쓰는거 아니겠죠...)

익명 사용자의 이미지

첨엔 뭐가 잘못됐지?
하고 생각했슴다.

푸하하!!!

macgebi의 이미지

아고.. 어떻게 고치는지 잘.. 하여튼.. 깜빡했는데..

문제는 for문 첫줄의 ' ; ' 이었습니다. 그럼 이만..

익명 사용자의 이미지

저두 for 문에 한표.. ^^
처음 씨언어 배울때 같은이유로 일주일간 고민했습니다.
한편으론 기뻤고, 한편으론 자신이 한심해 보였는데..
ㅋㅋ

익명 사용자의 이미지

Padding Rule에 대한 버그를 겪은 적이 있는 분을 알고 싶군요.

DBMS사용자가 아니라 개발자, 또는 Protocol Layer 2이하 라이브러리 개발자라면 겪어 보셨을 것으로 생각되는데..

O/S별로 padding Rule에 대해 정확한 정보를 가지고 계신 분 있습니까?

익명 사용자의 이미지

이런 것도 관련이 있을것 같아 리플 달아 봅니다.

http://www.codesourcery.com/cxx-abi/

Paladin의 이미지

Padding이 구조체의 padding을 의미하는 것이라면 그것은 Memory Alignment에 연관되어 있으며, Memory Alignment는 하드웨어 특성에 기인하는 것이지 OS의 차이에 기인하는 것은 아닙니다.

Memory Alignment에 대해서는 이곳저곳에서 자세한 설명을 몇번 하였었지만 이젠 좀 재미없어서 생략하겠습니다.

힌트만 하나 드리지요.

메모리 주소가 0,1,2,3,4,5,6,7이렇게 해서 4G까지 가는데,
하나의 byte짜리 integer는 "주소 0,1,2,3"의 4개 byte에 저장 될 수는 있어도, "주소 1,2,3,4"처럼 4개로는 저장될 수가 없습니다. 왜 이런가 하면, 물리적 메모리가 병렬처리를 하기 위해 그렇습니다.

더 자세한건 컴퓨터 구조론 책을 참조하시면 되겠습니다. 책소개는 생략하겠습니다.

9th Paladin

Paladin의 이미지

"하나의 byte짜리" --> "하나의 4byte짜리"

9th Paladin

익명 사용자의 이미지

무려 1년만에 답이 올라오는군요.

제가 말씀드린 것은 이론이 아니라 O/S별로 Padding Rule에 대한 정보를
가지고 계신 분이 있냐는 것입니다.(Knight님의 설명대로 표현하자면 H/W별로)

내가 HP장비에서 Padding Rule을 몰라 고생한 것을 생각하니 다른 장비에
포팅하는 것이 엄두가 안나서 그렇습니다.

라키시스의 이미지

전 새로운 프로세서에서 프로그램을 시작할 때마다
간단한 프로그램 돌려 보고서 padding rule 부터 확인해 봅니다.
속편해요 그게 :)

익명 사용자의 이미지

"OS 또는 CPU에 따른 Padding rule은 알 수 없고 알 필요도 없다"

라고 가정하고 프로그램해야 안심할 수 있지 않을까요?

BCD로 몽땅 때려 맞추는게 제일 속 편하다는...

익명 사용자의 이미지

이기종간 통신프로그램 짜본 사람은 이런 기대를 할수 없죠.

익명 사용자의 이미지

Encoding/Decoding을 하지 않고 생짜로 바이너리로 보내지 않는 한
신경쓸 필요 없습니다. (저도 비슷한 경험이 있었는데 때문에 Platform
Independent Coder를 만들었습니다, TEXT로도 Binary로도 되도록, OOP가
좋은 점 아니겠습니까? 특히 Smalltalk 같은 실행 시간 바인딩이 되는
놈들, ST, ObjC, Java(이놈은 필요없지만...) 이놈들은 클래스 변수를
바꾸는 방식으로 소스를 고치지 않고도 방법의 교체가 가능하지요. C++라면
당연히 안되겠지요? :-)

익명 사용자의 이미지

왜 안되나요? (질문입니다.)

익명 사용자의 이미지

Simula Schoold의 OOPL들, Simula, C++등은 컴파일 타임에만 OOP입니다. 실행시간에는 Class등의 객체에 대한 정보를 가지고 있지 않습니다. 때문에 실행 중간에, 또는 동적으로 클래스 객체를 바꾸거나 메소드를 추가하는 것은 불가능 합니다.

익명 사용자의 이미지

제가 잘 모르겠어서 계속 질문하는건데요. C++도 동적바인딩이 되잖습니까?
동적바인딩 외에, C++에 없는 다른 요소가 있는 건가요?

익명 사용자의 이미지

와~ 글많네...

체스맨_의 이미지

버그는 그게 문제를 크게 일으키든, 작게 일으키든,
쉽게 잡든, 어렵게 잡든 잡고 나면 별게 아닌 것
같습니다. 버그란게 딱 이거다하고 찝어 말할 수
있다기 보다는 같은 종류의 버그라도 프로그램
환경과 여러 정황에 따라 서로 다르게 나타나기
때문에, 추억속에 버그나, 악몽같은 버그는 무수히
많지만, 그걸 기억해서 적어내는 건 버그 잡는 것
만큼 어렵네요. :-)

C.J.LEE의 이미지

오래전 일이지만 MSC 6.0을 사용할 때의 일이 었습니다.
주로 BC 3.1을 사용하던 저는, 아무 생각 없이 프로그램하다가 몇 개 블럭을 추가하고 나면 프로그램이 돌지 않는..
에러 메세지 인즉 프로그램 사이즈가 넘 크다나..ㅜ.ㅜ;
결국 데이터 영역을 EMS로 올리고 프로그램 다 뜯어 고치고....
BC 3.1에서는 컴팔 자체가 안되니까 좀 빨리 발견하는데....
쓸데 없으면 머리로 못이라도 박아야 하나..쩝..

C.J.LEE의 이미지

오래된 이야기지만 BC 3.1을 사용 할 때의 일입니다.
코딩 하기 전에 프로그램 블럭을 여러개로 만들고
작업을 하다보면 어느듯 소스코드가 1000라인을 훌쩍 넘어 서버리는 일이 발생하게 되는데..
int i;
라고 선언 된 곳에 Error 표시가 되면서
i가 선언 되지 않았다고....
한참을 헤매다가 라인 수를 보면 (ㅡ.+;) ...
결국 900라인 이하까지 줄여서 컴팔 하는데 의도 하지 않은 프로그램 블럭이 또 생기는 바람에 또한번 잔머리..
머리가 나쁘면 손발이 고생한다나..
...

bxhs의 이미지

회사에서 큰 프로그램을 짤때 이런 에러가 생겼는데요
파일이 천개정도 되고,gnu 메이크와 펄,컴파일러로
컴파일을 하게 됐읍니다.

작업을 하기위해 어떤 타겟을 열었읍니다.
#define XXX를 푼것이죠..

근데 이상한 에러가 나더라고요..
에러내용은 전혀 안나오고 그냥 멈추면서..
펄스크립트에서 이상한 메시지를 내보내더군요.

먼저 알지도 못하는 펄스크립트를 뒤졌읍니다.
펄함수 이름으로 대충 짐작해보니까..
외부 레퍼런스가 잘못되었음을 알았죠..

그런데 처음은 전혀 이것에 연관되어 떠오르는
생각이 없는것이었읍니다.
왜냐하면 전혀 엉뚱한 파일에서
펄이 메시지를 내면서 멈췄기때문이죠.
게다가 에러가 난 파일은
제가 연 타겟과 전혀 상관없는 것이었읍니다!

그래서 4,5일을 괴로워하면서 보냈읍니다.

그러다가 남들과 이야기하면서 떠오른 생각이..
내가 타켓을 열어서 이런 문제가 생겼다면,
전혀 엉뚱한 파일에서 에러가 나더라도..

그 파일이 내가 연 타겟과 관련된 파일을 인클루드
하고 있다고 결론을 내렸읍니다.

그런데 직접 인클루드 된것이 너무 복잡하게 되어있어서..
어떤 파일에서 에러가 났는지는 못찾고,
결국 그 타겟을 모두 검색했읍니다.

한참 이렇게 방황하다가..
결국...버그를 찾았는데 똑같은 정의가 각각 다른 파일에서
중복정의되어있었읍니다.

이 경우..어디서 에러가 났는지 직접적으로
알기가 매우 힘들었읍니다.
사실 버그찾은 부분도 전혀 예상치 못한 곳이었으니까요.

여기서 얻은 교훈은...

이런 종류의 에러가 났을때 에러가 난 파일
그 자체를 의심하지 말고..
(백날 뒤져봐야 거기서는 답이 안나오니까요..)
내가 취한 행동과, 에러메시지 그 자체를 가지고..
해결을 해야한다는 것이었읍니다.

GunSmoke_의 이미지

이만용씨의 newbie 테스트는 어떠한가요?

/* newbie test */

#include /* for fprintf, stdout */

int
main()
{
char *ptr;

fprintf(stdout, "%s\n", ptr);
}

익명 사용자의 이미지

전 데몬 프로그램 버그 잡는게 제일 짜증났었습니다.

몇만라인이 넘는 데몬인 경우에.. 특히 심하져..

b+ tree에다가.. cache기능 넣구 이것저것 섞고
thread를 몇백개를 돌리고 cash refresh까지 하고,
라이브러리 구축해서 만든 프로그램일 경우에..

1바이트씩이나 4바이트씩 가끔 증가하는 현상을 잡을려면..

머리에서 쥐가 나데여!

만들어 놓은 모듈별로 돌리고 돌리고 시간과의 싸움입니다.

그래서 찾은 모듈을 처음부터 끝까지 메모리 영역을
계산하고 있으면..

하루가 어떻게 가는지 ㅠ.ㅠ

그래도 모듈별로 만들어 놓구 라이브러리 만들때 한개한개
입력출력에 대한 것을 정의 해 놓아서 잡는 것은 가능하데염

엉성하게 짜놓았다면.. 아마 죽음이었을 겁니다.
여러분도 디버깅 편리하게 짜시길.. 데몬은 정말
디버깅 필수입니다.

익명 사용자의 이미지

purify 써보세요~
C로 짤 때 메모리쪽 버그 잡는데는 그만인 듯 싶습니다.
특히 서버프로그램처럼 메모리누수가 치명적인 프로그램에서는
상당한 디버깅 시간과 사람수명을 단축시켜줍니다.

장정호의 이미지

여러 alternative malloc library를 사용하거나
자기가 직접 memory allocation library를 만들어서 사용하면
비교적 쉽게 잡을 수 있지 않나요?

shjeun의 이미지

-g option의 오묘함에 놀란 적이 있죠~

void func(){
int a[100];
....

return a;
}

이와 같은 루틴을 작성하는 사람을 보고 있으면 종종 뒤통수를 치죠~~ 너는 지역변수도 모르냐면서~
근데 이게 정상적으로 돌아갈때가 있습니다.

바로 -g 옵션을 주면 debug flags로 인하여 지역변수를 해제 하지 않더군요~ 그래서 정상적으로 돌아가네요~

그래서 예외가 있다는 것을 확인한적이 있습니다. ㅋㅋㅋ

:-)

shjeun의 이미지

앗 오타당.

int *func(){
int a[100];

....
return a;
}

이거 입니다~~ 이거 가지고 리플 달지 마세요~ ^^

:-)

익명 사용자의 이미지

님.. 이건 저 밑에 있는 지역변수 사용과 똑같군요..

-g옵션을 안줘도 간단하면 잘 돌아갑니다.

왜냐면.. 지역변수라도.. 메모리에 담겨있다가
리턴이 되면.. 그 메모리가 쓸 수 있는 영역으로 바뀌져
그래서 다른 메모리를 잡을때 이 영역에 잡구.. 데이터를
넣으면.. 원래의 데이터가 날아가고 새로운 데이터가
들어오는데..

그렇게 새로운 메모리가 이 메모리 영역에 침범하지
않았을 경우에는 정상적인 결과를 얻습니다.

정상적으로 쓰인다면.. return한 a값부터 데이터를 넣은 부분까지 문제없이
사용할 수 있습니다.

익명 사용자의 이미지

잘 돌아간다는건 약간의 어패가 있는것 같군요..
잘 돌아가는것 처럼 보인다.. 라는 말이 정확한 표현일지도...

분명한 논리적 에러라고 생각되는데요..

mushim의 이미지

지역변수는 함수가 호출되었을때 메모리의 stack 영역에 생성되기 때문에,
함수가 반환된후, 어떠한 함수도 한번도 호출 되지 않아야만 가능합니다.

실제적인 프로그램에서는 거의 불가능합니다.

dawnsea의 이미지

(컴팔러의 친절함 == 컴파일러의 무책임함)

.. 인 경우가 종종 있습니다.

컴팔러의 친절함을 자주 믿으면 안 될듯 싶습니다.

ㅡ.ㅡ;

종종 메모리와 관련해서 이러한 일들이.. ㅡ.ㅡ;

또는 volatile 문제라든지.. ㅡ.ㅡ;

saxboy_의 이미지

아주 멍청한 짓을 한번 한적이 있네요.
glibc 의 pthread와 malloc implementation 을 믿지 못해서, 테스트코드를 좀 사악하게 한번 짜서 돌려본 적이 있는데, 죽는다고 물어봤다가, 결국은 제 잘못이었다는. --;
(사실은 못 믿는 마음에 컴파일러 경고고 뭐고 보지도 않고 잘못됐다고 투덜댔었지요. )
결국 comp.programming.threads 에 올렸다가 아주 망신당하고 친절하게 지도를 받은적이 한번 있답니다. 좀 썰렁하게 긴 코드지만 -Wall 붙여서 한번 컴파일 해보시면 제가 얼마나 멍청한 짓을 했는가 바로 감잡으실...

교훈 : 잘되면 남탓이요. 못되면 내탓이라...

#include
#include

int get_sum(int x)
{
int ret, i, a;

/* just to give delay,
* the result is not important
*/
for (a=0; a<100; a++) {
ret=0;
for (i=0; i ret+=i;
}
} return ret;
}

void *thread_main_sub_sub(void *arg)
{
char *res;
char *nullmem;

nullmem=(char)malloc(1000);

printf("I'm %s [%d]\n",__FUNCTION__, pthread_self());
printf("I'm %s [%d], calc sum 1000000=%d\n", __FUNCTION__,
pthread_self(), get_sum(1
000000) );
printf("I'm %s [%d], byebye\n", __FUNCTION__, pthread_self());

free(nullmem);
}

void *thread_main_sub(void *arg)
{
pthread_t newtid=0;
char *res;

char *nullmem;

nullmem=(char)malloc(1000);
printf("I'm %s [%d], calling new thread [%d]\n",__FUNCTION__,
pthread_self(), newti
d);
pthread_create( &newtid, NULL, thread_main_sub_sub, NULL);
printf("I'm %s [%d], calc sum 100000=%d\n", __FUNCTION__,
pthread_self(), get_sum(10
0000) );
printf("I'm %s [%d], byebye\n",__FUNCTION__, pthread_self());
pthread_join( newtid, &res);

free(nullmem);
}

void *thread_main(void *arg)
{
pthread_t newtid=0;
char *res;

pthread_create( &newtid, NULL, thread_main_sub, NULL);
printf("I'm %s [%d], calling new thread [%d]\n", __FUNCTION__,
pthread_self(), newti
d);
pthread_join( newtid, &res);
}

#define MAX_THREAD 10

main()
{
pthread_t tid_arr[MAX_THREAD];
pthread_t tid2;
int i;
char *res;

for (i=0; i pthread_create( &tid_arr[i], NULL, thread_main, NULL);
printf("Creating thread [%d]\n", tid_arr[i]);
}

for (i=0; i pthread_join( tid_arr[i], &res);
printf("thread [%d/%d] joined\n", tid_arr[i], MAX_THREAD);
}

printf("Exiting... \n");
exit(0);
}

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

이게 답입니다.

Yep. Ignoring all those warning messages from gcc will get you into trouble. It runs w/o
problems with the changes listed at the end.

Basically
- use the right type cast for malloc
- use the right type for the return value of pthread_join
[though I'd pass NULL like the man page suggests if you are not going to use the return value]

There are plenty of other bugs (e.g., not checking return values for errors), but I'll leave
fixing them to you. :-)
--Mark
---------------------

diff mytest.c test.c
22c22
< /* char *res; */
---
> char *res;
25c25
< nullmem=(char*)malloc(1000);
---
> nullmem=(char)malloc(1000);
28,29c28,29
< printf("I'm %s [%d], calc sum 1000000=%d\n", __FUNCTION__,pthread_self(),
< get_sum(1000000) );
---
> printf("I'm %s [%d], calc sum 1000000=%d\n", __FUNCTION__,
> pthread_self(), get_sum(1000000) );
38c38
< void **res;
---
> char *res;
42,43c42,43
< nullmem=(char*)malloc(1000);
< printf("I'm %s [%d], calling new thread [%d]\n",__FUNCTION__,
---
> nullmem=(char)malloc(1000);
> printf("I'm %s [%d], calling new thread [%d]\n",__FUNCTION__,
49c49
< pthread_join( newtid, res);
---
> pthread_join( newtid, &res);
57c57
< void **res;
---
> char *res;
62c62
< pthread_join( newtid, res);
---
> pthread_join( newtid, &res);
72c72
< void **res;
---
> char *res;
80c80
< pthread_join( tid_arr[i], res);
---
> pthread_join( tid_arr[i], &res);

익명 사용자의 이미지

#include

char* func();
int main() {
char *q;
q = func();
fprintf(stderr , "%s\n" , q );
return 0;
}

char* func()
{
char *z;
char a[30];

z = "XYZ";
strcpy(a , "abcdef");

return z;
/* return a; */
}

이것이 데이터영역이라서 버그가 아닌가요?
전 이제껏 버그인줄 알았는데
로컬함수에서 포인터를 리턴할때
문자열상수에 대한 포인터는
리턴하는게 가능하군요.?

shjeun의 이미지

아래 뿐의 답변이 맞습니다. constant는 이미 메모리에 할당되어 있으니 어디서 return을 해도.. 살아있는 address이니~ 문제가 발생하지 않죠~ ..

오히려 static으로 할 경우에는 여러 모듈로 쪼갤때 다른 object로 export가 되지 않으므로 문제가 발생할 소지가 있습니다.

:-)

익명 사용자의 이미지

어차피 static으로 정의된 변수를 밖에서 직접 참조할 것이 아니라
변수에 대한 주소값을 리턴하는 식으로 밖에서 간접적으로 참조하기 때문에
걱정하신 문제는 발생하지 않을 것 같은데요.

localtime()이나 gltime() 등의 여러 static 함수들이 그렇게 살아갈텐데요. ^^

익명 사용자의 이미지

그 값이 찍 힌다고 된다고 봐서는 안됩니다.
z에 할당되는 포인터는 임시로 만들어졌다 사라지는 메모리에 있습니다.
즉 z = "XYZ"; 이 한줄에 "XYZ"의 lifetime은 국한 겁니다.
다른 곳에서 이 값이 제대로 참조되더라도 그것은 운에 맡기는 것이기 때문에
위험합니다. 만일 함수 밖으로 안전하게 "XYZ"를 return 하려면 다음과 같이 하는게
날겁니다.

char* func() {
static char z[40];
strcpy(z , "XYZ");

return z;
}
이러면 함수 바깥에서 안전하게 "XYZ"를 참조 할수 있겠지요.
문제는 thread-safe에 문제등의 문제가 있지만요.

익명 사용자의 이미지

아닌데...

제가 알기론
이런 경우 "XYZ"는 '초기화된 상수 data'영역에 자리잡게 됩니다.
물론, 님께서 하신 말씀이 틀린건 아니지만,
최소한 "XYZ"는 안전한 상수영역에 배치되기 때문에
별 문제 없습니다.

익명 사용자의 이미지

1) array size 문제
2) close 2번 문제
3) memset 에서 역시 size 문제..

위에 것들이 찾기 힘들더라고요.--;;;

익명 사용자의 이미지

Array size의 경우는 sizeof(Array) / sizeof(Array[0])
memset: memset( data, 0, sizeof(data) )

sizeof 의 적극적인 활용이 필요하죠.. ^^

dawnsea의 이미지

질문 입니다. ㅡ.ㅡ;

ARM 크로스 컴팔용 GCC 에서

16비트 = 하프워드 volatile 은 잘 되는 것 같은데.

32비트 = 풀워드 volatile 에 문제가 있는 것 같습니다.

16비트 * 2 = 32비트 억세스 플래시로 구성하고 있는데.

자꾸 하위워드만 살아남고 상위가 죽어버리는 거에용.. ㅡ.ㅡ; 이거 컴팔러 뻑인가요?

다른 문제 일까요?

익명 사용자의 이미지

죽는다는 말씀은 0xff 로 채워있다는 건가요?
그렇다면 flash command가 제대로 들어가는지 확인해 보심이...

dawnsea의 이미지

답변 고맙습니다. ^^;

근데요~ ㅡ.ㅡ;

라이팅 하고 나면.

16 * 2 = 32 비트로 구성한 플래시에서

하위 16비트는 제대로 써지고 상위 16비트만 안 써진다는. ㅡ.ㅡ;

그래서 버퍼 라이팅을 포기하고 현재는 워드 라이팅으로 쓰고 있습니다. ㅡ.ㅡ;

^^;

익명 사용자의 이미지

버그라고 하긴 뭐하고...
다른사람 코드보다 뒤집어진거...

...
char* s = foo();
...

char* foo()
{
char t[100];
strcpy(t, "bar", 100);

return t;
}

서지원_의 이미지

사실 거의 대부분의 경우에 저렇게 쓰는 것(local variable의
address를 return하는 것)이 의미가 없지만,
아주 잘 쓰면 유용하게 쓸 경우도 있습니다.

일단 저렇게 해서 return 한 값은 stack에 있는 값이기 때문에
다른 function을 call하기 전까지만 쓸 수 있죠

하지만 foo에서 t를 선언하기 전에 buffer를 약간 두면,
다른 function을 call한 후에도 쓸 수 있습니다.

malloc을 해서 쓸 경우의 overhead가 큰 경우
(malloc,free 특히 malloc은 비싼 operation이죠...)
저렇게 잘~ 쓰면 실행속도 최적화에 도움이 될 수도 있습니다.

물론, 아주 잘, 잘 써야 겠죠.

익명 사용자의 이미지

저같으면 함수를 부르기 전에 정의한 다음 매개 변수로 보내겠습니다. :)

페이지