[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]
 

아래의 항목은 "죠엘"의 블로그에 소개되어이 있는 내요들이다. 뭐 100% 동의하는 것은 아니지만, 충분히 기억해 둘 만하다.

1. 소스코드 관리시스템을 사용하고 있는가?
2. 한 번에 빌드 결과물을 만들어낼 수 있는 script를 사용하고 있는가?
3. 일일 빌드를 하고 있는가?
4. 버그 추적시스템을 운영하고 있는가?
5. 코드를 새로 작성하기 전, 매 baseline마다 알려진 모든 버그를 수정하는가?
6. 일정을 업데이트 하는가?
7. 요구명세서, 설계명세서 등 명세서를 작성하는가?
8. 개발자에게 조용하고 개인적인 작업환경을 제공하는가?
9. 경제적인 범위 내에서 최고의 성능의 도구를 사용하는가?
10. 테스터를 별도로 두고 있는가?
11. 프로그래머 채용 인터뷰 때 코딩 테스트를 하는가?
12. 무작위 사용편의성 테스트를 수행하는가?

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

XOR and minus seems to be about the same in concept, because, as if "A-B" means "difference between A and B", A^B also means "bitwise difference between A and B".

Let's see following two SWAP algorithm. (Analysing following alogrithm will be helpful to understand difference of concept between 'A-B' and 'A^B').

add swap
*x = *x + *y;
*y = *x - *y;
*x = *x - *y;

xor swap
*x ^= *y;
*y ^= *x;
*x ^= *y;

We should focus on that both of '-' and XOR are operation to get "difference".
('-' is difference of number and XOR is bitwise difference. But, those are all "difference"!)
So, we can implement swap operation with both '-' and "XOR".

Here is conceptual description of XOR swap.
*x ^= *y; "Store difference of 'original x' and 'original y'"
*y ^= *x; "We know difference of 'original x' and 'original y'(x). So, we can get 'original x' with 'original y' and store it into 'y'."
*x ^= *y; "We know difference of 'original x' and 'original y'(x), and 'orignal x'(y). So, we can get 'original y' and store it into 'x'."

Understanding something is "Understanding it's basic concept". It's very important.!!

So, someone who understands XOR, should be able to infer "swap with XOR" at the moment of knowing about "swap with '+/-'"

'Study > Computer Science' 카테고리의 다른 글

[Study] Ascending Stack Vs. Descending Stack  (0) 2007.02.07
[Study] Full Stack Vs. Empty Stack  (0) 2007.01.08
[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

There is copy construction in C++. And default copy constructor does shallow copy. But, we should take care of shallow-copied-instance. Every programmer knows about this issue. But, sometimes, it is difficult to detect mistake from shallow copy. Here is an example.

// a.h
class A
{
public:
    A(void){
        _ptr = NULL; _len = 0;
    }

    A(int len){
        _len = len;
        _ptr = malloc(_len);
    }

    ~A(void){
        if(NULL != _ptr) { free(_ptr); }
    }

    void*  _ptr
    int _len
};

-------

// file1.c
static A gA(100); // --- (*a)
void getA (A& param)
{
    A = gA;
}

// file2.c
void function (void)
{
    A localA; // --- (*1)
    getA(localA);
    ...
    // do something with localA.
    ...
}

In this case, 'localA'(*1)'s destructor is called at the moment of returning from the function. And at that time, memory of 'gA._ptr' is freed, because 'localA' is shallow copy of 'gA'.
Stack variable is automatically(implicitly) destructed when function is returned. And this is not easy to realize without keeping it in mind.

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

Developers working in interpreted languages tend to be more productive than those working in compiled language (Jones 1986a).
[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

* Space vs. Speed
- Most famous one. (Memory space vs. execution speed) - details are omitted.

* Modularity vs. (Performance & Space)
- Well-designed-module should be stable. To be stable, there may be lots of 'parameter check', 'condition check', 'sanity check' and so on. Besides, for the safety reason, it may not trust passed memory space. So, it may allocates it's own memory space and try to copy data from passed memory to use it. Those make this trade-off. Well-designed-module hides it's information and stable as it is. But to be like this, it consumes speed and space.

* Abstraction vs. Debugging
- When software is stable, abstraction reduces maintenance costs very much. But, if not, debugging is more difficult than straightforward designed software, especially, to the engineer who are not accustomed to the software.

* low possibility to misuse vs. various and rich functionality
- It is clear...

* Backward-Compatibility vs. Innovation
- ...

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

Reference is enhanced concept from Pointer in C++.
So, many C++ books says "Reference is better than Pointer. So try to use Reference if possible."
Yes, reference has several advantages. It guarantees that there is enough memory space for the variable. So, we don't need to do so-called "NULL Check" in most cases (In some evil cases, address can be NULL even if it is reference).
But, in terms of readability, sometimes, using Pointer is better than Reference. Since C, programmers think that parameter is passed by value. Let's see below.

func(a); // --- (*1)
func(&a); // --- (*2)

At first look - before looking at declaration of "func()", programmer tends to think that "Value of 'a' is passed. So, even after (*1), value of 'a' will be preserved." But in case of (*2), they might think that "Address of 'a' is passed. So, after (*2), value of 'a' may be changed."
But, Reference stands against of this 'Popular' concept - reference parameter of 'func()' may change value of 'a'.
But, in case that 'func()' doesn't change 'a', using reference - should be declared as 'const' - as parameter can be good choice.
In other words, if value of parameter is changed, using pointer is better then using reference in terms of readability, in my point of view.

In summary, my recommendation is,

"If possible, use Reference as parameter only when value of parameter is not changed with 'const' keyword."


[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

In general, handling exception smoothly is very difficult. So, in many software, there are lots of prematured routine for exception handling. My recommend is, "just put "Assert" instead of premature exception handling(henceforth PEH)".

PEH give noisy information when exception is occurred, because software crashes at unexpected place due to PEH (Not the place where exception is actually raised).
Let's see following codes.

...
info = Get_personal_info(name);
if(!info){ return SYS_Fail;}
...
Personal_InfoT* Get_personal_info(char* name)
{
    ...
    int index = Search_with_name(name);
    if(index < 0) {return NULL;} // fail. (exception handling part)  ---- (*)
    ...
}

In this case, the place where exception is raised at first is (*). But, to handling exception, this function returns just 'NULL'. And at caller, it also returns 'SYS_Fail'. OK. It's reasonable. But, in practice, handling all these returned error smoothly requires lots of costs. So, in many cases, this is handled prematurely. And eventually, this error leads to software crash at unexpected place.

So, again, my point is just put 'Assert'. After all, we should try to debug all errors. 'Assert' is very helpful in debug. And then, we should decide whether remaining as 'Assert' or handling exception later - usually, exception from IO should be handled. This is more efficient in my opinion.

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

At first, see [
 virtual method table in wikipedia ].

'pointer fixups' is needed to support inheritance in C++.
Let's dig deeper.. See following example. (classes comes from above Wikipedia).

// <CASE 1>
#ifdef _CASE_1_
class B1
{
public:
    void f0() {}
    virtual void f1() {}
    int int_in_b1;
};

class B2
{
public:
    virtual void f2() {}
    int int_in_b2;
};

class D : public B1, public B2
{
public:
    void d() {}
    void f2() {}  // override B2::f2()
    int int_in_d;
};
#endif // _CASE_1_

// <CASE 2>
#ifdef _CASE_2_
class B1
{
public:
    virtual ~B1(){}
    void f0() {}
    virtual void f1() {}
    int int_in_b1;
};

class B2
{
public:
    virtual ~B2(){}
    virtual void f2() {}
    int int_in_b2;
};
class D : public B1, public B2
{
public:
    ~D(){}
    void d() {}
    void f2() {}  // override B2::f2()
    int int_in_d;
};

#endif // _CASE_2_

void funcA(B1* obj) {
    // -------- (*1)
}

void funcB(B2* obj) {
    // -------- (*2)
}

void
main()
{
    D* p = new D;
    funcA(dynamic_cast<B1*>(p)); // -------- (*a)
    funcB(dynamic_cast<B2*>(p)); // -------- (*b)
    // delete p; // ----- (*l)
    // delete (dynamic_cast<B1*>(p)); // ----- (*m)
    // delete (dynamic_cast<B2*>(p)); // ----- (*n)
}

We can see that pointer 'p' is passed as parameter in (*a) and (*b). And to do this type-casting and 'pointer fixup' is needed. That is, actual memory address passed as parameter of (*a) is different from the one of (*b). This is 'pointer fixup'. In case of GCC and MSVC7, there is 8-byte-gap as above wikipedia said.; { Address_of_obj_at_(*1) + 8 = Address_of_obj_at_(*2) }.
That's OK. It's very common.

Then, what happen at the moment of deleting memory?
We may guess that memory address passed is same in (*l) and (*m), but not in (*n) - may be 8-byte-gap in GCC and MSVC7 same as the case of (*a) and (*b). In <CASE_1>, program works just as we expects. (*l) and (*m) don't make an exception at runtime, but (*n) does. Then, as you know, why <CASE_2> works well? Newly assigned memory is deleted correctly in <CASE_2>. Than, is it really true that passed addresses are different among (*l), (*m) and (*n)? To investigate deeper, I tested it again with overloaded new/delete operator.

void * operator new(size_t size) {
    return malloc(size);  // ------ (*A)
}

void operator delete(void *p) {
    return free(p); // ----- (*B)
}

We can check passed address at (*B). In <CASE_1>, 'p' at (*B) is same with 'obj' at (*2), and error is raised - memory address trying to free is different with allocated one. Difference between <CASE_1> and <CASE_2> is destructor declared as 'virtual'. Interestingly, in <CASE_2>, 'p' at (*B) is the value of 'obj' at (*1) - original address -, and work successfully. In summary, if 'virtual destructor' is defined, than original address value is passed to 'delete' operator. It is interesting, isn't it?. :-)

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

We can use 'typename' in template class.

template
class C
{
    typename T::bT* a;
}

But, keyword 'typename' cannot be used outside template. So, following is incorrect syntax.

class A;
class B
{
    A*     a;
    typename A::bT* b; // error here!
};

So, in this case, class A's definition should be included before definition of class B.

It's just small information about using 'typename'.

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

Throwing exception in C++ is very expensive operation. But, "try... catch..." itself doesn't drain performance if exception is not thrown. So, "Throwing exception" should not be used for just convenience in coding!! This should be used only to handle unexpected and not-often-raised exception!

+ Recent posts