volatile

References

volatile 키워드에 대해서 찾아보게 된 계기는 ‘Head First Design Patterns’의 싱글톤 패턴에서 volatile을 사용하고 있었기 때문이었다.

C언어 문법 중 완전히 이해되지 않은 부분이 volatile이었는데, 오늘에야 그 의미를 이해하게 되었다. volatile의 의미는 컴파일 최적화를 하지 않는다는 것이다. 그것은 컴파일러의 최적화 옵션과 관련이 있다.

  • 코드는 순서대로 실행되지 않는다.
  • 코드대로 기계어로 변환되지 않는다.

아래는 위키에서 가져온 코드이다.

static volatile int foo;

void bar (void)
{
    foo = 0;

    while (foo != 255);
}

사실 위 코드는 foo값이 255가 될때까지 기다린다. 그러나 volatile 키워드가 없다면, 컴파일러는 아래와 같이 최적화한다.

void bar_optimized(void)
{
    foo = 0;

    while (true);
}

사실 컴파일러 입장에서는 명백하다. foo는 0이고 변경되지 않기 때문에 항상 while은 참이 되므로, foo를 비교할 필요없다고 여기게 된다. 그러나, 실제로는 하드웨어 인터럽트등을 통해 값이 변경될 수 있다는 것이다.
‘Head First Design Patterns’에서는 멀티스레딩에 대한 내용을 서술하면서, 위의 코드에서와 비슷한 상황을 설명하고 있다. 그러나 References의 마지막 링크에서는 조금 다른 이야기를 하고 있으므로 참조할 필요가 있다.

Unity를 이용한 TDD

Reference
  • Unity 의 examples/example_1

Unity는 C언어를 이용한 TDD framework이다. Embedded에 적용할 수 있도록 header file 2개와 c파일 하나로 구성되어 있다.
다만, Test Runner하는 부분이 성가신데, 이는 ruby script를 이용하여 해결할 수 있다.

다음과 같이 단순한 예제를 구성하였다.

/* add.h */
#include <stdint.h>
int32_t add(int32_t a, int32_t b);
/* add.c */
#include <stdint.h>

int32_t add(int32_t a, int32_t b)
{
    return (a+b);
}
/* test_add.c */
#include "add.h"
#include "unity.h"

void setUp(void)
{
    /* 생략시 에러 발생 */
}

void tearDown(void)
{
    /* 생략시 에러 발생 */
}

void test_add(void)
{
    TEST_ASSERT_EQUAL(7, add(3,4));
}

파일은 Unity중 필수적으로 필요한 것만 복사하여 구성하였다.

.
├── add.c
├── add.h
├── test_add.c
└── unity
    ├── generate_test_runner.rb
    ├── unity.c
    ├── unity.h
    └── unity_internals.h

1 directory, 7 files

컴파일 및 실행하는 방법은 다음과 같다.

ruby unity/generate_test_runner.rb test_add.c
gcc -Iunity unity/unity.c add.c test_add_Runner.c
./a.out

test_add.c:11:test_add:PASS
-----------------------
1 Tests 0 Failures 0 Ignored
OK

이러한 방법은 실제로 사용하기에는 다소 불편하다.

  • Test Runner를 만드는 방법이 복잡하다. ruby script를 이용하면 되지만, 번거롭고 ruby가 설치되어 있어야 한다.
  • Test Case가 변경되면, Test Runner도 같이 변경해주어야 한다.
  • shell command에서 명령을 내리기에는 손이 많이 간다.

Unity example에 포함된 makefile을 다음과 같이 수정하였다.

/* makefile */
# ==========================================
#   Unity Project - A Test Framework for C
#   Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
#   [Released under MIT License. Please refer to license.txt for details]
# ==========================================

UNITY_ROOT=./unity
C_COMPILER=gcc
TARGET=testAdd
SRC_FILES=$(UNITY_ROOT)/unity.c add.c test_add.c test_add_Runner.c
INC_DIRS=-I$(UNITY_ROOT)

CLEANUP = rm -f *.o ; rm -f $(TARGET)

all: clean default

default:
        ruby $(UNITY_ROOT)/generate_test_runner.rb test_add.c
        $(C_COMPILER) $(INC_DIRS) $(SRC_FILES) -o $(TARGET)
        ./$(TARGET)

clean:
        $(CLEANUP)

디렉토리 구조나 makefile을 좀 더 적절하게 변경하는 것은 추후에 추가할 예정이다.

그런데 example_1을 따라해보니, 실제로 매우 불편했다.

  • 나 스스로는 Test Runner를 만들지 못하겠다. script를 써야만 했다.
  • Test Group하나만 가능하다. 즉 Test Case 모음은 하나의 파일에만 존재해야 했다. Test Runner를 만들기 위해서 ruby scriptt로 2개의 Test Case 파일을 시도하면 에러가 발생했다. ( main( ) 함수가 2개 생기기 때문이다. )

이러한 문제는 example_2에서 해결되었다.

tmux 사용법

reference

tmux는 좀 더 개선된 screen이라 생각하면 된다.

용어
  • session : tmux 실행시 생성
  • window : 하나의 session에 여러개의 window 생성 가능
  • pane : 하나의 window안에 여러개의 pane으로 분할 가능
사용법

다음은 실행과 종료 방법이다.

$ tmux
$ exit  # 종료  또는 ctrl + d

session 명령

$ tumx new -s 세션이름 # 세션이름으로 생성 

ctrl + b + d  # 셔션을 유지한채로 나오기

$ tmus ls  # 세션 목록 보이기
$ tmux attach -t 세션명 # 세션으로 다시 들어가기

window 명령

ctrl + b + c # 새 window 생성
ctrl + b + & # window 삭제
ctrl + b + , # window 이름 변경
ctrl + b + window 번호 # window 이동
ctrl + b + n # window 이동, next
ctrl + b + p # window 이동, previous
ctrl + b + l # window 이동, last

pane 명령

ctrl + b + % # 가로 나누기
ctrl + b + " # 세로 나누기
ctrl + b + q # 특정 pane 이동

ctrl + b + o # pane 이동
ctrl + b + 방향기 # pane 이동

$ exit # 삭제 또는 ctrl + d
ctrl + b + x # pane 삭제

ctrl + b + [Alt] + 방향키 # 크기 조절

copy mode
처음 실행시에 copy mode의 키가 제대로 동작하지 않았다. 기본으로 emacs style로 되어 있어서 그런것이었다. vi style로 변경하면 잘 된다.
vi style을 적용하면, copy mode에서 이동하는 방향키도 i,j,k,l, ctrl-f, ctrl-b 를 사용할 수 있다.

# ~/.tmux.conf
# set-window-option -g mode-keys vi

ctrl + b + [ # copy mode 진입
space # copy 시작
Enter # copy 완료
ctrl + b + ] # paste

'Linux' 카테고리의 다른 글

tmux 3.2 설치 및 설정파일  (0) 2022.01.06
makefile의 phony  (0) 2013.05.07

bootstrap 사용법

bootstrap을 구동하기 위한 뼈대 코드는 다음과 같다.

<!DOCTYPE html>
<html>

  <head>
      <meta charset="utf-8">    <!-- 한글을 위해 필요 -->
    <title>Bootstrap 101 Template</title>
    <link href="bootstrap.min.css" rel="stylesheet" media="screen">
  </head>

  <body>
    <h1>안녕, HTML!</h1>
  </body>
</html>
그리드 시스템
<div class="row">
    <div class="col-md-4">
        위키백과는 위키를 이용하여 전 세계 사람들이 함께 만들어가는 웹 기반의 다언어 백과사전입니다.
    </div>
    <div class="col-md-4">
        위키백과는 다섯 가지 기본 원칙에 따라 운영됩니다.
    </div>
</div>
컬럼 오프셋하기

왼쪽 컬럼에 오프셋을 적용하면, 우측 컬럼은 오프셋만큼 같이 이동된다.

<div class="row">
    <div class="col-md-4 col-md-offset-">
        위키백과는 위키를 이용하여 전 세계 사람들이 함께 만들어가는 웹 기반의 다언어 백과사전입니다.
    </div>
    <div class="col-md-4">
        위키백과는 다섯 가지 기본 원칙에 따라 운영됩니다.
    </div>
</div>

YAML 사용하기

Reference

YAML은 XML에 비해 눈으로 데이터 확인이 용이하며, 해당 데이터를 쉽게 직렬화할 수 있다.

다음은 YAML의 hello program이다.

-
  name: Tom
  age: 32
-
  name: Dick
  age: 19  
EOS

config = YAML.load(yaml_string)    # from yaml_string

puts config[0]['age']  # 32
puts config[1]['name'] # Dick

YAML을 구성하기 위해서는 Array 로 할것인가, Hash로 할것인가로 결정해주면 된다. 물론 중첩도 가능하다.

arry = <<EOS
- [Tom, 32] # Array
- [Dick, 19] # Array
EOS

hash = <<EOS
{ name: Tom, age:32 } # Hash
EOS

다음은 기본적인 사용법을 정리한 hello_yaml.rb 이다.

# 필요한 파일
require 'yaml'

# 변환
str = "Hello, Yaml!"
puts str.to_yaml # y(str)


arr = ["one", "two", "three"]
puts arr.to_yaml # y(arr)

# 파일 저장
fileName = File.open('counting.yml', 'w')
    YAML.dump(arr, fileName)
fileName.close

# 파일 읽기
fileName = File.open('counting.yml', "r")
    newArr = YAML.load(fileName) # to array
fileName.close

puts newArr.to_yaml # y(newArr)

'Ruby' 카테고리의 다른 글

Koan - Methods  (0) 2014.03.01
Koan - Regular Expressions  (0) 2014.02.28
Koans - Symbols  (0) 2014.02.27
Ruby에서의 TDD  (0) 2014.02.22
Windows 환경에서 Shotgun이 실행되지 않을때  (0) 2014.02.21

2.2 헤더 파일 만들기

헤더 파일에 포함되는 내용은 다음과 같다

  • 외부에 공개할 매크로의 정의(함수형 매크로)
  • 외부에 공개할 상수의 정의(#define, enum 등의 정의)
  • 외부에 공개할 구조체, 공용체의 정의
  • 외부에 공개할 자료형의 정의 (typedef를 이용한 자료형의 정의)
  • 외부에 공개할 함수의 원형 선언
  • 외부에 공개할 전역 변수의 extern 선언
  • 이 라이브러리를 이용할 때 필요한 다른 헤더 파일의 인클루드문(예를 들어 함수의 인자나 반환값으로 FILE *형을 이용한다면 stdio.h를 인클루드해야 한다.)
2.2.1 매크로 정의

보통 매크로 함수는 일반적인 함수와 구분해주기 위해 이름을 모두 대문자로 기술하는 것이 관례이다. 꼭 외부에 공개할 필요가 있는 매크로만을 헤더 파일에 기술한다.

매크로가 중복 정의된 경우 gcc는 다음과 같이 처리한다.

  • 두 정의가 같은 내용인 경우, 에러를 발생하지 않고 통과한다.
  • 두 정의가 서로 다른 경우, 컴파일 시에 “redefined”를 알리는 경고가 발생하고, 나중에 정의된 값을 취한다.

매크로 이름의 충돌을 확실히 방지하고 싶다면 아래와 같이 경고를 발생시키는 편이 낫다.

#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#else
#warning Duplicated definition of MAX
#endif
2.2.2 상수 정의
2.2.3 구조체, 공용체 정의

헤더 파일에서 구조체를 정의하는 경우, “이 구조체는 라이브러리의 사용자가 멤버 변수를 자유롭게 조작해도 무방하다.”는 의사를 암시적으로 포함하는 셈이 되므로 이에 유의해야 한다.

2.2.4 자료형 정의
2.2.5 함수의 원형 선언

함수 원형은 반드시 명시적으로 선언하고, 컴파일시에 -Wall 옵션을 주어 관련 경고를 모두 출력하도록 하자.

2.2.6 전역 변수의 extern 선언
2.2.7 다른 라이브러리 헤더 파일의 인클루드

원칙적으로는, 라이브러리를 컴파일할 때에만 필요한 경우라면, .c에, 라이브러리를 이용할 때 필요한 경우라면 .h에 인클루드하는 것으로 정해두면 된다.

2.2.8 다중 인클루드 문제

인클루드의 중첩은 다중 인클루드를 유발한다. 다중 인클루드가 발생하면 같은 단어에 대해 #define의 정의나 typedef의 정의가 여러 번 등장하게 된다.

#define의 재정의
  • 같은 단어에 대해 같은 정의를 내리는 경우, 오류나 경고로 처리하지 않는다.
  • 같은 단어에 대해 다른 정의를 내리는 경우, 경고를 출력하고, 나중에 정의된 뜻을 따른다.
typedef의 재정의
  • 같은 자료형에 대해 중복된 정의를 내리면 무조건 에러로 처리한다.

이러한 문제들을 회피하기 위해, #ifndef#endif로 묶어서 같은 헤더 파일 본문이 여러 번 반복되지 않도록 해둔다.

#ifndef    _STRING_H_INCLUDED_
#define    _STRING_H_INCLUDED_
/* header file 본문 */
#endif

'C' 카테고리의 다른 글

Unity를 이용한 TDD - 2  (0) 2014.04.13
volatile  (0) 2014.04.09
Unity를 이용한 TDD  (0) 2014.04.02
[도서] 쉽게 배우는 C프로그래밍 테크닉 - 1.4 헤더 파일 만들기  (0) 2014.03.04
gdb 사용법  (0) 2014.03.03
1.4.7 const 활용

포인터를 const로 정의하면, 그 포인터가 가리키는 값을 변경할 수 없다.

int test(void)
{
    char s[] = "test";
    const char * p = s;
    p[0] = 'T';    /* const 포인터가 가리키는 주소의 값을 변경하려고 시도    */

    return 0;
}
1.4.8 const를 인자로 활용
void strcopy(char * dst, const char * src)
{
    while (*src != '\0')
        *(dst++) = *(src++);
    *dst = '\0';
}
  • const char *로 정의한 포인터가 가리키는 값은 변경할 수 없다. (경고)
  • const char * 포인터를 char *로 형변환 하는 것은 허용되지 않는다. (경고)

const를 반환하는 함수의 활용 예

typedef enum { Black, White, Blue, Red, Green } Color;

const char * ColorName(Color c)
{
    const char * s;
    switch(c)
    {
        case Black: s = "Black" break;
        case White: s = "White" break;
        case Blue : s = "Blue"  break;
        case Red  : s = "Red"   break;
        case Green: s = "Green" break;
        default   : s = "Unknown";
    }

    return s;
}
1.4.9 const를 사용할 때 주의사항

const로 선언할 수 있는 것은 모두 const로 선언하는 것이 좋을 것처럼 여겨진다. 하지만 실제로는 그렇지 않다. 특히 함수가 깊게 중첩되는 경우에는 const를 사용하지 않는 것이 좋다.

'C' 카테고리의 다른 글

Unity를 이용한 TDD - 2  (0) 2014.04.13
volatile  (0) 2014.04.09
Unity를 이용한 TDD  (0) 2014.04.02
[도서] 쉽게 배우는 C프로그래밍 테크닉 - 2.2 헤더 파일 만들기  (0) 2014.03.04
gdb 사용법  (0) 2014.03.03

GDB 사용법

다음 예제는 introduction to GDB a tutorial - Harvard CS50 (http://www.youtube.com/watch?v=sCtY--xRUyI)에서 가져왔다. 프로그램은 factorial을 구하는 것으로 버그를 가지고 있어서 제대로 동작하지 않는다.

/* filename : factorial.c */
#include <stdio.h>

int main(void)
{
        int num;
        int factorial;
        int i;

        do
        {
                printf("Enter a positive integer: ");
                scanf("%d",&num);
        }
        while(num<0);

        for(i=0; i<=num;i++)
                factorial = factorial * i;

        printf("%d! = %d\n", num, factorial);

        return 0;
}
Compile하는 법

Compile option으로 -g을 넣는다.

gcc -g factorial.c`)
gdb 실행법
gdb ./factorial
gdb 종료법
quit
break point 걸기
break main
line별로 실행하기
next # go to next line
run
기타
list

Koan - Methods

Case 115

첫번째 테스트는 메소드의 리턴값의 class이다.

  def test_calling_with_variable_arguments
    assert_equal __, method_with_var_args.class
    assert_equal __, method_with_var_args
    assert_equal __, method_with_var_args(:one)
    assert_equal __, method_with_var_args(:one, :two)
  end

'Ruby' 카테고리의 다른 글

YAML 사용하기  (0) 2014.03.07
Koan - Regular Expressions  (0) 2014.02.28
Koans - Symbols  (0) 2014.02.27
Ruby에서의 TDD  (0) 2014.02.22
Windows 환경에서 Shotgun이 실행되지 않을때  (0) 2014.02.21

Koan - Regular Expressions

Case 83

정규표현식의 클래스는 Regexp이다.

def test_a_pattern_is_a_regular_expression
    assert_equal __, /pattern/.class
end
Case 84

/x?/는 존재하거나 존재하지 않는다는 의미이다. 테스트케이스의 의미처럼 option이라고 생각하자. 그리고 정규표현식의 기호는 다음을 참고하자. (http://www.nextree.co.kr/wp-content/uploads/2014/01/jhkim-140117-RegularExpression-06.png)

def test_question_mark_means_optional
    assert_equal __, "abbcccddddeeeee"[/ab?/]
    assert_equal __, "abbcccddddeeeee"[/az?/]
end
Case 88

/x*/는 x가 0번이상 반복하는 것을 의미한다. 3번째 테스트케이스는 nil이 아니다. (0번이상에 주의하자)

def test_asterisk_means_zero_or_more
    assert_equal __, "abbcccddddeeeee"[/ab*/]
    assert_equal __, "abbcccddddeeeee"[/az*/]
    assert_equal __, "abbcccddddeeeee"[/z*/]

    # THINK ABOUT IT:
    #
    # When would * fail to match?
end

# THINK ABOUT IT:
#
# We say that the repetition operators above are "greedy."
#
# Why?
Case 90

ruby에서 문자열[ ]는 문자열에서 정규표현식을 조사하는 구문이고, select는 조건을 만족하는 원소를 추출하는 메소드이다.

  def test_character_classes_give_options_for_a_character
    animals = ["cat", "bat", "rat", "zat"]
    assert_equal __, animals.select { |a| a[/[cbr]at/] }
  end
Case 93

공백문자 처리시에 \t\n도 포함된다.

  def test_slash_s_is_a_shortcut_for_a_whitespace_character_class
    assert_equal __, "space: \t\n"[/\s+/]
  end
Case 96

[/[^0-9]+/]는 not 으로 사용되었다.

  def test_a_character_class_can_be_negated
    assert_equal "the number is ", "the number is 42"[/[^0-9]+/]
  end
Case 97
  def test_shortcut_character_classes_are_negated_with_capitals
    assert_equal __, "the number is 42"[/\D+/]
    assert_equal __, "space: \t\n"[/\S+/]
    # ... a programmer would most likely do
    assert_equal __, "variable_1 = 42"[/[^a-zA-Z0-9_]+/]
    assert_equal __, "variable_1 = 42"[/\W+/]
  end
Case 98
  def test_slash_a_anchors_to_the_start_of_the_string
    assert_equal __, "start end"[/\Astart/]
    assert_equal __, "start end"[/\Aend/]
  end

'Ruby' 카테고리의 다른 글

YAML 사용하기  (0) 2014.03.07
Koan - Methods  (0) 2014.03.01
Koans - Symbols  (0) 2014.02.27
Ruby에서의 TDD  (0) 2014.02.22
Windows 환경에서 Shotgun이 실행되지 않을때  (0) 2014.02.21

+ Recent posts