'생활의 발견' 카테고리의 다른 글
노동시장 신조어 : 나이트쿠스,노무,더피,런치노마드,리본,치코노믹,신NG,코피스,포미 (0) | 2010.04.10 |
---|---|
윈도우 모바일 프리 게임 탑 40 (0) | 2010.03.27 |
2009.10.25 카라( Kara ) 용산 CGV 공연 실황 (0) | 2009.10.25 |
젊은이들이 취업못하게 되는 뻔한 이유.... (0) | 2009.09.04 |
게임 그래픽 전문가 강좌 (0) | 2009.08.06 |
노동시장 신조어 : 나이트쿠스,노무,더피,런치노마드,리본,치코노믹,신NG,코피스,포미 (0) | 2010.04.10 |
---|---|
윈도우 모바일 프리 게임 탑 40 (0) | 2010.03.27 |
2009.10.25 카라( Kara ) 용산 CGV 공연 실황 (0) | 2009.10.25 |
젊은이들이 취업못하게 되는 뻔한 이유.... (0) | 2009.09.04 |
게임 그래픽 전문가 강좌 (0) | 2009.08.06 |
소프트웨어기술경력증 수신 (0) | 2009.11.07 |
---|---|
데이터베이스 정규화 (0) | 2009.11.07 |
GTK+ 프로그래밍 기초 자료 (0) | 2009.11.01 |
GSM : Overview of the Global System for Mobile Communications (0) | 2009.10.04 |
휴대폰 개발 프로세스 (1) | 2009.09.27 |
데이터베이스 정규화 (0) | 2009.11.07 |
---|---|
GTK+ 프로그래밍 기초 자료2 (0) | 2009.11.01 |
GSM : Overview of the Global System for Mobile Communications (0) | 2009.10.04 |
휴대폰 개발 프로세스 (1) | 2009.09.27 |
프로그래밍 언어 포럼 순위 (랭킹) (1) | 2009.09.27 |
2009.10.25 카라( Kara ) 용산 CGV 공연 실황
윈도우 모바일 프리 게임 탑 40 (0) | 2010.03.27 |
---|---|
영어고전을 pdf 파일로 받아볼 수 있는 곳 : http://www.planetebook.com/ (0) | 2009.11.01 |
젊은이들이 취업못하게 되는 뻔한 이유.... (0) | 2009.09.04 |
게임 그래픽 전문가 강좌 (0) | 2009.08.06 |
상대방의 거짓말을 알아채는 10가지 방법 [조인스] (0) | 2009.05.19 |
Chapter | Module | Files | Lines | Description |
---|---|---|---|---|
1-2 | agents | .py | 532 | Implement Agents and Environments (Chapters 1-2). |
3-4 | search | .py .txt | 735 | Search (Chapters 3-4) |
5 | csp | .py .txt | 449 | CSP (Constraint Satisfaction Problems) problems and solvers. (Chapter 5). |
6 | games | .py | 285 | Games, or Adversarial Search. (Chapters 6) |
7-10 | logic | .py .txt | 887 | Representations and Inference for Logic (Chapters 7-10) |
11-12 | planning | .py | 6 | Planning (Chapters 11-12) |
13-15 | probability | .py .txt | 170 | Probability models. (Chapter 13-15) |
17 | mdp | .py .txt | 141 | Markov Decision Processes (Chapter 17) |
18-20 | learning | .py | 585 | Learn to estimate functions from examples. (Chapters 18-20) |
21 | rl | .py | 14 | Reinforcement Learning (Chapter 21) |
22 | nlp | .py .txt | 169 | A chart parser and some grammars. (Chapter 22) |
23 | text | .py .txt | 364 | Statistical Language Processing tools. (Chapter 23) |
doctests | .py .txt | 42 | Run all doctests from modules on the command line. For each | |
py2html | .py | 109 | Pretty-print Python code to colorized, hyperlinked html. | |
utils | .py .txt | 713 | Provide some widely useful utilities. Safe for "from utils import *". | |
5201 |
넷북 올 하반기 `이렇게 바뀐다` (0) | 2009.11.07 |
---|---|
` 名 言 錄`(리더스다이제스트 1983.3--1988.1) (0) | 2009.11.01 |
Standards and Style for Coding in ANSI C (0) | 2009.10.11 |
한글필기체 폰트 (0) | 2009.10.04 |
추억의 XT 컴퓨터 : GW-BASIC (0) | 2009.10.04 |
[출처] http://www.jetcafe.org/jim/c-style.html
$Id: c-style.html,v 1.3 1996/12/31 18:24:36 larson Exp $Back to the home page.
There are many rules and suggestions here. Too many, it seems, to satisfy while still getting your code to actually work. This is intentional - it is better to work under too many constraints than too few. Your code will look better if you have spent your time deciding on which stylistic rule to relax than if you have been making choices in a vacuum.
The coding style espoused here is strict and traditional. Some of the requirements (especially 8-space tabs and non-nested include files) will cause grief for many programs, but they will not be a problem for most well-written programs. This style makes poorly-written programs blatantly ugly. It will occasionally inconvenience well-written code, but that is a tradeoff we accept in order to encourage good style in general.
Lastly, like rules of etiquette, these guidelines are not an end in themselves, but rather they attempt to move you to be mindful of your actions, for they are not private - they affect others (future readers and maintainers), and ultimately you are responsible to a higher authority (ISO WG14).
This d0cument is stolen from the work of Mike Haertel, Richard O'Keefe, and Rob Pike.
The major improvements to the C language made in this standard are:
void
's and enum
's have been added. The following are mistakes of C which we advocate fighting.
int
The syntax for type declarations can get kind of beastly, but it's okay if you just remember that declaration follows use, as in:
int (*apfi[])();
C is a language with pointers; don't go into denial over this. The fact that a[i]
is equivalent to *(a + i)
is one of the defining characteristics of C, and should be embraced.
Some people recommend creating abstract data types of the form:
typedef struct T *T;Then values of the abstract type can be declared as:
T t;making
t
look like an object in its own right. However this obscures the fact that t
is a reference to an object, rather than an object itself. This also prevents passing t
by value rather than by reference. Lastly, C is an imperative language. Although some compilers implement tail recursion, loops should be written as iterative loops. Functional language programming techniques which madly allocate memory will not work will with current C technology.
That being said, good places to put comments are:
/* single line comments look like this *//* * Important single line comments look like multi-line comments. *//* * Multiline comments look like this. Put the opening and closing * comment sequences on lines by themselves. Use complete sentences * with proper English grammar, capitalization, and punctuation. *//* but you don't need to punctuate or capitalize one-liners */The opening
/
of all comments should be indented to the same level as the code to which it applies, for example: if (fubar()) { /* * Fouled up beyond all recognition. Print a nastygram * and attempt to clean up. If that doesn't work, * die horribly, and try to crash the system while * we're at it. */ ...}If you put a comment on the same line as code, set it off from the code with a few tabs. Don't continue such a comment across multiple lines. For example:
printf("hi\n"); /* hello revisited */In fact, try to avoid such comments altogether - if it`s not important enough to warrant a complete sentence, does it really need to be said?
The size of the comment should be proportional to the size of the code that it refers to. Consequently, properties of code that can fit within a single 24-line screen should not be commented unless they are not obvious. By contrast, even obvious global properties and invariants may need to be made explicit. This doesn't have to be through comments, though. The assert()
macro is an excellent ``executable comment''.
includes of system headersA reasonable variation might be to have several repetitions of the last three sections.
includes of local headers
type and constant definitions
global variables
functions
Within each section, order your functions in a ``bottom up'' manner - defining functions before their use. The benefit of avoiding redundant (hence error-prone) forward declarations outweighs the minor irritation of having to jump to the bottom of the file to find the main functions.
In header files, use the following organization:
type and constant definitionsAgain, several repetitions of the above sequence might be reasonable. Every object and function declaration must be preceded by the keyword
external object declarations
external function declarations
extern
. (See below for why.) Now, for a very important rule: Avoid having nested includes, ever. I mean it. If you've ever tried to track a bug through the SunOS /usr/include
maze, you'll understand why. Consider using the makedepend
tools to help maintain your source file dependencies in your Makefile
.
static
every function and global variable that you possibly can. (I consider it a design flaw that C doesn't do this by default.) When declaring a global function or variable in a header file, use an explicit extern
. For functions, provide a full ANSI C prototype. For example:
extern int errno;extern void free(void *);Do not use parameter names in function prototypes - you are increasing the risk of a name collision with a previously-defined macro, e.g.:
#define fileptr stdin...extern int foo(FILE *fileptr);Instead, d0cument parameter names only as necessary using comments:
extern void veccopy(double * /*dst*/, double * /*src*/, size_t);
Why the extern
? It is OK to declare an object any number of times, but in all the source files there can be only one definition. The extern
says ``This is only a declaration.'' (A definition is something that actually allocates and initializes storage for the object.) Historically,
int foo;was ambiguously treated as either a declaration or both declaration and definition depending on linker magic. However, ANSI C allows it to be an error for this to appear at file scope in more than one place in a program. Header files should never contain object definitions, only type definitions and object declarations. This is why we require
extern
to appear everywhere except on the real definition. In function prototypes, try not to use const
. Although the ANSI standard makes some unavoidable requirements in the standard library, we don't need to widen the problem any further. What we are trying to avoid here is a phenomenon known as ``const
poisoning'', where the appearance of const
in some prototype forces you to go through your code and add const
all over the place.
Don't rely on C's implicit int
typing; i.e., don't say:
extern foo;say:
extern int foo;This is poor style and should be completely avoided (and might go away in C9X!). Similarly, don't declare a function with implicit return type. If it returns a meaningful integer value, declare it
int
. If it returns no meaningful value, declare it void
. (By the way, the C standard requires you to declare main()
as returning int
.) Provide typedefs for all struct
and union
types, and put them before the type declarations. Creating the typedef eliminates the clutter of extra struct
and union
keywords, and makes your structures look like first-class types in the language. Putting the typedefs before the type declarations allows them to be used when declaring circular types. It is also nice to have a list of all new reserved words up front.
typedef struct Foo Foo;typedef struct Bar Bar;struct Foo { Bar *bar;};struct Bar { Foo *foo;};
This give a particularly nice scheme of exporting opaque objects in header files.
In header.h
:
typedef struct Foo Foo;In
source.c
: #include "header.h"struct Foo { .. };Then a client of header.h can declare a
Foo *x;but cannot get at the contents of a
Foo
. In addition, the user cannot declare a plain (non pointer) Foo
, and so is forced to go through whatever allocation routines you provide. We strongly encourage this modularity technique. If an enum
is intended to be declared by the user (as opposed to just being used as names for integer values), give it a typedef too. Note that the typedef has to come after the enum
declaration.
Don't mix any declarations in with type definitions; i.e., don't say:
struct foo { int x;} object;Also don't say:
typedef struct { int x;} type;(It's important for all typedefs to stand out by themselves.)
Declare each field of a structure on a line by itself. Think about the order of the fields. Try to keep related fields grouped. Within groups of related fields, pick some uniform scheme for organizing them, for example alphabetically or by frequency of use. When all other considerations are equal, place larger fields first, as C's alignment rules may then permit the compiler to save space by not introducing "holes" in the structure layout.
enum { Red = 0xF00, Blue = 0x0F0, Green = 0x00F };static const float pi = 3.14159265358;instead of
#defines
, which are rarely visible in debuggers. Macros should avoid side effects. If possible, mention each argument exactly once. Fully parenthesize all arguments. When the macro is an expression, parenthesize the whole macro body. If the macro is the inline expansion of some function, the name of the macro should be the same as that of the function, except fully capitalized. When continuing a macro across multiple lines with backslashes, line up the backslashes way over on the right edge of the screen to keep them from cluttering up the code.
#define OBNOXIOUS(X) \ (save = (X), \ dosomethingwith(X), \ (X) = save)Try to write macros so that they are syntactically expressions. C's comma and conditional operators are particularly valuable for this. If you absolutely cannot write the macro as an expression, enclose the macro body in
do { ... } while (0)
. This way the expanded macro plus a trailing semicolon becomes a syntactic statement. If you think you need to use #ifdef
, consider restricting the dependent code to a single module. For instance, if you need to have different code for Unix and MS_DOS, instead of having #ifdef unix
and #ifdef dos
everywhere, try to have files unix.c
and dos.c
with identical interfaces. If you can't avoid them, make sure to d0cument the end of the conditional code:
#ifdef FUBAR some code#else other code#endif /* FUBAR */Some sanctioned uses of the preprocessor are:
#if 0
. #ifdef __GNUC__
. <float.h>
and <limits.h>
. #if
to test whether some condition holds that you know how to handle, but are too lazy to provide code for the alternative, protect it with #error
, like this: #include <limits.h>#if INT_MAX > UCHAR_MAXenum { Foo = UCHAR_MAX + 1, Bar, Baz, Barf };#else#error "need int wider than char"#endif(This example also illustrates a reasonable use of <limits.h>.)
Names should be meaningful in the application domain, not the implementation domain. This makes your code clearer to a reader who is familiar with the problem you're trying to solve, but is not familiar with your particular way of solving it. Also, you may want the implementation to change some day. Note that well-structured code is layered internally, so your implementation domain is also the application domain for lower levels.
Names should be chosen to make sense when your program is read. Thus, all names should be parts of speech which will make sense when used with the language's syntactic keywords. Variables should be noun clauses. Boolean variables should be named for the meaning of their "true" value. Procedures (functions called for their side-effects) should be named for what they do, not how they do it. Function names should reflect what they return, and boolean-valued functions of an object should be named for the property their true value implies about the object. Functions are used in expressions, often in things like if's, so they need to read appropriately. For instance,
if (checksize(s))is unhelpful because we can't deduce whether
checksize
returns true on error or non-error; instead if (validsize(s))makes the point clear and makes a future mistake in using the routine less likely.
Longer names contain more information than short names, but extract a price in readability. Compare the following examples:
for (elementindex = 0; elementindex < DIMENSION; ++elementindex) printf("%d\n", element[elementindex]);for (i = 0; i < DIMENSION; ++i) printf("%d\n", element[i]);In the first example, you have to read more text before you can recognize the for-loop idiom, and then you have to do still more hard work to parse the loop body. Since clarity is our goal, a name should contain only the information that it has to.
Carrying information in a name is unnecessary if the declaration and use of that name is constrained within a small scope. Local variables are usually being used to hold intermediate values or control information for some computation, and as such have little importance in themselves. For example, for array indices names like i
, j
, and k
are not just acceptable, they are desirable.
Similarly, a global variable named x
would be just as inappropriate as a local variable named elementindex
. By definition, a global variable is used in more than one function or module (otherwise it would be static or local), so all of it's uses will not be visible at once. The name has to explain the use of the variable on its own. Nevertheless there is still a readability penalty for long names: casefold
is better than case_fold_flag_set_by_main
.
In short, follow "Mike's Rule" to make variable name size proportional to scope:
length(name(variable)) ~ log(countlines(scope(variable)))
Use some consistent scheme for naming related variables. If the top of memory is called physlim
, should the bottom be membase
? Consider the suffix -max
to denote an inclusive limit, and -lim
to denote an exclusive limit.
Don't take this too far, though. Avoid ``Hungarian''-style naming conventions which encode type information in variable names. They may be systematic, but they'll screw you if you ever need to change the type of a variable. If the variable has a small scope, the type will be visible in the declaration, so the annotation is useless clutter. If the variable has a large scope, the code should modular against a change in the variable's type. In general, I think any deterministic algorithm for producing variable names will have the same effect.
Nevertheless, if the type name is a good application-domain description of the variable, then use it, or a suitable abbreviation. For instance, when implementing an ADT I would write:
/* * Execute registered callback and close socket. */voidchan_close(Chan *chan) /* No better name for parameter than "chan" */{ (*chan->deactivate)(chan->arg); (void) close(chan->fd);}but when using the ADT I would write:
/* * Log a message when the watched-for event happens. */struct Monitor { int (*trigger)(void *region); void *region; char *message; Chan *log; /* describes how Chan is used */};
There are weaknesses in C for large-scale programming - there is only a single, flat name scope level greater than the module level. Therefore, libraries whose implementations have more than one module can't guard their inter-module linkage from conflicting with any other global identifiers. The best solution to this problem is to give each library a short prefix that it prepends to all global identifiers.
Abbreviations or acronyms can shorten things up, but may not offer compelling savings over short full words. When a name has to consist of several words (and it often doesn't), separate words by underscores, not by BiCapitalization. It will look better to English-readers (the underscore is the space-which-is-not-a-space). Capitalization is reserved for distinguishing syntactic namespaces.
C has a variety of separately maintained namespaces, and distinguishing the names by capitalization improves the odds of C's namespaces and scoping protecting you from collisions while allowing you to use the same word across different spaces. C provides separate namespaces for:
#define NUSERTASKS 8 #define ISNORMAL(S) ((S)->state == Normal)Any fully capitalized names can be regarded as fair game for
#ifdef
, although perhaps not for #if
. goto
statement can be read aloud, and name it for why you go there, not what you do when you get there. For instance, goto bounds_error;is more helpful than
goto restore_pointer;
struct Foo { long bar; };with
typedef long Foo;since you still have the "struct" keyword everywhere, even when the contents are not being examined. The useless "struct" keywords also clutter up the code. Therefore we advocate creating a typedef mirror of all struct tags:
typedef struct Foo Foo;Capitalize the tag name to match the typedef name.
struct timeval { unsigned long tv_sec; long tv_usec; };for they are already in a unique namespace.
_t
suffix or other cutesy thing to say ``I'm a type'' - we can see that from it's position in the declaration! (Besides, all names ending with _t
are reserved by Posix.) The capitalization is needed to distinguish type names from variable names - often both want to use the same application-level word. enum Fruit { Apples, Oranges, Kumquats };
static
(and most should be), make the name short and sweet. If they are externally-visibly, try to give then a prefix unique to the module or library. Lastly, develop some standard idioms to make names automatic. For instance:
int i, j, k; /* generic indices */char *s, *t; /* string pointers */char *buf; /* character array */double x, y, z; /* generic floating-point */size_t n, m, size; /* results of sizeof or arguments to malloc */Foo foo, *pfoo, **ppfoo; /* sometimes a little hint helps */
Use real tab characters for indenting. Tabs are always 8 spaces. This policy has the following advantages:
Use the One True Brace Style (1TBS) as seen in K&R. The following quotation from Henry Spencer's Ten Commandments for C Programmers says it better than I can:
Thou shalt make thy program's purpose and structure clear to thy fellow man by using the One True Brace Style, even if thou likest it not, for thy creativity is better used in solving problems than in creating beautiful new impediments to understanding.The rationale behind this brace style, straight from the horse's (Dennis') mouth, is that the braces are just line noise to make the compiler happy, and so don't deserve to be specially set apart. (The GNU style is a particularly bad offender in this regard!) Also, the 1TBS conserves vertical space, which is important for those of us working on 24 line displays. (It also helps avoid excessive eye movement on big displays.)- The Eighth Commandment
Purists point out that 1TBS is inconsistent since it has one style for statements and another for functions. That's okay since functions are special anyway (you can't nest them). It's also good to know that with most editors you can get to the top of the current function by searching backward for the regexp ^{
.
Avoid unnecessary curly braces, but if one branch of an if
is braced, then the other should be too, even if it is only a single line. If an inner nested block is braced, then the outer blocks should be too.
Some examples:
if (foo == 7) { bar();} else if (foo == 9) { barf(); bletch();} else { boondoggle(); frobnicate();}do { for (i = 0; i < n; ++i) a[i] = 0; plugh(); xyzzy();} while (!blurf());
In switch
statements, be sure every case ends with either a break
, continue
, return
, or /* fall through */
comment. Especially don't forget to put a break
on the last case of a switch
statement. If you do, I promise someone will forget to add one someday when adding new cases.
switch (phase) {case New: printf("don't do any coding tonight\n"); break;case Full: printf("beware lycanthropes\n"); break;case Waxing:case Waning: printf("the heavens are neutral\n"); break;default: /* * Include occasional sanity checks in your code. */ fprintf(stderr, "and here you thought this couldn't happen!\n"); abort();}This last example also illustrates how to handle labels, including case labels and goto labels: put each label on a line by itself, and outdent it by a tab stop. However, if outdenting a label would take it all the way out to the left edge of the screen, insert a leading space.
Use goto
sparingly. Two harmless places to use it are to break out of a multilevel loop, or to jump to common function exit code. (Often these are the same places.)
Lay out your functions like this:
/* * Optional comment describing the function. */typename(args){ declarations code}It is important that the name of the function be in the first column of text with no indentation. Some text processing utilities (e.g.
etags
) rely on this to find function definitions. Even if you don't use such tools, it's extremely helpful to know that the regular expression ^
name matches the single definition of the function. Note that we will not be using old-style function definitions where the args are declared outside the parameter list. Include a blank line between the local variable declarations and the code. Also feel free to include other blank lines, particularly to separate major blocks of code.
Multiple declarations can go on one line, but if the line gets too long don't try to continue it in some fancy way, just start a new declaration on the next line. Avoid declarations in all but the most complex inner blocks. Avoid initializations of automatic variable in declarations, since they can be mildly disconcerting when stepping through code with a debugger. Don't declare external objects inside functions, declare them at file scope. Finally, don't try to go into denial over C's ``declaration by example'' syntax. Say:
char *p;not:
char* p;In the long run, such fights with the language will only cause you grief. (One of the reason's Stroustrup's original C++ book was practically unreadable was because he was constantly fighting with C.)
Use spaces around keywords. Use spaces around binary operators, except .
and ->
, for they are morally equivalent to array subscripts, and the ``punctuation'' operator ','. Don't use spaces around unary operators, except sizeof
and casts. Example:
x = -y + z + sizeof (Foo) + bar();Note that function call is a unary operator, so don't use a space between a function name and the opening parenthesis of the arguments. The reason for making an exception for
sizeof
is that it is a syntactic keyword, not a function. These rules lead to: if (something)for syntactic keywords, and
foo(something)for functions. Don't parenthesize things unnecessarily; say
return 7;not
return (7);and especially not
return(7);Remember, return is the exact antonym of function call! The parsing precedence of the bitwise operations (
&
, |
, ^
, ~
) can be surprising. See Ritchie's explanation for the reasons why. Always use full parentheses around these operators. Some C style guides take this a bit too far, though. One author went as far as to suggest that C programmers should rely on *
and /
bind more tightly than +
and -
, and parenthesize the rest. This is a good way to write Lisp code, but it makes C look ugly. A C programmer should be able to recognize its idioms and be able to parse code like:
while (*s++ = *t++) ;
If an expression gets too long to fit in a line, break it next to a binary operator. Put the operator at the beginning of the next line to emphasize that it is continued from the previous line. Don't add additional indenting to the continued line. This strategy leads to particularly nice results when breaking up complicated conditional expressions:
if (x == 2 || x == 3 || x == 5 || x == 7|| x == 11 || x == 13 || x == 17 || x == 19) printf("x is a small prime\n");This example also illustrates why you shouldn't add additional indenting when continuing a line - in this case, it could get confused with the condition body. Avoid breakpoints that will give the reader false notions about operator precedence, like this:
if (x == 2 || x > 10&& x < 12 || x == 19)If you're breaking an expression across more than two lines, try to use the same kind of breakpoint for each line. Finally, if you're getting into really long expressions, your code is probably in need of a rewrite.
Avoid sloppiness. Decide what your style is and follow it precisely. I often see code like this:
struct foo { int baz ; int barf; char * x, *y;};All those random extra spaces make me wonder if the programmer was even paying attention!
The indent
utility can automatically check most of these indentation conventions. The style given here corresponds to the indent options
-bap -bad -nbc -bs -ci0 -di1 -i8which can be specified in a file named
indent.pro
in your home directory. Note that indent
tends to mess up typedef
-defined identifiers unless they are explicitly given on the command line. a = b = c = 1;and assignment within expressions
if (!(bp = malloc(sizeof (Buffer)))) { perror("malloc"); abort();}This capability can sometimes allow concise code, but at other times it can obscure important procedure calls and updates to variables. Use good judgement.
The C language lacks a true boolean type, therefore its logic operations (! == > < >= <=
) and tests (in the conditional operator ?:
and the if
, while
, do
, and for
statements) have some interesting semantics. Every boolean test is an implicit comparison against zero (0
). However, zero is not a simple concept. It represents:
(i == 0) (x != 0.0) (c == '\0')instead of
(i) (!x) (c)An exception is made for pointers, since
0
is the only language-level representation for the null pointer. (The symbol NULL
is not part of the core language - you have to include a special header file to get it defined.) In short, pretend that C has an actual boolean type which is returned by the logical operators and expected by the test constructs, and pretend that the null pointer is a synonym for false. Write infinite loops as:
for (;;) ...not
while (1) ...The former is idiomatic among C programmers, and is more visually distinctive.
Feel free to use a for
loop where some of the parts are empty. The purpose of for
is to centralize all loop control code in one place. If you're thinking ``for each of these things, we have to do something,'' use a for
loop. If a for
statement gets too long to fit in a line, turn it into a while
. If your loop control is that complicated, it probably isn't what for
is for (pun intended).
Never return from the function main()
, explicitly use exit()
. They are no longer equivalent - there is an important distinction when using the atexit()
feature with objects declared locally to main()
. Don't worry about the details, just use this fact to program consistently. This does spoil the potential for calling main()
recursively, which is usually a silly thing to do.
static
to the module an optimizing compiler can inline them again, if necessary. Helper functions can also be reused by other functions. However, sometimes it is hard to break things down. Since functions don't nest, variables have to be communicated through function arguments or global variables. Don't create huge interfaces to enable a decomposition that is just not meant to be.
fgets
instead so that you can be sure that you don't overflow your buffer. malloc
, It has type void *
, so it will be compatible with anything. K&R2, p. 142 gives contrary advice, but it has since been retracted by Dennis Ritchie: In any case, now that I reread the stuff on p. 142, I think it's wrong; it's written in such a way that it's not just defensive against earlier rules, it misrepresents the ANSI rules.(From the newsgroup
comp.std.c
on August 15, 1995.) ` 名 言 錄`(리더스다이제스트 1983.3--1988.1) (0) | 2009.11.01 |
---|---|
AIMA Python Code 파이썬 인공지능 코드 (0) | 2009.10.18 |
한글필기체 폰트 (0) | 2009.10.04 |
추억의 XT 컴퓨터 : GW-BASIC (0) | 2009.10.04 |
Refactoring Tools Review - Part I (0) | 2009.10.03 |
AIMA Python Code 파이썬 인공지능 코드 (0) | 2009.10.18 |
---|---|
Standards and Style for Coding in ANSI C (0) | 2009.10.11 |
추억의 XT 컴퓨터 : GW-BASIC (0) | 2009.10.04 |
Refactoring Tools Review - Part I (0) | 2009.10.03 |
Ansi C 리펙토링 (0) | 2009.10.03 |
The Europeans realized this early on, and in 1982 the Conference of European Posts and Telegraphs (CEPT) formed a study group called the Groupe Spécial Mobile (GSM) to study and develop a pan-European public land mobile system. The proposed system had to meet certain criteria:
The developers of GSM chose an unproven (at the time) digital system, as opposed to the then-standard analog cellular systems like AMPS in the United States and TACS in the United Kingdom. They had faith that advancements in compression algorithms and digital signal processors would allow the fulfillment of the original criteria and the continual improvement of the system in terms of quality and cost. The over 8000 pages of GSM recommendations try to allow flexibility and competitive innovation among suppliers, but provide enough standardization to guarantee proper interworking between the components of the system. This is done by providing functional and interface descriptions for each of the functional entities defined in the system.
Using the ITU-T definitions, telecommunication services can be divided into bearer services, teleservices, and supplementary services. The most basic teleservice supported by GSM is telephony. As with all other communications, speech is digitally encoded and transmitted through the GSM network as a digital stream. There is also an emergency service, where the nearest emergency-service provider is notified by dialing three digits (similar to 911).
A variety of data services is offered. GSM users can send and receive data, at rates up to 9600 bps, to users on POTS (Plain Old Telephone Service), ISDN, Packet Switched Public Data Networks, and Circuit Switched Public Data Networks using a variety of access methods and protocols, such as X.25 or X.32. Since GSM is a digital network, a modem is not required between the user and GSM network, although an audio modem is required inside the GSM network to interwork with POTS.
Other data services include Group 3 facsimile, as described in ITU-T recommendation T.30, which is supported by use of an appropriate fax adaptor. A unique feature of GSM, not found in older analog systems, is the Short Message Service (SMS). SMS is a bidirectional service for short alphanumeric (up to 160 bytes) messages. Messages are transported in a store-and-forward fashion. For point-to-point SMS, a message can be sent to another subscriber to the service, and an acknowledgement of receipt is provided to the sender. SMS can also be used in a cell-broadcast mode, for sending messages such as traffic updates or news updates. Messages can also be stored in the SIM card for later retrieval [2].
Supplementary services are provided on top of teleservices or bearer services. In the current (Phase I) specifications, they include several forms of call forward (such as call forwarding when the mobile subscriber is unreachable by the network), and call barring of outgoing or incoming calls, for example when roaming in another country. Many additional supplementary services will be provided in the Phase 2 specifications, such as caller identification, call waiting, multi-party conversations.
The mobile equipment is uniquely identified by the International Mobile Equipment Identity (IMEI). The SIM card contains the International Mobile Subscriber Identity (IMSI) used to identify the subscriber to the system, a secret key for authentication, and other information. The IMEI and the IMSI are independent, thereby allowing personal mobility. The SIM card may be protected against unauthorized use by a password or personal identity number.
The Base Transceiver Station houses the radio tranceivers that define a cell and handles the radio-link protocols with the Mobile Station. In a large urban area, there will potentially be a large number of BTSs deployed, thus the requirements for a BTS are ruggedness, reliability, portability, and minimum cost.
The Base Station Controller manages the radio resources for one or more BTSs. It handles radio-channel setup, frequency hopping, and handovers, as described below. The BSC is the connection between the mobile station and the Mobile service Switching Center (MSC).
The Home Location Register (HLR) and Visitor Location Register (VLR), together with the MSC, provide the call-routing and roaming capabilities of GSM. The HLR contains all the administrative information of each subscriber registered in the corresponding GSM network, along with the current location of the mobile. The location of the mobile is typically in the form of the signalling address of the VLR associated with the mobile station. The actual routing procedure will be described later. There is logically one HLR per GSM network, although it may be implemented as a distributed database.
The Visitor Location Register (VLR) contains selected administrative information from the HLR, necessary for call control and provision of the subscribed services, for each mobile currently located in the geographical area controlled by the VLR. Although each functional entity can be implemented as an independent unit, all manufacturers of switching equipment to date implement the VLR together with the MSC, so that the geographical area controlled by the MSC corresponds to that controlled by the VLR, thus simplifying the signalling required. Note that the MSC contains no information about particular mobile stations --- this information is stored in the location registers.
The other two registers are used for authentication and security purposes. The Equipment Identity Register (EIR) is a database that contains a list of all valid mobile equipment on the network, where each mobile station is identified by its International Mobile Equipment Identity (IMEI). An IMEI is marked as invalid if it has been reported stolen or is not type approved. The Authentication Center (AuC) is a protected database that stores a copy of the secret key stored in each subscriber's SIM card, which is used for authentication and encryption over the radio channel.
Channels are defined by the number and position of their corresponding burst periods. All these definitions are cyclic, and the entire pattern repeats approximately every 3 hours. Channels can be divided into dedicated channels, which are allocated to a mobile station, and common channels, which are used by mobile stations in idle mode.
In addition to these full-rate TCHs, there are also half-rate TCHs defined, although they are not yet implemented. Half-rate TCHs will effectively double the capacity of a system once half-rate speech coders are specified (i.e., speech coding at around 7 kbps, instead of 13 kbps). Eighth-rate TCHs are also specified, and are used for signalling. In the recommendations, they are called Stand-alone Dedicated Control Channels (SDCCH).
The F burst, used on the FCCH, and the S burst, used on the SCH, have the same length as a normal burst, but a different internal structure, which differentiates them from normal bursts (thus allowing synchronization). The access burst is shorter than the normal burst, and is used only on the RACH.
Recall that the speech codec produces a 260 bit block for every 20 ms speech sample. From subjective testing, it was found that some bits of this block were more important for perceived speech quality than others. The bits are thus divided into three classes:
To further protect against the burst errors common to the radio interface, each sample is interleaved. The 456 bits output by the convolutional encoder are divided into 8 blocks of 57 bits, and these blocks are transmitted in eight consecutive time-slot bursts. Since each time-slot burst can carry two 57 bit blocks, each burst carries traffic from two different speech samples.
Recall that each time-slot burst is transmitted at a gross bit rate of 270.833 kbps. This digital signal is modulated onto the analog carrier frequency using Gaussian-filtered Minimum Shift Keying (GMSK). GMSK was selected over other modulation schemes as a compromise between spectral efficiency, complexity of the transmitter, and limited spurious emissions. The complexity of the transmitter is related to power consumption, which should be minimized for the mobile station. The spurious radio emissions, outside of the allotted bandwidth, must be strictly controlled so as to limit adjacent channel interference, and allow for the co-existence of GSM and the older analog systems (at least for the time being).
The most important component of DTX is, of course, Voice Activity Detection. It must distinguish between voice and noise inputs, a task that is not as trivial as it appears, considering background noise. If a voice signal is misinterpreted as noise, the transmitter is turned off and a very annoying effect called clipping is heard at the receiving end. If, on the other hand, noise is misinterpreted as a voice signal too often, the efficiency of DTX is dramatically decreased. Another factor to consider is that when the transmitter is turned off, there is total silence heard at the receiving end, due to the digital nature of GSM. To assure the receiver that the connection is not dead, comfort noise is created at the receiving end by trying to match the characteristics of the transmitting end's background noise.
The mobile station measures the signal strength or signal quality (based on the Bit Error Ratio), and passes the information to the Base Station Controller, which ultimately decides if and when the power level should be changed. Power control should be handled carefully, since there is the possibility of instability. This arises from having mobiles in co-channel cells alternatingly increase their power in response to increased co-channel interference caused by the other mobile increasing its power. This in unlikely to occur in practice but it is (or was as of 1991) under study.
The signalling protocol in GSM is structured into three general layers [1], [19], depending on the interface, as shown in Figure 3. Layer 1 is the physical layer, which uses the channel structures discussed above over the air interface. Layer 2 is the data link layer. Across the Um interface, the data link layer is a modified version of the LAPD protocol used in ISDN, called LAPDm. Across the A interface, the Message Transfer Part layer 2 of Signalling System Number 7 is used. Layer 3 of the GSM signalling protocol is itself divided into 3 sublayers.
An RR-session is always initiated by a mobile station through the access procedure, either for an outgoing call, or in response to a paging message. The details of the access and paging procedures, such as when a dedicated channel is actually assigned to the mobile, and the paging sub-channel structure, are handled in the RR layer. In addition, it handles the management of radio features such as power control, discontinuous transmission and reception, and timing advance.
There are four different types of handover in the GSM system, which involve transferring a call between:
Handovers can be initiated by either the mobile or the MSC (as a means of traffic load balancing). During its idle time slots, the mobile scans the Broadcast Control Channel of up to 16 neighboring cells, and forms a list of the six best candidates for possible handover, based on the received signal strength. This information is passed to the BSC and MSC, at least once per second, and is used by the handover algorithm.
The algorithm for when a handover decision should be taken is not specified in the GSM recommendations. There are two basic algorithms used, both closely tied in with power control. This is because the BSC usually does not know whether the poor signal quality is due to multipath fading or to the mobile having moved to another cell. This is especially true in small urban cells.
The 'minimum acceptable performance' algorithm [3] gives precedence to power control over handover, so that when the signal degrades beyond a certain point, the power level of the mobile is increased. If further power increases do not improve the signal, then a handover is considered. This is the simpler and more common method, but it creates 'smeared' cell boundaries when a mobile transmitting at peak power goes some distance beyond its original cell boundaries into another cell.
The 'power budget' method [3] uses handover to try to maintain or improve a certain level of signal quality at the same or lower power level. It thus gives precedence to handover over power control. It avoids the 'smeared' cell boundary problem and reduces co-channel interference, but it is quite complicated.
The location updating procedures, and subsequent call routing, use the MSC and two location registers: the Home Location Register (HLR) and the Visitor Location Register (VLR). When a mobile station is switched on in a new location area, or it moves to a new location area or different operator's PLMN, it must register with the network to indicate its current location. In the normal case, a location update message is sent to the new MSC/VLR, which records the location area information, and then sends the location information to the subscriber's HLR. The information sent to the HLR is normally the SS7 address of the new VLR, although it may be a routing number. The reason a routing number is not normally assigned, even though it would reduce signalling, is that there is only a limited number of routing numbers available in the new MSC/VLR and they are allocated on demand for incoming calls. If the subscriber is entitled to service, the HLR sends a subset of the subscriber information, needed for call control, to the new MSC/VLR, and sends a message to the old MSC/VLR to cancel the old registration.
For reliability reasons, GSM also has a periodic location updating procedure. If an HLR or MSC/VLR fails, to have each mobile register simultaneously to bring the database up to date would cause overloading. Therefore, the database is updated as location updating events occur. The enabling of periodic updating, and the time period between periodic updates, is controlled by the operator, and is a trade-off between signalling traffic and speed of recovery. If a mobile does not register after the updating time period, it is deregistered.
A procedure related to location updating is the IMSI attach and detach. A detach lets the network know that the mobile station is unreachable, and avoids having to needlessly allocate channels and send paging messages. An attach is similar to a location update, and informs the system that the mobile is reachable again. The activation of IMSI attach/detach is up to the operator on an individual cell basis.
The same initial random number and subscriber key are also used to compute the ciphering key using an algorithm called A8. This ciphering key, together with the TDMA frame number, use the A5 algorithm to create a 114 bit sequence that is XORed with the 114 bits of a burst (the two 57 bit blocks). Enciphering is an option for the fairly paranoid, since the signal is already coded, interleaved, and transmitted in a TDMA manner, thus providing protection from all but the most persistent and dedicated eavesdroppers.
Another level of security is performed on the mobile equipment itself, as opposed to the mobile subscriber. As mentioned earlier, each GSM terminal is identified by a unique International Mobile Equipment Identity (IMEI) number. A list of IMEIs in the network is stored in the Equipment Identity Register (EIR). The status returned in response to an IMEI query to the EIR is one of the following:
An incoming mobile terminating call is directed to the Gateway MSC (GMSC) function. The GMSC is basically a switch which is able to interrogate the subscriber's HLR to obtain routing information, and thus contains a table linking MSISDNs to their corresponding HLR. A simplification is to have a GSMC handle one specific PLMN. It should be noted that the GMSC function is distinct from the MSC function, but is usually implemented in an MSC.
The routing information that is returned to the GMSC is the Mobile Station Roaming Number (MSRN), which is also defined by the E.164 numbering plan. MSRNs are related to the geographical numbering plan, and not assigned to subscribers, nor are they visible to subscribers.
The most general routing procedure begins with the GMSC querying the called subscriber's HLR for an MSRN. The HLR typically stores only the SS7 address of the subscriber's current VLR, and does not have the MSRN (see the location updating section). The HLR must therefore query the subscriber's current VLR, which will temporarily allocate an MSRN from its pool for the call. This MSRN is returned to the HLR and back to the GMSC, which can then route the call to the new MSC. At the new MSC, the IMSI corresponding to the MSRN is looked up, and the mobile is paged in its current location area (see Figure 4).
Telecommunications are evolving towards personal communication networks, whose objective can be stated as the availability of all communication services anytime, anywhere, to anyone, by a single identity number and a pocketable communication terminal [25]. Having a multitude of incompatible systems throughout the world moves us farther away from this ideal. The economies of scale created by a unified system are enough to justify its implementation, not to mention the convenience to people of carrying just one communication terminal anywhere they go, regardless of national boundaries.
The GSM system, and its sibling systems operating at 1.8 GHz (called DCS1800) and 1.9 GHz (called GSM1900 or PCS1900, and operating in North America), are a first approach at a true personal communication system. The SIM card is a novel approach that implements personal mobility in addition to terminal mobility. Together with international roaming, and support for a variety of services such as telephony, data transfer, fax, Short Message Service, and supplementary services, GSM comes close to fulfilling the requirements for a personal communication system: close enough that it is being used as a basis for the next generation of mobile communication technology in Europe, the Universal Mobile Telecommunication System (UMTS).
Another point where GSM has shown its commitment to openness, standards and interoperability is the compatibility with the Integrated Services Digital Network (ISDN) that is evolving in most industrialized countries, and Europe in particular (the so-called Euro-ISDN). GSM is also the first system to make extensive use of the Intelligent Networking concept, in in which services like 800 numbers are concentrated and handled from a few centralized service centers, instead of being distributed over every switch in the country. This is the concept behind the use of the various registers such as the HLR. In addition, the signalling between these functional entities uses Signalling System Number 7, an international standard already deployed in many countries and specified as the backbone signalling network for ISDN.
GSM is a very complex standard, but that is probably the price that must be paid to achieve the level of integrated service and quality offered while subject to the rather severe restrictions imposed by the radio environment.
GTK+ 프로그래밍 기초 자료2 (0) | 2009.11.01 |
---|---|
GTK+ 프로그래밍 기초 자료 (0) | 2009.11.01 |
휴대폰 개발 프로세스 (1) | 2009.09.27 |
프로그래밍 언어 포럼 순위 (랭킹) (1) | 2009.09.27 |
파이썬 로보틱스 프로그래밍 (0) | 2009.09.20 |
[출처] http://www.geocities.com/KindlyRat/GWBASIC.html
GW-BASIC
Download GW-BASIC here! Below we have GWBASIC 3.23, GW-BASIC Manual, Compiler, tutorials, examples and games, all absolutly free. Just click on the blue lettered selection, save and un-zip.
Welcome Modern School of Nagpur
--DIRECTIONS--
Click, download and unzip the following zip files:
GWBASIC.EXE.zip - the complete GW-BASIC 3.23 ( 60.3K )
GWBASIC_Help.zip - a complete GW-BASIC manual by Microsoft in "help" format with indexes and easy to use format. Many thanks to Tom Cloud!
gw-man.zip - a complete GW-BASIC manual by Microsoft in html files. Thanks to Thomas Shaffner. Click on index 2 after downloading. It is an excellent linked index.
Another manual for GW-BASIC.
COMPILER.zip - a GW-BASIC compiler. Read the "read me" for all the information that I know. It outputs a stand alone exe file when compiled with the /O option and linked.
OptionList.zip - a list of options for the GW-BASIC compiler (BASCOM). Some do not work.
Compiling GW-BASIC -porting GW-BASIC to QuickBasic2.0 ( probably our compiler ).
GWCOM.html - an optional list of commands. 12 Jun 2002 by KindlyRat before the above manual was recieved.
exil- a Linux / GW-BASIC converter.
basictut.zip - an optional tutorial by Castcoly Software. After unzipping click BT1 and BT2 and press "page up" and "page down" to read.
Free BASIC Compilers and Interpreters
Norman DeForest - deep research into the GW-BASIC language.
Scott's GW-BASIC / Windows solutions!
IconToBasic - run your favorite BASIC programs from desktop Icons!
BASICtoTEXT - change BASIC to Text (Notepad) files.
GWscan - a program from ScottServer that does the same thing. Check out the other programs too!
Here's another good GW-BASIC tutorial.
Here's a link to a TRS-80 (RadioShack) to Windows translator by AW Software.
hbasic.zip - A version of GW-BASIC for Hercules graphics
Commodore Basic for Windows.
Microsoft Small Basic A new BASIC for Windows!.
font.zip - a font set for GW-BASIC for Hercules graphics
Here's some new fonts for the DOS Box that we use with GW-BASIC, Thanks to Matt Gregory!
-- HINTS --
GW-BASIC was once the main language used on home computers and is still useful in learning the fundamentals of computer programming and smaller utility programs. It still runs on Microsoft Windows computers thru WindowsXP, though not in a window, but on the black screen used for MS-DOS programming above that will pop up when you click on the GWBASIC icon.
GW-BASIC is an interpreted language which means that it isn't compiled. You type the program in or copy and paste it into a text ( Notepad ) file saved with the name and a .BAS suffix, surrounded by quotation marks. Entering "run" will start the program. GW-BASIC uses line numbers and runs and saves files with a ".BAS" suffix in the same file as the interpreter. You can also run a program by "dragging and dropping" the program on top of the interpreter.
You can find a Users Guide and Reference at any good garage sale or library. Also don't forget to check Amazon.com's used books and I found a "Microsoft GW-BASIC Users Guide and Reference" at Half.com for $0.75! If all else fails I have a list of GW-Basic commands for download above and several tutorials and links to tutorials.
---GWBASIC GAMES---
note: these games are for GW-BASIC, BUT can be "LIST"ed and clipboarded and saved to notepad. Then you can load them into other BASIC languages. You may have to change a few lines.
dominoes.zip...a good dominoes game
chess1.zip...chess game by RAKASKA
SLOT.zip...slot machine game by ROCHE FAHLANEIGH
MNPLY.zip...monoply game by ROCHE FAHLANEIGH
bluesbox.zip...a song writing / playing program
checkers.zip...a good but dumb game
SantaParavia.zip...( UNDER DEVELOPMENT )
lemonade.zip...run a lemonade stand
HAMURABI.zip...rule a primitive society
HAMURABI3.zip...rule a primitive society
Slot6.zip...a good slot machine game
Pic3.zip...draws random colorful rectangles
BATNUM.zip...a good math game. add line "110 RANDOMIZE TIMER" for better random numbers
23mtch.zip... an old 23 Match game
aceydu.zip... acey deucy anyone?
buzzwd.zip... a buzzword creator
chomp.zip... two or more players. from ScientificAmerican
craps.zip... a good craps game
stars.zip... a number guessing game
fibonacc.zip... a Fibonacci Series Calculator
mugwmp.zip... find the mugwmp on a 10 X 10 grid
eliza.zip... an artificial intelligence game
nim.zip... a good NIM game, use caps
Scott...BASIC programs
salvo1.zip...an artillery game
---CODE EXAMPLES---
archie.zip...learn Structured BASIC Programming!
BAS-INT.zip...C code for a tiny BASIC interpreter.
gwup07.zip...decodes protected GW-BASIC programs
sequential.zip...demonstrates search, list first and read
circles.zip... random circles in SCREEN1
SDOFRND2.zip... predict future by random generators
snails.zip...population sampeling theory test
PIBAS.zip...PI calculation by the casino method
RootSumSquare.zip... prove a calculation method
factor.zip... finds the factors of numbers
demo2.zip... old Zenith BASIC graphics demo
menuet.zip... PLAY music demonstration
binary.zip...demonstrates search, list first and read
dietest.zip...checks the Random Generator
two-buffer.zip...demonstrates sort of A$, list first and read
bubble.zip...demonstrates sort of A$, list first and read
Mandelbrot.zip...Mandelbrot Fractal demonstration
ListDisk.zip... by Eric Tchong, formats BASIC programs for listing or printing.
SortThem... another Eric Tchong QuickBASIC program that demonstrates sorts.
RanHex.zip...random hexadecimal generator
Cypher.zip...a text encoding program
AnimalVegtableMineral.zip...a game/example that learns
99bob.zip...demonstrates messing around in BASIC
combinations.zip...possible combinations of three numbers
MorseConverter...converts a message to Morse Code.
KindlyRat
Standards and Style for Coding in ANSI C (0) | 2009.10.11 |
---|---|
한글필기체 폰트 (0) | 2009.10.04 |
Refactoring Tools Review - Part I (0) | 2009.10.03 |
Ansi C 리펙토링 (0) | 2009.10.03 |
디지털 타임즈 [알아봅시다] 교육의 틀을 바꾸는 `디지털교과서` (0) | 2009.09.20 |
By Uri Lavi
Cross post from IRefactor
Don Roberts and John Brant stated in the book Refactoring - Improving the Design of Existing Code:
"Refactoring with automated tool support feels different from manual refactoring".Indeed - It is!
한글필기체 폰트 (0) | 2009.10.04 |
---|---|
추억의 XT 컴퓨터 : GW-BASIC (0) | 2009.10.04 |
Ansi C 리펙토링 (0) | 2009.10.03 |
디지털 타임즈 [알아봅시다] 교육의 틀을 바꾸는 `디지털교과서` (0) | 2009.09.20 |
C/C++ Programming Links (0) | 2009.09.02 |
General Remarks
On this page we only want to give an overview on some of the methods we apply in the refactoring of C code. These methods differ from the methods decribed e.g. in Martin Fowlers book about refactoring. One of the reasons is that we developed the methods for C where as Martin Fowler describes them for Java. Another even more relevant reason is that we set up the methods for safety critical microcontroller applications which have to use MISRA C and usually use a defined rigid architecture and design rules. Refactoring in this environment has to follow defined rules. For more details we recommend to read the page on refactoring principles where we go into more details on this subject.
Make the Code Error free according to the MISRA checking Tool
I discussed the ideas of a safe coding in C on the pages about design, therefore I will not repeat it here. The main goal of this refactoring step is to let an automatic code checking tool run on your code and analyze its output log file. These tools come up with most of the important errors you can bring into a C-program while you code. These are potential erros like the:
if (a = b){... some code ...}
where almost certainly it was meant to be:
if (a == b){... some code ...}
The tool will also come up with error messages concerning suspicious type casts. If you have:
void my_function(void)
{
unsigned char x;
unsigned short y;
... some code ...
x = y;
... some code ...
}
The assignment of y to the variable x will lead to a loss of the upper 8 data bits. This may be perfectly o.k. because your data range of y is within 8 bit. Then you can do a simple type cast to solve the problem:
void my_function(void)
{
unsigned char x;
unsigned short y;
... some code ...
x = (unsigned char)y;
... some code ...
}
Refactoring at this point has to check the possible data ranges and find a satisfying solution for the problem. This may be a simple type cast to keep the checking tool silent, but it also may mean a bigger re-design of the code portion to avoid the loss of bits in an assignment.
The overall goal is to work on the code util the checking tool does not report any more errors. This will guarantee a certain quality of the code, which is suitable for safety critical systems.
Get rid of Bitfields
The bitfields in focus here are the so called ANSI-bitfields. They are commonly named that way because they already have been defined in the old ANSI-C standard (1989). They are defined as follows:
struct BF {
int bit0:1;
int bit1:1;
int bit2:1;
int bit3:3;
int bit4:1;
int bit5:1;
int bit6:1;
} bitfield;
The access to the individual bit is done as:
bitfield.bit1 = 1;
There are several problems involved in these bitfields. First of all not all compilers allow the same kind of definitions. Some compilers allow multiple data types as e.g. "unsigned short" or "unsigned char" for the definition of the individual bits. The data types can be all basic data types and they can be mixed in the same struct definition. Other compilers rigidly stick to the old ANSI definition which only allows the datatype "int" for the bits and multibit fields in the struct (multibit fields have e.g. the size of 3 bits in our example). However if you are forced to use the data type "int" in reality it makes the individual bit a signed number of the value -1. Therefore if you use a bit in any calculation it will use the -1 or 0 for the calculation although you most likely would expect it to be 1 and 0. This means you have a problem due to unexpected values in your calculation but you also may have a problem with the portability of your code. Only if you restriced yourself to using "int" as datatype for the bits you can be sure that your code is portable across all compilers.
Another problem is that the memory representation of your ANSI-bitfield is completely implementation dependent. You simply do not know how the stuff is stored in the memory. This means you can not access it as a complete word, character or long variable. Such a way of accessing the bitfield may come in handy if you think of a fast initialization. You could initialize the bitfiled by writing to it as a word variable and assigning 0 to it. Since you do not know how it is represented in memory you are forced to initialize it bit by bit via the symbolic expression. Another problem due to the various memory representations may occur if you have to store memory data e.g. to a file in one CPU environment and reload the data in another environment with a different memory representation of bitfields.
Because of these problems I came up with a method to use normal variables such as unsigned short or unsigned long as bitfields. With some smart macros I can set a bit e.g. with the expression bitfiled |= 0x8000; and reset the same bit with the expression bitfield &=~ 0x8000; With a couple of defines for the bits and the operations this leads to an easy to use method for bitfields which can be also accessed as normal variables. Their size is the size of the basic datatype used in the definition. I also came up with some functions to read and write the multibit fields inside a bitfield. They look a bit complex, but runtime measurements showed that this way of dealing with bitfields is even faster than the operations on ANSI-bitfields.
Because of these problems you should have a refactoring step which replaces all ANSI-bitfields with the smarter and less problematic solution.
Data Types have to be used according to the Design Rules
In the design rules I set up we first of all prohibited all floating point variables. Only a few microcontrollers have a floating point unit and can handle them without overhead. A micro which does not have a FPU will go into quite lengthy library calls to deal with them. This is simply a runtime killer. On top of it there was never a series application which really required the precision and data range of floats. Another problem is that floats are a bit tricky in compare operations (if, for, while, etc.). The expression if (a == b) will be rarely fulfilled if both or one of the variables is a float. The same is true for loops. For these reasons I banned floating point values completely out of all systems I was responsible for.
Other datatype issues are mainly related to the mixing of datatypes. You should avoid mixing signed and unsigned calculations. Bring everything to the one kind. However if you are forced to mix them you should make sure that the conversions are save and e.g. the signed value is definitely not negative before you assign it to an unsigned value. Or have you ever seen this example?:
int main(void)
{
unsigned char x;
x = ~0xAA;
if (x == ~0xAA)
{
printf("I should come out here\n\n");
}
else
{
printf("I should not be here\n\n");
}
getchar();
}
Everybody would expect to come out in the "I should come out here" branch. But it doesn't! The problem comes from the implicit promotion (8bit -> 16bit) the C compiler performs and then it casts it back to 8bit. A problem due to the implicit mixing of datatypes. The refactoring should take care of this and bring the code into a save condition.
No Global Variables
I described the danger of global variables sufficiently in the design section of this web-site and there is no need to repeat it here. It is also against the idea of object orientation to have global variables. One of the outstanding features of object orientation is that the data are combined with the operative code lines in the same module and that these data are encapsulated. This means that they can not be accessed from other modules (object) except via the defined get-function. If your code needs global variables it is suffering from much more serious design sins and should be refactored to conform to a true object oriented design. After you have done this it should be no problem to make the global variables at least static and thus not linkable for other modules.
Standard C-Libraries avoided?
Most programmers will be puzzled by this demand to get rid of the use of standard C-Libraries. But here I am talking about safety critical microcontroller applications. The demands of the related international standards show clearly that your source code has to undergo certain quality and test measures before it can be certified in the used system. In other words there is a big fuzz about the code YOU wrote and then you just use the code SOMEBODY ELSE wrote in the form of standard libraries. And on top of it, in most cases you do not even have the source code of the library so that you are not even able to see how it was done. What? Do you believe this code is perfect just because it was sold to you when you bought your compiler? Fairytales! It was done by programmers and they make mistakes. If you want to have the control of what is happening in your safety critical system you must not use third party software and if you have to use it for some reason it has to be transparent regarding the source code, the tests which have been performed on it and the other quality measures which were employed. Usually you do not get this proof. You just get a heap of binaries. Make your own libraries, test them according to the required measures by the international standards and make sure by refactoring that it is used everywhere in your code.
Check the Control and Data Flow
As indicated in the design section a good system should be data flow driven in its design and the data flow should be uni-directional as far as possible. This means that the outputs which have to be generated by the system need certain data. The output should aquire the data it needs to perform the output. This data is processed in some way and in turn relies on further data, until you reach the inputs of your system. In other words your objects should have get-functions but preferably no set-functions. Make sure in the refactoring phase that this design princple is adhered to. It may mean bigger changes in the design if you have to restructure the code, but it is worth while.
Use defined Naming Conventions
Naming conventions are a bit formalistic and thus not very highly esteemed. However if you name your variables, functions and modules consistently and according to a defined system you will have the great benefit that everybody will immediately know what the code is all about. Starting from the programmer himself, the colleagues who does a review, the next programmer who wants to re-use it or do maintenance on it, the tester, all will benefit from it. I will not go into the naming convention itself since I did this on another page, but use this refactoring method and apply the naming convention to make the code better understandable.
Use defined Templates
Define templates for your code and use them! There should be clearly identified sections where you put typedefs, defines, function prototypes, the source code itself, etc. etc. The reasons are similar to those for using defined names for variables and functions. It makes the whole code better understandable and everybody knows where to find what.
Keep the Header Structure simple
Some years ago I came as a newcomer into a project and tried to get all the code and headers collected and tried them to compile. It took me three days to succeed! O.k. there were some configuration managent problems and I had to get some files from certain colleagues instead of beeing able to get it from the code repository. But most of the time I wasted with finding out which headers I needed to get in which of their versions. Finally, after I succeeded, I found that for the literal handful of source code modules I needed almost 50 different header files which were nested, had cross includes and all sorts of dependencies. Many of the headers were included because a single define was needed out of the hundreds it contained. I cleaned it out after I was put in charge of the design. Now the same software needs 5 headers and they do not have any dependencies, cross includes or circular includes. And they are single level includes i.e. there are no nestings. The refactoring step needs to be based on a clear concept and "design" of your headers. You need to know what headers there shall be and what they have to contain. Then make sure that they do not depend on each other. Include them in the right sequence rather than making nestings. The following example shows how to reduce the nestings:
The header file macros.h makes use of a basic data types in the form of a redefinition of "unsigned short"
#define HIGEST_BIT(T_UWORD)0x8000
In order to be able to do this the file macros.h should include the file which contains the redefinition of "unsigned short"
#include"datatypes.h"
#define HIGEST_BIT(T_UWORD)0x8000
The datatype.h file contains the following define:
typedef unsigned shortT_UWORD;
But the problem is that many other headers will also need these data type redefinitions. Now imagine that the same happens with other defines which are located in other headers. The result is a nested header structure with circular and cross includes, like the one I described above. The solution is quite simple. You just have to prohibit sub-includes completely, and then after you have set up a clear concept what shoud be in which header, simply include the headers in the right sequence in your source files:
#include"datatypes.h"
#include"macros.h"
Then all headers which are included after the inclusion of the datatypes have them available and do not need to include it for themselves.
Split up C-Functions
There are a few indicators which tell you if a C-function should be split up or not. One of the indicators is the parameter list. If the pass parameters to a function are more than 4 then you should have a closer look to find a way to split up the function. The number of 4 is based on the usual number of parameters which can be passed to a function in CPU registers. If you have more parameters they go on the stack. This value may differ depending on the CPU and compiler you use. In some cases this may be six parameters, but not more. This rule is concerned about the runtime and resources of your application, but at the same time it can be said that the more parameters a function has the more complex it is. A condidate to be split in several smaller functions.
I have seen functions with 40 input interface variables. Not as pass parameters but as global variables. A closer look showed that the function contained a number of independent sections. Each section had e.g. 10 code lines and used 5 of the variables, but the sections were not related to each other in any way. This was an ideal candidate to make a function out of each of these sections and use a pass parameter interface to hand them the needed data. Generally speaking if a functions is bigger than what you can display on a single screen page you should find a way to split it up.
Use temporary Variables the Right Way
There are some commonly practiced sins concerning local variables. First of all you should not write to a variable which came in by the pass parameter interface. It is an input and a good programming style is that you never ever ovewrite an input. Neither a temporary variable which came in via the pass parameter interface nor static or global data. Always open a new variable if you have to modify something on your inputs. If you do not observe this it may be hard to test your software.
Secondly avoid to reuse the same local variable for different purposes. I saw programs which had a variable called "temp" and it was used in sequence as a loop counter, for storing intermediate calculation results, etc. etc. Open an own variable for each of these purposes. If you fear that you end up with too many local variables in your function and that they are therefore put on the stack, you should split the function!
Use local variables for all operations in your function and interface to the outside world i.e. static variables or get-functions of other objects at the beginning and at the end of your function. This advise is contrary to what you find in other refactoring literature but there are some good reasons for it. First of all there is the subject of runtime again. If you call a get-function multiple times in a piece of code, the function will a be always called and executed. This generates some overhead because in order to call a function the address of the next instruction after the function call will be stored on the stack, some registers may be saved to the stack, there has to be a jump to the called function and finally a jump back to the stored next instruction of your calling program part. All this is some overhead if you compare it to simply accessing a variable in memory. Therefore call the sub-function once and use the value which will be held in a register throughout your part of the program. This is a trade off between an object oriented design on the one hand and an optimized speed on the other hand. The same is true for the use of static data in your piece of code. If a variable which is residing permanent in the memory is used multiple times the program will always do a fetch from the memory each time it comes across the variable. This fetch operation will also consume extra time which you can avoid if the value is loaded to a register (local variable) and then used in the program. The following example will illustrate this. It uses a pseudo Assembler code to illustrate what operations are performed by the CPU on the memory to run through a sequence of code lines:
void my_function()
{
... some code ...
if (My_StaticValue < -8191) Register1 = Memory(My_StaticValue)
Register2 = -8191
Register1 COMPARE TO Register2
{
My_StaticValue = -8191; Memory(My_StaticValue) = Register2
}
else
{
if (My_StaticValue > 8191)Register1 = Memory(My_StaticValue)
Register2 = 8191
Register1 COMPARE TO Register2
{
My_StaticValue = 8191;Memory(My_StaticValue) = Register2
}
}
}
Now I show the example of the same code lines if the value is not residing in memory, but is coming in by the pass parameter interface:
void my_function(My_Value)Register1 contains My_Value
{
... some code ...
if (My_Value < -8191)
Register2 = -8191
Register1 COMPARE TO Register2
{
My_Value = -8191; Register1 = Register2
}
else
{
if (My_Value > 8191)
Register2 = 8191
Register1 COMPARE TO Register2
{
My_Value = 8191;Register1 = Register2
}
}
}
As you can see the second design option uses less code lines and therefore executed faster. Less code simply means faster execution. But on top of it you have to consider that the access to the memory is a slow operation. A line accessing the memory can take double or even more of the time that an operation using registers would take. There are influences like the waitstates and other bus issues which slow down these operations.
But there is another subject which gives us a good reason for interfacing to the outside world and using temporary variables inside the function. As you can see in the above example concerning the data types the implicit promotion and type casting mechanism of C-compilers can play quite nasty tricks on you. By interfacing every used variable to locals you can use local variables which are of the natural bit size of the registers and thus avoid automatic promotion by the compiler. With other words you simply perform a step explicitly which would be done by the compiler anyway implicitly without you being aware of it. The casting and promotion between the various bit sized variable types is under your control after you did this refactoring step. In a code for a 16 bit CPU could look like:
unsigned short my_function(unsigned char my_offset)
{
unsigned short x;
unsigned short y;
x = (unsigned short)my_StaticCharacterX;
y = (unsigned short)my_StaticCharacterY;
if (x <= y)
{
x = y + (unsigned short)my_offset;
}
else
{
x = y - (unsigned short)my_offset;
}
return(x);
}
Resolve multiple Definitions
Have you ever seen data definitions using a struct in a struct in a struct in a blabla and so on?
There is the feeling of the programmer or some other reason to group data together in structs. Certainly there is a place for structs in C but nesting them over more than one level turns into a nightmare. The access to an element would then look like:
My_struct.my_first_substruct.my_second_substruct.yet_another_struct.element = 1;
Because this is quite a mouth full and turns unreadable in the code smart programmers tend to have the idea to make defines to shorten the "mouth full". Then they do something like this:
#define STRUCT_L1 My_struct.my_first_substruct
#define STRUCT_L2 STRUCT_L1.my_second_substruct.yet_another_struct
#define MY_ELEMENT STRUCT_L2.element
And after they did this proud piece of redefinitions they are going to use "MY_ELEMENT" in their code. The result is that the person doing maintenance or testing on these structures and their redefinitions will simply go coo coo after a day of work trying to find the bottom of this swamp. No joke, I have seen nestings of ten levels and more! Get rid of this. Beak it up. There is no logical reason for grouping every single variable or constant used in the system in ONE struct. There are ways you can convince your linker to pack stuff together in the memory, and there are ways to break up the monster structs into smaller ones. Simple basic data types, simple and small structs, if needed a few arrays, that's what you should go for and your refactoring should achieve this.
Use Defines instead of Fixed Numbers
This is a simple refactoring method. Instead of using fixed values like 3.14 you should make a define like:
#define PI 3.14
Then in your code you should write "PI" instead of typing "3.14" at the places you need the constant. This makes the code better understandable, especially if your defines have sensible names. Further you can easily change the define at one place and all your code uses the updated value. Of course only if you used the define consequently at all places. This will avoid to have inconsistencies in case a constant changes. No need to look through all the modules in this case. The change is effiective at all places.
Avoid Function like Macros
Some programmers love function like macros. They seem to use them at every possible occasion. However they have a number of problems and therefore I prohibited them in all projects I was responsible for. Let's look at one of the problems. Imagine we have the following macro to do an absolute value calculation:
#define abs(x) (x>0)?x:-x
Then we use the macro in the follwoing code lines:
int main(void)
{
int b;
int c;
int y;
b = 5;
c = 7;
y = abs(b-c);
}
What you would expect is the result of 2 being assigned to y, because 5 - 7 equals to -2 and the absolute value of -2 is 2. But the function like macro does not do the trick. The actual result is -12 which is simply wrong. The reson for this is that the macro undergoes a text replace and thus the code line y = abs(b-c); extends to:
y = (b-c>0)?b-c:-b-c
Therefore the result is -b-c which is -12.
There are other problems with macros. If you do not explicitly use type casts to make the involved variables the data types you define, they will be automaitcally promoted to "int", which could involve the change of the sign and the bitsize of the value. This also leads to anwanted effects. I simply prohibited them because there is no logical reason for having them. Just make a simple function out of it and if you fear the overhead make the function "inline" and it will be extended to every code portion where the function is called, just like a macro would be, however without the dangers I just pointed out. Just one further hint. Inline functions need to be in the same module as the code which makes use of them, but if you put the inline function into a header it even can be used by every module which includes the header. For this subject you should disregard the MISRA standard. MISRA has no problem to allow function like macros (although they come up with some restrictions) but the 2004 MISRA it prohibits in rule 8.2 to put code into a header. At this point the standard is simply stupid and incorrect and you should do your own sensible rules here, like I pointed out in this refactoring step.
Simplify logical Conditions
Some multiple conditions in C-programms can be very hard to comprehend, and they are even harder to be tested. The same is true for nested "if" conditions. The following line may serve as an example:
if((a == b) || (c == y) || (z != a)) { printf("hello world");}
You can split this up into a few simple if conditions which make it much easier to see what is going on:
tmp = 0;
if(a == b) tmp = 1;
if(c == y) tmp = 1;
if(z != a) tmp = 1;
if (tmp == 1) printf("hello world");
Sorry for this simple example. It should show the principle of this refactoring step. If you look at the assembler lines the compiler makes out of the two examples you will find that the effort is almost the same and runtime or other resource issues can not be an argument against it. But it is much easier to be understood and it is much easier to modify it and test it.
Cut your Modules the Right Way
How should your modules look like? There are a few simple metrics which can help you to set them up in a way that they have a good size. As already mentioned each function in a module should not be bigger than what can be displayed on on screen page. Then there is the number of get-functions in a module. More than 20 is definitely too much and it is an indicator that you should find a way to split it up. Between 10 and 20 is o.k. if it can be justified that the module is good this way. 10 or less get-functions is ideal. Set-functions should be the exception in order to keep the data flow unidirectional. The number of private functions (sub-functions only used inside the same module) can be as much as needed. Again their size should be not bigger than a screen page. If there are very many very little ones you should also have a look into the design of the module. The number of public functions (except get-functions) should not exceed 3. In addition to that there may be a init-function which initializes the module. These numbers are values from experience and could be found in good design I saw so far.
In addition to that you should have a look at the interfaces of a module (object). The best is if they have basic data types. If you have arrays and structs (i.e. pointers to them) at the interfaces you should have a second look to see if this can be avoided. Very often you can get rid of such an interface by simply moving one function from one module to another.
Last but not least you should always make sure that your modules constitute objects in the sense of object orientation. I.e. they should represent an object of the real world, e.g. a sensor, a switch, a solenoid. Of course you should also make sure that you have an architecture which defines layers in your software (e.g. a HW-abstraction layer). All these principles should influence your refactoring activities on the size and shape of your modules.
추억의 XT 컴퓨터 : GW-BASIC (0) | 2009.10.04 |
---|---|
Refactoring Tools Review - Part I (0) | 2009.10.03 |
디지털 타임즈 [알아봅시다] 교육의 틀을 바꾸는 `디지털교과서` (0) | 2009.09.20 |
C/C++ Programming Links (0) | 2009.09.02 |
대학시절 자료복구[일부] http://comphy.iptime.org/~old/ (0) | 2009.08.31 |
하드웨어적 관점에서 접근한
휴대폰 개발 프로세스
휴대폰 개발은 다른 임베디드 시스템 이상으로 방대한 특성 탓에 각 분야마다 많은 전문 인력이 투입되어 개발이 진행된다. 이 글에서는 하드웨어를 중심으로 휴대폰 개발의 개괄적인 현황과 개발 프로세스를 설명하고, 다른 임베디드 시스템 개발에도 적용될 수 있는 소프트웨어 기법 몇 가지를 추가로 소개한다.
먼저 프로토콜, 플랫폼 등을 기준으로 휴대폰의 변화 과정을 설명하면서 개발 프로세스를 이해하는 데 필요한 기본 지식을 점검해 본다.
프로토콜에 따른 구분
먼저 프로토콜에 따라 휴대폰 서비스를 구분해 보면 다음과 같다.
CDMA와 GSM
무선 구간에서 기지국과 단말기 사이의 신호 전송방식은 동기식과 비동기식으로 구분되는데 북미 방식인 동기식은 기지국에서 GPS(Global Positioning System)의 시간과 위치정보를 이용하고, 유럽방식인 비동기식은 GPS를 사용하지 않는다. CDMA (Code Division Multiple Access)로 대표되는 동기식의 경우 IS-95 프로토콜을 포함한 cdmaOne과, IS-2000 프로토콜을 근간으로 한 cdma2000이 있으며, 비동기식은 GSM(Global Service for Mobile Telecommunication)과 이를 기반으로 한 UMTS가 있다. cdma2000과 UMTS는 IMT-2000의 규격에 부합되는 프로토콜이다.
IMT-2000
1996년 5월 10월 TG8/1 회의에서 승인된 IMT-2000은 CDMA와 GSM으로 양분되어 국제로밍이 어려웠던 점을 극복하고자 전 세계적으로 동일한 주파수(2GHz) 대역을 사용해 로밍할 수 있게 하는 방식이다. 유럽방식인 비동기식 CDMA와 북미방식인 동기식 CDMA로 구분되며 비동기식 CDMA를 W-CDMA(Wideband CDMA), 동기식 CDMA를 MC-CDMA(Multi Carrier CDMA)라고 한다. 음성통화 중심의 2G (Generation)에서는 전 세계 80% 이상이 GSM을 사용하고 있으므로 고속 데이터 서비스를 포함해 부가 서비스가 강화된 3G는 대부분의 국가가 유럽방식인 W-CDMA를 채택하고 있다.
W-CDMA와 HSDPA
영상통화와 VOD, VoIP, UCC, 내비게이션에 이르기까지 무선 인터넷 기반의 새로운 서비스를 제공하는 IMT-2000은 CDMA와 GSM으로부터 진화했다. CDMA에서 출발한 동기식은 주파수 효율과 데이터 서비스 속도를 개선해 CDMA2000 1x, CDMA2000 1x EVDO를 거쳐 CDMA2000 1x EVDO Revision A로 진화하고 있으며, GSM을 기반으로 한 비동기식은 WCDMA, HSDPA를 거쳐 HSUPA로 진화하고 있다. HSDPA는 다운로드 속도에 비해 업로드 속도가 제한적이었으나 HSUPA에서는 업로드 속도까지 함께 개선되어 영상통화를 비롯한 본격적인 양방향 초고속 데이터 서비스 시대를 열고 있다.
플랫폼에 따른 구분
이어서 플랫폼을 이용한 구분을 살펴보면 다음과 같다.
휴대폰 플랫폼
휴대폰 플랫폼은 타깃에 따라 Value, Multi media, Enhanced, Convergence로 나눌 수 있는데 휴대폰 기능에 충실한 Value는 아프리카나 동남아, 중국과 같은 저가 시장을 목표로 하고, 최상위 플랫폼인 Convergence는 휴대폰용 프로세서의 비약적인 발전에 힘입어 PC 성능을 접목한 지능적인 휴대폰을 목표로 한다.
지금까지의 휴대폰 플랫폼은 디지털 카메라나 MP3 플레이어, 휴대용 TV, 내비게이션, 게임기 등의 융합에 필요한 멀티미디어 처리 능력의 향상에 주력해 왔다. 하지만 최근에는 PC에서 수행하던 업무들을 이동 중에도 수행할 수 있도록 휴먼 인터페이스를 포함한 대화면 터치스크린, HSDPA/HSUPA 고속 데이터 서비스, WiFi를 결합한 MID(Mobile Internet Device) 형태의 Convergence 플랫폼으로 발전하고 있다. 또 다른 측면에서는 RFID나 각종 센서와 결합해 모바일 RFID나 홈 네트워크, 헬스케어와 같은 특수 분야까지 진출하기도 한다.
<그림1> 프로토콜에 따른 구분
모바일 프로세서
기능성과 이동성이 강조된 휴대용 플랫폼에 대한 수요가 급증함에 따라 스마트폰을 중심으로 한 MID 제품군에 많은 관심이 집중되고 있는데, 기존 휴대폰에 채용된 ARM9 코어에 모뎀 제어와 2D 그래픽 처리용 DSP가 결합된 형태로는 한계에 부딪힐 수밖에 없다. 이를 해결하기 위해 최근 인텔은 1.8GHz 성능을 제공하는 CISC 기반의 초소형 기기용 프로세서인 Atom을 출시한 바 있고, ARM 역시 ARM11을 비롯해 1GHz의 성능과 밀리와트(mW) 급 전력 소모를 나타내는 Cortex-A8을 공급하고 있다. 특히 MID에서 자유로운 인터넷 환경을 구축하기 위해서는 고성능 프로세서와 함께 향상된 그래픽 구현 시스템이 제공되어야 하는데 ARM의 경우 Mali GPU를 통해 모바일 시스템 개발에 필요한 대부분의 기능을 제공하고 있다. Mali GPU는 CPU와 별도로 동작할 수 있는 3D 엔진 소프트웨어로 OpenGL ES와 OpenVG를 지원한다.
스마트폰
PDA에 이동전화 기능이 포함된 PDA폰과 달리 스마트폰은 이동전화에 인터넷 기능을 포함한 것으로 SMS뿐만 아니라 PIMS(Personal Information Management System), MMS(Multimedia Messaging Service), 이메일 전송, MPEG 및 MP3 플레이어 등이 구현됨에 따라 사용자들이 스마트폰에서도 PC에 버금가는 기능들을 체험할 수 있게 되었다. 최근 관심을 끌고 있는 스마트폰 운영체제로는 심비안, 블랙베리, OS X 포터블, 윈도우 모바일, 안드로이드 등을 꼽을 수 있으며 이중 심비안과 윈도우 모바일, 안드로이드가 리더 그룹을 형성하며 경쟁을 펼치고 있다.
휴대폰 개발 프로세스
휴대폰은 프로세서가 무선 프로토콜 처리 기능이 포함된 베이스밴드 프로세서(Baseband Processor)라는 점을 제외하면 모바일 게임기나 내비게이션과 마찬가지로 프로세서와 메모리, LCD, 사운드 칩, 키패드 등이 결합된 디지털 전자제품으로 볼 수 있다. 하지만 이동통신 장비의 특성상 기지국 장비와 RF 신호를 주고받으므로 전파 및 프로토콜과 관련된 까다로운 인증 절차가 따른다.
휴대폰 개발 프로세스는 일반적인 전자제품과 유사하다. 먼저 시장과 고객층을 분석하고 소프트웨어와 하드웨어의 사양 및 가격, 출시 시점을 결정하는 제품기획 단계를 수행한다. 이어서 하드웨어와 소프트웨어, 그리고 기구물 개발 단계를 거쳐 기능과 성능 및 인증 테스트 단계를 통과하면 최종적으로 양산에 들어간다.
제품 기획
제품 기획 단계에는 기획자 외에 하드웨어, 소프트웨어 개발자 및 디자이너가 참여하게 되는데 하드웨어 개발자는 원하는 기능을 구현하기 위해 적절한 부품을 찾아보고 사용 가능성, 납기, 가격 등을 확인한다. 소프트웨어 개발자는 구현하려는 기능에 대한 검토와 더불어 선정한 기능이 하드웨어에서 지원하는지를 하드웨어 개발자와 협의한다. 디자이너는 기구물 형태, 재질 등이 하드웨어 부품 배치와 전파 감도에 영향을 미치지 않는지를 하드웨어 개발자와 협의하고 실물 형상을 검토하기 위해 목업(Mock up)을 제작한다.
하드웨어 설계 및 제작
하드웨어의 설계 및 제작은 회로 설계, 보드 제작, 검토, 완료로 세분화된다. 회로 설계 단계에서는 RF 엔지니어와 베이스밴드 엔지니어로 분류된 하드웨어 개발자에 의해 베이스밴드 프로세서 제조사에서 제공하는 레퍼런스 회로도를 참조해 <화면 1>의 EDA 툴 같은 전문 설계 툴로 회로도를 작성한 후 보드 제작을 위해 PCB Artwork 담당자에게 전달한다. PCB Artwork 담당자는 PCB 제작을 위해 RF 성능을 고려해 부품의 배치와 연결선의 경로를 결정하는 Artwork 작업을 수행한다.
<화면1> PowerPCB를 이용한 PCB 설계
이후 PCB가 완성되면 부품과 함께 SMT(Surface Mount tech/nology) 업체에 전달해 PCB 위에 부품을 납땜함으로써 보드를 완성한다. SMT 작업은 PCB 위에 납 분말을 도포한 후 부품을 올려놓고 뜨거운 열풍으로 납땜하는 방법으로 이뤄진다. SMT 작업이 완료되면 육안검사와 더불어 전원을 인가해 쇼트나 단락의 문제가 없는지를 확인한다. 소프트웨어 개발자는 보드의 검증을 위해 부트로더를 이용해 부품을 테스트하는 코드를 작성함으로써 테스트를 수행한다.
소프트웨어 개발 및 테스트
소프트웨어 개발자는 베이스밴드 칩 제조업체에서 소스 코드 형태로 제공하는 레퍼런스 보드에 대한 소프트웨어 패키지를 이용해 필요한 기능을 구현한다. 베이스밴드 칩 제조사에서 권장하는 레퍼런스 보드와 유사한 방법으로 하드웨어를 설계한 경우에는 GPIO 및 메모리 매핑 부분과 LCD 드라이버만 수정하면 초기화면을 띄울 수 있다. 휴대폰 UI의 경우 BREW나 WIPI와 같은 표준 미들웨어를 이용하기도 하지만 대부분 독자적인 UI 시스템을 개발해 사용한다. WAP 브라우저나 게임과 같은 콘텐츠들은 써드파티 업체에서 라이선스를 받아 장착하는데 소스 코드가 제공되지 않으므로 상호 인터페이스를 잘 정의하고 긴밀한 협의를 거쳐 진행된다.
개발 단계에서는 테스트를 전담하는 인력에 의해 수많은 검증 과정을 거쳐 소프트웨어 안정성과 신뢰성을 검증한다. 가이드북에 따라 모든 항목을 반복 테스트한 후 결과를 개발자에게 전달해 오류나 버그가 수정될 수 있게 한다. RF 프로토콜과 Call에 관련된 소프트웨어 기능들은 공인 인증기관들에 의해 정해진 규격에 따라 인증절차를 거치게 된다. 휴대폰이 사용되는 지역의 통신사업자들도 자체적인 인증규격을 가지고 있으므로 최종적으로는 사업자의 인증과정을 다시 한 번 거쳐야 한다.
기구물 제작
케이스로 대표되는 기구물은 키패드, 버튼, 커버 등으로 구성된다. 기구물은 고객이 상품을 평가하는 첫 번째 요소이므로 제품의 상품성을 높이기 위해 최근에는 외부의 검증된 상품 디자이너를 참여시켜는 등 품질 향상에 노력하고 있다. ID(Industrial Design)는 손으로 그린 스케치를 2D 이미지로 변환하고 최종적으로는 3D로 렌더링한 디자인 프로파일을 얻어 최종 휴대폰 디자인을 결정하는 작업이다. ID 작업에 대한 실물 검토를 위해 목업 업체에 시안을 의뢰하기도 하는데 이 과정에서 ID 결과물을 플라스틱 등으로 제작해 디자인의 완성도를 실물로 확인할 수 있다.
기구 설계 작업이 끝나면 결과물을 가지고 대량 생산을 할 수 있도록 금형 설계를 진행하는데 금형 설계자는 품질을 고려해 금형을 설계한다.
제품 검증
휴대폰은 특성상 작은 하자에도 큰 문제를 발생시킬 수 있으므로 기능 테스트와 성능 테스트, 신뢰성 테스트, 인증으로 이어지는 매우 철저한 검증 작업이 요구된다. 이중 가장 먼저 수행하는 기능 테스트는 제품의 정상 동작 여부를 확인하는 과정으로 기능 테스트를 통과하면 제품의 성능을 확인하고 이를 극대화시키는 성능 테스트를 수행한다. 제품을 사용하는 소비자의 다양한 실제 환경을 고려한 신뢰성 테스트까지 마치면 공인 인증기관을 통해 CDG-1, CDG-2, CDG-3 인증을 의뢰한다. 여기서 CDG-1은 하드웨어 성능에 대한 인증이고 CDG-2는 통신 프로토콜과 같은 소프트웨어 성능에 대한 인증이며 CDG-3은 기지국 장비 연동 성능에 대한 인증이다.
양산
제품 검증까지 완료되면 개발 제품을 대량으로 생산하는 양산을 진행한다. 양산이 시작되기 전에 개발팀과 협력해 품질 관리에 걸림돌이 되는 부분을 수정하는데, 양산 전에 미처 발견하지 못한 문제점들에 대해서는 양산을 진행하면서 빠른 시간 안에 수정 또는 개선하는 작업을 병행해 진행한다. 대부분 소프트웨어와 관련된 수정이므로 제작사 홈페이지나 AS센터를 통해 무상 업그레이드를 진행한다.
휴대폰 개발 기술
휴대폰 개발 기술은 빠르게 진화하고 있으며 제품 라이프 사이클도 6개월 이내로 짧아지고 있다. 모든 제조업체들은 가장 성능이 좋으면서 가격 경쟁력이 있는 부품을 선택하고자 노력하고 있으며 초대형 부품 회사들이 모든 휴대폰 제조사를 상대로 부품을 공급하고 있으므로 경쟁사들의 최신 휴대폰 성능이나 사양은 크게 차이나지 않는 것이다.
베이스밴드 프로세서
휴대폰의 가장 핵심 부품인 베이스밴드 프로세서는 전 세계 시장의 95% 이상이 ARM 코어를 기반으로 DSP를 내장한 ASIC 형태의 제품이다. ARM 코어는 ARM9이 주로 사용되었으며, 최근에는 ARM11과 ARM9이 듀얼로 결합한 Conversions용 제품의 출시가 늘고 있다. 함께 포함된 DSP는 주로 2D나 3D 처리와 같은 그래픽 프로세서의 역할과 이동통신 프로토콜 처리를 담당하는 모뎀 역할을 수행한다. 내부 버스나 외부 버스 구조는 일반적인 ARM 아키텍처와 동일하다.
메모리
휴대폰에서는 공간 집적도를 높이기 위해 PSRAM(Pseude-SRAM), SDRAM(Synchronous-DRAM), NAND Flash를 결합해 하나의 패키지로 구성한 MCP(Multi-Chip Package) 메모리를 사용한다. 이중 PSRAM은 NAND Flash 기반의 부팅 과정에서 복사된 커널 이미지를 실행하는 용도로 사용되는데 DRAM에 충전회로를 하나의 패키지로 결합해 SRAM처럼 만들었다고 해서 PSRAM이라 부른다. NOR Flash보다 값이 저렴해 많이 채택되는 NAND Flash는 읽기 시간이 상대적으로 길다는 단점이 있으나 쓰기 효율은 좋은 편이므로 카메라 사진이나 동영상 데이터, MP3와 같은 데이터를 저장하는 용도로 많이 사용한다.
표준 DRAM에 비해 4배 이상 빠른 속도로 동작하는 SDRAM은 행 주소와 열 주소로 나눠 진 Matrix 형태의 주소 방식을 사용한다. 따라서 한 주소를 접근하려면 2회에 걸쳐 어드레싱해야 하므로 타이밍이 복잡한 회로가 되지만, 주소 개수를 절반 가까이 줄일 수 있어 칩의 핀 개수가 줄어드는 이점이 있다. 그 밖에 SDRAM은 Interleaved Cell 뱅크와 Burst 모드를 제공하는데 Interleaved Cell은 두 개로 분리된 셀 뱅크가 동시에 동작할 수 있다. 그러므로 연속되는 정보의 흐름이 뱅크 사이에서 변화하면서 진행될 수 있어 전체 메모리 사이클을 줄이고 전송 속도를 높일 수 있다. Burst 모드는 프로세서가 시작 주소만 지정하면 자동으로 연속적인 나머지 주소 공간을 접근할 수 있게 하는 기능이다. 따라서 메모리 접근 사이클에서 주소 지정에 따른 주소 로딩 시간을 단축시켜 대용량 데이터의 고속 전송이 이뤄질 수 있다.
LCD 모듈 인터페이스
LCD는 LDI(LCD Driver ID)를 통해 프로세서와 연결되는데 LDI는 프로세서가 보내는 데이터를 LCD 패널에 뿌려주는 기능을 수행한다. 프로세서는 LDI에 데이터와 명령을 보내는 방식으로 원하는 화면을 출력하는데, 일반적으로 프로세서와 LDI 사이에 데이터나 명령을 송수신하는 방법에는 CPU 인터페이스와 RGB 인터페이스가 적용된다.
먼저 CPU 인터페이스 방식에서는 LDI에 포함된 프레임 버퍼에 데이터를 전송하면 LDI가 그 안에 저장된 이미지 데이터를 주기적으로 LCD 패널에 뿌려 주므로 프로세서 입장에서는 LCD에 표시되는 그림을 변경하고 싶을 경우에만 LDI에 새로운 이미지 데이터를 보내 주면 된다. 따라서 LCD 화면을 관리하는 데 그다지 큰 부하가 발행하지 않는다. 그에 반해 RGB 인터페이스 방식은 프로세서가 프레임 버퍼를 만들어 두고 그 안에 저장된 이미지를 주기적으로 LDI로 전달해야 한다. 따라서 LCD 화면에 보이는 그림이 변하지 않는 상황에서도 동일한 데이터를 LDI로 주기적으로 전송해야 하는 탓에 부하가 많이 걸린다. 일반적으로 RGB 인터페이스 방식은 LCD 컨트롤러를 내장한 고속 프로세서에서 사용되므로 동영상 디스플레이와 같이 빠르게 변화하는 화면을 LCD로 출력하는 경우에 적합하다.
카메라
CIS(Camera Image Sensor), ISP(Image Signal Processor), 컨트롤러로 구성된 카메라 모듈은 이미 휴대폰의 필수 디바이스로 자리 잡았다. CIS는 렌즈로 입사된 이미지를 디지털 데이터로 변환하는 센서로 인간의 망막과 같은 역할을 한다. ISP는 CIS에서 변환한 디지털 데이터에 여러 가지 연산을 수행하는데 명도, 채도, 조도 등을 조작해 인간의 눈에 비치는 것과 가장 유사한 이미지를 만들어 낸다. 최근에는 카메라 컨트롤러에 ISP가 포함되기도 한다. 캡처, JPEG 압축과 변환, 확대, 축소, 오버레이 등 카메라 기능을 제어하고 LCD로 이미지를 출력하는 기능은 카메라 컨트롤러가 수행하는데 이 역시 베이스밴드 프로세서에 포함되기도 한다. 하지만 별도의 컨트롤러를 사용하는 것이 카메라의 성능을 더 높일 수 있으므로 외부에 별도로 장착하는 경우가 많다.
카메라 컨트롤러는 카메라 기능이 사용되지 않는 동안에는 프로세서에서 전송한 이미지가 그대로 LCD에 출력되도록 By-pass 모드로 설정되며, 카메라가 동작할 때에는 카메라 모듈로부터 입력된 데이터를 LCD로 출력하는 캡처, 프리뷰, OSD(On Screen Display) 모드 중 하나의 상태로 설정된다. 캡처 모드는 사진 촬영에 사용되는데 ISP로부터 전송 받은 데이터를 JPEG로 인코딩해 베이스밴드 프로세서로 보낸다. 프리뷰 모드는 카메라 렌즈에 잡힌 모습을 그대로 LCD로 보내며, OSD는 배경 그림을 놓고 촬영한 것과 같은 오버레이 기능이 필요할 때 사용한다.
오디오
휴대폰 환경에서 사운드 포맷은 음성 통화를 위한 음성 코덱인 보코더(Vocoder)와 MP3나 AAC(Advanced Audio Coding)와 같은 일반적인 사운드를 처리하는 오디오 코덱, 그리고 MIDI와 같이 음을 만들어 내는 합성음(synthetic audio) 코덱으로 구분된다. 음성 코덱으로는 QCELP(QualComm Code Excited Linear Predictive Coding)와 AMR(Adaptive Multi Rate)이 대표적이다. AAC는 MPEG-2의 오디오 레이어를 가리키는 96Kbps 정도의 가변 비트율을 지원하므로 고정 비트율인 MP3의 128Kbps보다 압축률이 높은 편이다. 전자악기의 연주 정보 등을 상호 전달하기 위해 정해진 하드웨어 및 통신 프로토콜의 국제적 표준규격인 MIDI는 악기 종류, 음색 수, 음색 번호, 음의 길이나 세기, 효과음 등의 여러 가지 요소를 디지털화함으로써 전자음악의 보급에 많은 기여를 했으며 전자악기, 컴퓨터, 휴대폰 등에서 광범위하게 사용되고 있다.
안테나
최근의 휴대폰 디자인은 더 얇고 가벼우며 작은 형태를 지향하고 있다. 기존의 Stubby 안테나와 같은 돌출형 구조는 휴대폰의 디자인에 많은 제약을 줄 수 있어 최근에 출시되는 대부분의 신형 휴대폰은 내장형 안테나(Intenna)를 장착해 출시되고 있다. 특히 휴대폰의 두께가 10mm 이하로 얇아지면서 안테나의 소형화는 더욱 중요한 이슈가 되었다.
최근에는 DMB, GPS, WLAN 등의 서로 다른 주파수 대역의 통신 서비스를 모두 지원하는 휴대폰까지 등장하고 있다. 따라서 하나의 휴대폰에 여러 개의 안테나가 장착되거나, 동시에 여러 주파수 대역을 다룰 수 있는 멀티밴드(Multiband) 안테나를 적용하고 있다.
윈도우 모바일 아키텍처
퀄컴은 MSM7xxx 베이스밴드 프로세서에 ARM11과 ARM9을 듀얼로 집적해 윈도우 모바일이나 임베디드 리눅스를 응용 운영체제로 추가 설치할 수 있게 지원한다. <그림 2>에서 보는 것처럼 기존의 휴대폰 소프트웨어들은 ARM9과 가상화 운영체제 상에서 레거시 운영체제 에뮬레이션을 통해 동작하고, 응용프로그램은 ARM11 기반의 응용 운영체제 상에서 동작하는 형태다.
윈도우 모바일의 경우는 디바이스 드라이버를 포함한 주요 휴대폰 서비스 부분은 퀄컴에서, 윈도우 커널을 포함한 응용 부분은 마이크로소프트에서 각각 제공하고 있다.
<그림2> MSM7xxx 윈도우 모바일 아키텍처
심비안
심비안은 마이크로소프트를 견제하기 위해 사이온, 모토롤라, 노키아, 파나소닉, 소니, 에릭슨, 삼성전자, 지멘스 등의 8개 업체가 공동으로 설립한 기업의 이름이자 운영체제의 이름으로 사이온이 현재 최대 주주이다. 객체지향 개념으로 설계된 심비안은 휴대폰 서비스에 최적화된 스마트폰 플랫폼으로 C++로 구현되어 있다. 마이크로 커널 아키텍처 기반으로 디바이스 드라이버 계층은 윈도우 모바일과 같이 물리 계층과 논리 계층으로 구분되어 있으며, 우수한 32비트 RTOS 능력을 갖춰 창을 여러 개 띄워 놓고 편집 작업을 할 수 있다. 특히 메모리나 저장장치 등의 리소스가 부족하거나 통신이 불안정한 환경에서도 신속하게 사용자 데이터로의 접근이 가능하도록 지원한다.
<그림3> 심비안 아키텍처
파일시스템을 배제한 GUI 개발
임베디드 리눅스나 윈도우 모바일과 같은 스마트폰 환경과 달리 일반적인 휴대폰 환경은 파일시스템을 사용하지 않고 소스 코드 상에 BMP나 WAV, 심지어 DSP 펌웨어까지 C 언어의 배열 형태로 변환해 사용하는 경우가 있다. 이러한 방법은 파일시스템에 대한 접근을 필요로 하지 않으므로 빠른 결과를 얻을 수 있고, 부팅시 파일시스템이 초기화되지 않은 상태에서도 효과음 처리나 LCD에 관련 이미지를 출력하는 등의 작업을 할 수 있다는 장점을 지닌다. 단 커널 사이즈가 커지는 것이 단점이다.
BMP 이미지 적용
일반적으로 스마트폰에서는 GUI를 만들 때 그림 이미지인 BMP 파일을 이용해 버튼이나 대화상자를 만든다. 하지만 BMP 파일을 C 언어의 배열 형태로 변환해 커널 이미지에 포함하면 파일시스템에 대한 접근 없이 빠르게 LCD로 해당 이미지를 출력할 수 있어 성능을 중요시하는 영역에서는 효과적이다. LCD가 5:6:5 포맷을 사용하는 16비트 컬러를 사용한다면 기존 BMP 이미지에서 그림을 나타내는 부분을 워드(16비트) 단위로 읽어 16진수로 변환하는 프로그램을 사용하면 된다. 이런 프로그램은 인터넷에서 무료로 구할 수 있으므로 직접 작성할 필요가 없다. <리스트 1>은 <화면 2>를 C 언어 배열로 변환한 결과이다.
<화면2> BMP 이미지
<리스트 1> BMP 이미지를 C 배열로 변환한 결과
unsight char logo[1024] =
{
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF,
0xFF , 0xFF , 0xC0 , 0x5D , 0x4B , 0x42 , 0xB0 , 0x88
//중략
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
};
WAV 적용
휴대폰 부팅 과정에 효과음을 넣기 위해 WAV 포맷을 많이 사용하며, WAV 파일도 C 언어 배열로 변환해 사용할 수 있다. 일반적으로 WAV 포맷의 경우 ARM 코어에서 코덱을 제공하므로 I2S(Inter IC Sound)를 사용해 WAV 데이터를 DAM Transfer로 전송하면 된다. WAV 파일 변환 역시 BMP 파일 변환과 동일하다.
폰트 적용
GUI에서 글자를 출력하려면 폰트 파일이 필요하다. 일반적으로 폰트 파일은 상업용 파일을 많이 사용하므로 제품 개수당 로열티가 있다. 또한 사용하는 언어에 따라 수 KB에서 수십 MB 정도의 용량을 가지므로 필요한 폰트만 선택할 필요가 있다. 보통 .ttf 확장자를 갖는 트루타입 폰트를 BMP 파일 변환과 유사한 방법으로 변환해 사용한다.
최근 지식경제부는 ‘지식·혁신 주도형 산업 강국으로의 전환’을 비전으로 모바일 산업을 초일류 주력산업으로 선정하고, 5대 주력 기간산업과 IT 융합을 위한 기술 개발을 통해 경쟁력을 지속적으로 확보하겠다는 전략을 발표했다. 이미 세계 시장과 산업은 급변하고 있으며 세계 모바일 시장에서 모바일 산업의 지속적인 경쟁력을 유지하고 우위를 확보하기 위해서는 모바일 분야 개발자들의 선택과 집중이 어느 때보다 절실한 시점이다.
GTK+ 프로그래밍 기초 자료 (0) | 2009.11.01 |
---|---|
GSM : Overview of the Global System for Mobile Communications (0) | 2009.10.04 |
프로그래밍 언어 포럼 순위 (랭킹) (1) | 2009.09.27 |
파이썬 로보틱스 프로그래밍 (0) | 2009.09.20 |
ePub 의 개요 [전자책 표준] (0) | 2009.09.03 |