编程指南

这里有一些提示写好的代码,最初的上下文中收集ReactOS项目。

更重要的是正确的比快

过早的优化是万恶之源。

关于写作时间和内存高效的程序,我将提出一个不应该关心执行速度在构建代码。 这样的担忧将会是浪费时间。 构建健壮且容易维护的程序,如果它展示的性能是不能接受的,配置文件只和改善那些最需要优化的部分。 -RexJolliff

小和智能的解决方案解决问题不是一种耻辱

使 代码小和智能,同时保留其可读性和可理解性。 遵循准则“更少的代码——更少的错误——可读性更强,更容易审查”。 考虑您的解决方案。  别误会这条规则作为优化规则,也许它会影响最终的应用程序的速度和大小/驱动程序库,但这不是目的(主要)的原因。  如果聪明的解决方案需要更多的变化,试图尽可能地适应使它更聪明和更可读。 你的信用不是来自你写的行数。

编写线程安全的代码

特别注意这个如果你写一个图书馆,图书馆可以使用MT-application。 这无论如何适用于内核,内核是一个图书馆,必须是线程安全的本身。

线 程安全的代码区别从线程不安全代码是使用全局变量。 线程安全的代码,避免了几乎所有的全局变量。 这也包括局部静态变量和类变量。  人们必须谨慎决定是否一个变量或数据结构是本地,thread-global或process-global。 尽量减少全局变量。  如果这是不可能的,你需要一个thread-global变量然后线程本地存储(TLS)是正确的。  如果你需要一个过程的全局变量,那么正确的事情是一个典型的全局变量。 但是别忘了申报volatile修饰符。  如果你不声明它不稳定,这可能发生,两个线程的上下文变量的副本。 所以数据一致性可能无法保证。

全 局数据结构更加困难。 thread-global结构就像一个列表是没有问题,因为只有拥有线程知道它。  然而process-global结构被多个线程访问需要更多的照顾。 你必须同步线程的访问内核提供了所谓的同步对象。  这些帮助你的线程来保证共同独占访问常用的数据结构。 互斥锁通常用于此目的。 每个列表一个互斥对象主要是好的。  但如果这种结构很难被访问,一个应该考虑使用多个互斥。 每一个元素是一种资源浪费。 所以使用互斥对象数范围是一个很好的妥协。

这同样适用于内核模式。 只是难做,甚至一个使用自旋锁同步线程在多个处理器。

 volatile int really_global_i;  DWORD tlsi = TlsAlloc();  TlsSetValue( tlsi, 95 );

短:

  • 避免全局变量。

  • 使用TLS thread-global变量。

  • 找到合适的同步粒度对全球结构。

  • 编写multiprocessor-aware代码。

尽可能使用多线程和有用但明智的

多线程不是圣杯。 然而存在多个例子在世界太合适。 Win32-GUI应用程序通常有一个gui线程和一堆workingthreads。 也有可能有很多GUI-threads但这迅速变得复杂;如果你不采取预防措施是不允许触摸GUI从多个线程。

这不是简单的写MT-GUI-apps。 然而有些事情只是凭直觉threadable,所以试一试。

另 一件事是服务器应用程序。 它只是一个服务器应用程序是多线程的。 幸运的是这不是大问题服务器使用多个线程。  然而使用太多的线程并不好,,更好的策略是睡服务器线程池。 如果一个请求到达时,排队,派往其中一个线程。 Win32为此提供了所谓的I /  o完成端口。 使用此机制,所有这些调度和队列工作是一个小事。

使 用多个线程并行执行相同的代码使我们另一个问题。 如果这样的程序是运行在SMP或SMT能力系统,所谓cache-aliasing发生。  处理器缓存被组织在所谓的高速缓存线路,每一个都由32、64或更多个字节。 这些线是圆映射到内存地址。  结果是,地址映射到相同的高速缓存线路,重复8 MB左右。

在SMT系统这意味着两个线程使用相同的内存访问模式相互阻碍。 相同的内存访问模式如果开始使用相同的代码几乎在同一时间和记忆的地方是8 MB。

在SMT系统这意味着,如果一个线程读取一个字节,处理器加载整个高速缓存线路。 同样发生在其他虚拟处理器,但一些MB(映射到相同的高速缓存线路)。 下次又第一个处理器访问它的地址。 现在这两个处理器必须彼此缓存同步。 这一次又一次地发生。 结果就是这样一个程序运行速度在一个单处理器系统:- - - - - -(这对SMT-systems变得更糟,因为两个虚拟处理器共享相同的缓存。 这意味着还SMT和Cache-aliasing。

试着打开的文件共享(esp SHAREMODE_DELETE。)

让程序支持多个实例

避免数据损坏事故

这不是一个好的事情如果您的应用程序(或系统)崩溃,和用户留下损坏文件。

系统,bugcheck功能确保系统停止,希望之前的损失。 使用健康检查,以确保你发现腐败。 对于通用的应用程序,很高兴让备份文件你有回到的问题。

编写先发制人的代码

使用自旋锁很少,但在需要的地方

提升IRQL尽可能短,认为写作的一个DPC

记得Unicode / Unicode

不 要让你的程序只使用UNICODE或ANSI。 包括< tchar。 h >和使用TCHAR  char,而是宏观_T()为文本字符串,_tcscpy()、_tcscmp(),…或lstrcpy(),lstrcmp(),…宏操纵字符串。  计算长度的TCHARs TCHAR数组,使用sizeof(数组)/ sizeof(阵列[0])或sizeof(数组)/  sizeof(TCHAR)。

不要硬编码的英语短语源代码——如果你必须收集在一个地方(容易本地化)

1,一个方法是首先写硬编码短语像我们通常使用:

  • 这应该是第一步在处理不好写代码。

  • 现在让我们忘记unicode / nounicode

    printf("my hardcoded string");

2,然后让他们这样的宏:

    #define HARDSTRING "my hardcoded string"     printf(HARDSTRING);

3 -最后:

    char* AllStrings[NUMSTRINGS];     #define HARDSTRING AllStrings[1]       printf(HARDSTRING); // <--- not touched anymore

你可以直接跳到第二步在开发节省自己的时间,不需要在你的代码中搜索字符串。

不要为每个连接用户创建一个线程,使用I / O完成端口

有 一个线程来服务所有用户是一个坏主意。 它迫使用户等待一个未定义的时间而不是慢。 一个解决方案是为每个用户创建一个线程。 然而这是一个坏主意。  没关系如果你提供一个可测最大的查询。 但如果你不能确定有多少查询到达,你最好使用舒适的I / O完成端口。  创建一个与另一个服务器的线程:主要是你的线程I / O操作(高清访问)。 调用线程不会伤害太多操作系统但是你的I / O子系统。  得到类似的抖动。 单线程的I / o阻碍对方完成和整个事情变得缓慢和能力融化。

所以限制一个服务器线程的数量是最好的主意。 可以用手或用程序这样的行为I / O完成端口。 例子:

I  / O完成端口是一个伟大的多线程模型是快速和高效。 这么说吧。 你创建的线程,等待,直到他们被要求做一些工作。 他们会吃0  CPU时间等待,只会在需要的时候醒来。 另一方面它的多线程代码时,不会造成延误一些操作块然后你可以提供所有这些线程只使用一个线程的CPU。  这意味着它不会低于只有一个线程,更为您的代码可能表现在SMP系统更快。  他们当然比单线程代码更难使用或为每个操作创建线程池的线程之后时不时的检查工作,但实际上复杂性的增加有回报的。

认为改变写作方向

从左到右阅读顺序并不是唯一方向读和写文本上。 存在也从右到左的阅读顺序以希伯来语和从上到下的阅读顺序是在日本。 而一个法律在日本也从左到右阅读顺序。 所以这两个订单你可以限制自己。 这意味着<帮我>

创建一个GUI,layoutmanager而不是像素定位控制

如果你使用像素定位控制,GUI将不再看如果由于本地化文本变化,如果用户选择字体你没有预料到的(如大字体的视障)如果写作方向变化。 也可能你的对话框不会可调整大小的。

编写时间和内存效率的程序

这种冲突与前面的语句优化。 记住这句话并不意味着你应该浪费内存或周期。

内 存大小总是更大的处理器得到更快。 但项目总是效率较低。 好了,够了。 这似乎只是一个建议,因为每个人都有决定和设计的应用程序的。  至少这些提示:请记住,您可以使用内存映射文件。 这让你更容易访问你的文件数据,你不需要重建整个世界数据在内存中。  这是一件事你应该避免:像一些坏游戏,高清,如果开始占领一个演出,在swapspace相同。 如果你面向对象的工作,使用参考对象参数。  这就避免了一个拷贝构造函数被称为两次。 一次时间对象,一个更高层次的变量。 避免冗余数据通过复制一个对象的层次结构。  这意味着,不要一组变量传递给下一个更深层次的对象等等,但是使用一个指针的智能设计。

避免内存泄漏

说 起来容易做起来难。 内存泄漏总是一个进攻。 没有什么人能真的从来没有内存泄漏。 然而把这些提示。 您可以使用一个语言垃圾收集器,像埃菲尔铁塔。  在C语言中,你唯一的选择是更加小心,总是写对malloc和free或使用引用计数。 如果你使用c++存在汽车的技术和智能指针的指针。  如何使用它们,请参阅相应的文献。


投入大量的注释代码

  • 认为的注释块解释整个模块和衣服。

抽象和简单之间找到合适的平衡

抽象是一种有用的编程概念。 很容易走火入魔。就

不要把多余的程序数据的副本

如果你复制一段代码需要维护代码两次。 通常工作人员在您的代码只会工作的一个副本,所以你必须确保他们保持同步。

  • 未完待续.......

如果使用汇编,总是用C实现一个替代方法或使用

ReactOS 或将是一个多平台的操作系统。 有时间好快汇编作品关键操作是好的。 然而,必须有另一个用C编写的,永远。 只有这个保证使ROS编译每个目标平台。  最后一个提示:第一个目标总是让一段代码运行。 如果我们发现这一块是一个瓶颈,我们将优化它甚至用汇编程序。

保持文件的元信息

ReactOS 文件系统、文件可能有许多额外的信息。 这包括时间戳、属性访问控制列表,多个文件流和扩展属性。 不是每个文件系统提供了每一个功能。  但如果这样做,是讨厌的用户使用这些信息,如果一个程序破坏元信息。 最多发生在复制或包装文件或保存一个文件时。  这些项目是创建一个新文件和存储从旧到新的内容。 后来他们删除旧的和重命名。 通常的最佳方法是内存映射原并使用它的映射数据为r / o直接访问。  如果需要更改,因为用户使用程序,一个自我即写即拷机制,使一个工作副本。 如果用户想要拯救他的变化,改变了结构通过memorymapping写回。

如果这不是一个选择你,记住要使用的参数hTemplateFile它仅仅调用。

不要使用绝对路径,不意味着路径名

使用其中一个程序只能安装在C开车吗? 明显但很讨厌,不是吗?

如 你所知,驱动器字母(痛苦)因系统而异。 所以在路径中包括驱动器字母是坏的。 而使用相对路径或本地路径。 路径也没有公理在windows系统。  „程序文件”目录例如改变它的拼写从定位到本地化。  在Win32函数GetWindowsDirectory确实存 在,GetSystemDirectory,SHGetPathFromIDList,SHGetSpecialFolderPath  SHGetFolderPath。 使用这些函数可以解决单一设施之间的差异和本地化。

你也可以使用环境变量。

避免直接分配分配的内存的指针

避免这样的事情:

char *ptr; ptr = malloc(size);

像malloc函数可以随时返回NULL,不幸的是它会在你最意想不到的时候发生。 记住,在内核模式有malloc等价物的行为,这意味着BSOD一样。

一个建议是调用一个函数,总是检查null调用malloc像函数之前,抛出一个异常回来,这样你就不会忘记。 当然你要记住现在写异常处理程序,但这样你涵盖大量和节省一些代码和编译器把少很多条件跳转。 这意味着你写更少的代码,和更多的机会是正确的也快!

当心与C的语法

C语法不需要的一件事是可能编写代码是这样的:

 if (a = 5)  {  ...  }

这意味着什么! 用于C程序员非常简单,但对初学者很困惑。

但是最严重的副作用是可能你想简单测试如果是等于5,是一种常见的错误不要写你真正想要的方式。

 if (a == 5)  {  ...  }

匈牙利符号前缀

大型项目有很多人工作应该有共同点。 Hunagrian符号前缀命名约定提供的一致性和自我文档。

类型 前缀 例子
int n nCount
浮动 f fBaby
d dRoot
字符 c cKey
字符串(零终止) 深圳 szInput[]
全球 g_ g_nScore
指针 (总是第一页) * pnArray
结构体 年代 SCast
l lHours
无符号 u uAge




Programming Guidelines

Here are some hints to write good code, originally collected in the context of the ReactOS project.

For advice on writing secure code please see Secure Programming.

It's more important to be correct than to be fast

Premature optimization is the root of all evil.

With respect to writing time and memory efficient programs, I  would put forward that one should not concern themselves with execution  speed while building code.  Such concerns will be mostly a waste of  time.  Build the program to be robust and easily maintainable, then if  it exhibits performance that is not acceptable, profile it and improve  only those sections that most need to be optimized. -RexJolliff

Small and smart solutions to solve a problem aren't a shame

Make your code small and smart while preserving its readability and  understandability. Follow the guide line "less code – less bugs – more  readable – easier to review". Think twice about your solution. Don't  misunderstand this rule as optimization rule; maybe it affects the speed  and size of the resulting application/driver/library, but that is not  the (primary) intended reason. If the smart solution needs more changes,  try to adapt as much as possible to make it smarter and more readable.  Your credit is not derived from the count of lines you wrote.

Write thread safe code

Especially be aware of this if you write a library; your library may  be used by an MT-application. This applies anyway to the kernel, since  the kernel is a library which has to be thread-safe per se.

What differentiates thread safe code from thread unsafe code is  the use of global variables. Thread safe code avoids nearly all global  variables. This includes also local static variables and class  variables. One has to decide carefully whether a variable or data  structure is local, thread-global or process-global. Try to eliminate  global variables. If this isn't possible and you need a thread-global  variable then thread local storage (TLS) is the right thing. If you need  a process global variable, then the right thing is a classic global  variable. However don't forget to declare it with the volatile modifier.  If you don't declare it as volatile, it may happen that two threads  hold a copy of the variable in their contexts. So data consistency may  not be guaranteed.

Global data structures are little more difficult. A thread-global  structure like a list is no problem since only the owning thread knows  about it. However a process-global structure which gets accessed by  multiple threads needs more care. You have to synchronize the accesses  of the threads with so called synchronization objects the kernel  provides. These help your threads to guarantee a mutual exclusive access  to common used data structures. The Mutex is commonly used for this  purpose. One Mutex per list is mostly okay. However if such a structure  gets accessed very hard, one should consider to use more than one Mutex.  One per element is a waste of resources. So using Mutexes for several  ranges would be a good compromise.

The same applies to kernel-mode. It's just harder to do and one  uses spinlocks to even synchronize threads over multiple processors.

volatile int really_global_i;  DWORD tlsi = TlsAlloc();  TlsSetValue( tlsi, 95 );

Short:

Avoid global variables.

Use TLS for thread-global variables.

Find the right synchronization granularity for global structures.

Write multiprocessor-aware code.

Use multithreading where possible and useful but judicious

Multithreading is not the holy grail. However there exist multiple  examples out in the world where MT would have been appropriate.  Win32-GUI apps usually have one GUI-thread and a bunch of  workingthreads. It's also possible to have many GUI-threads but this  gets complex very fast; if you don't take precautions it is not allowed  to touch the GUI from more than one thread.

It's not that simple to write MT-GUI-apps. However some things are just intuitively threadable, so have a try.

Another thing are server applications. It's just a have to for  server apps to be multi-threaded. Fortunately it is no big problem to  make a server use multiple threads. However using too many threads is  not good, either, a better strategy is to have a pool of sleeping server  threads. If a request arrives, it is queued and dispatched to one of  the threads. Win32 provides for this purpose the so called  I/O-Completion ports. Using this mechanism, all this dispatch and queue  work is a minor matter for you.

Using multiple threads which execute the same code in parallel  leads us to another problem. If such a program is run on an SMP or an  SMT capable system, a so called cache-aliasing happens. Processor caches  are organized in so called cache lines, each of which consists of 32,  64 or more bytes. These lines are circular mapped to memory addresses.  Result is that, addresses that map to the same cache line, repeat all  8 MB or so.

On SMT systems this means that two threads with the same memory  access pattern hinder each other. Same memory access pattern happens if  the same code was started nearly at the same time and the memory places  are 8 MB off.

On SMT systems this means that if a thread reads a byte, the  processor loads the whole cache line. The same happens on the other  virtual processor but some MB off (which maps to the same cache-line).  Next time the first processor accesses its address again. Now these two processors have to sync their caches with each other. This  happens again and again. Result is that such a program runs faster on  an uniprocessor system :-(  This gets even worse on SMT-systems, because  the two virtual processors share the same cache. This means also SMT and Cache-aliasing.

Try to open files shared (esp. SHAREMODE_DELETE)

Make programs support multiple instances

Avoid data corruption on crashes

It's not a nice thing if your application (or the system) crashes, and the user is left with corrupted files.

For the system, the bugcheck functions make sure the system  stops, hopefully before damage is done. Use sanity checks to make sure  you catch corruptions. For generic applications, it's nice to keep  backup files so you have something to go back to in case of problems.

Write preemptive code

Use spinlocks rarely but where needed

Raise IRQL as short as possible – think of writing an DPC

Remember Unicode/non-Unicode

Do not make your programs use only UNICODE or only ANSI. Include  <tchar.h> and use TCHAR instead of char, macro _T() for text  strings, _tcscpy(), _tcscmp(), … or lstrcpy(), lstrcmp(), … macros for  manipulating strings. To calculate length in TCHARs of the TCHAR array,  use sizeof( array ) / sizeof( array[0] ) or sizeof( array ) / sizeof(  TCHAR ).

Don't hardcode English phrases into source code – if you must, collect them in one place (easier to localize)

1 – An approach is to first write hardcoded phrases like we all usually use to do:

this should be the first step when dealing with bad written code.

let's forget unicode/nounicode now

   printf("my hardcoded string");

2 – Then make them macros like this:

   #define HARDSTRING "my hardcoded string"     printf(HARDSTRING);

3 – and finally:

   char* AllStrings[NUMSTRINGS];     #define HARDSTRING AllStrings[1]       printf(HARDSTRING); // <--- not touched anymore

You could skip directly to step 2 when developing to save yourself  some time by not having to search for strings in your code later.

Don't create a thread per connected user, use I/O Completion Ports instead

Having one thread to serve all users is a bad idea. It forces users  to wait an undefined time rather than being served slower. A solution is  to create one thread for every user. However this is a bad idea, too.  It's OK if you serve a determinable maximum of queries. But if you can't  determine how many queries will arrive, you better use the comfortable  I/O Completion Ports. The thing with creating one and another server  thread is: Mostly your thread does I/O operations (HD access). Invoking  too many threads doesn't hurt the OS but your I/O subsystem. You get  something like thrashing. The single thread's I/Os hinder each other to  be finished and the whole thing gets slower and capacity melts.

So restricting to a number of server threads is the best idea.  One could program such a behavior by hand or use the I/O completion  ports. Example:

I/O completion ports are a great multithreaded model made to be  fast and efficient. Put it this way. You create the threads once, that  get wait there until they are required to do some job. They will eat 0  CPU time when waiting and will only wake up when needed. On the other  side it gets the benefit of multithreaded code that won't cause delays  when some operation blocks and then you could serve all those threads  using only one thread for CPU. That means it won't be slower than having  only one thread, even more your code probably behaves faster in SMP  systems. They are of course harder to use than single threaded code or  to create threads for each action or to have a pool of threads that wake  from time to time checking for a job but actually the increase in  complexity pays off.

Think of changing writing directions

Left to right reading order is not the only direction to read and  write text on the globe. There exist also right to left reading order as  in Hebrew and top to down reading order as in Japanese. Whereas one  legal reading order in Japanese is also left to right. So you can  restrict yourself to these two orders. This means that <help me>

Create a GUI with layoutmanager rather than pixel positioning of controls

If you use pixel positioning of controls, your GUI will no longer  look right if the texts change due to localization, if the user selects a  font you didn't expect (like large fonts for the visually impaired) and  if the writing direction changes. Also probably your dialogs will not  be resizable.

Write time and memory efficient programs

This conflicts with the earlier statement about optimizations. Keep  in mind that that statement does not mean you should waste memory or  cycles though.

Memory sizes get always bigger and processors get always faster.  But programs get always less efficient. OK, enough. This seems to be  just an advice, because everyone has to decide and design ones  application by ones own. At least these hints: Keep in mind that you can  use memory mapped files. This gives you easier access to your file data  and you do not have to rebuild the whole data world in memory. This is a  thing you should always avoid: like some bad games, occupy one gig on  HD and if started, the same amount in swapspace. If you work object  oriented, use references for object parameters. This avoids a copy  constructor to be called twice. One time for the temporal object, one  time for the higher level variable. Avoid copying of redundant data  through an object hierarchy. This means, do not pass a set of variables  to the next deeper object and so on but use an intelligent design with  pointers.

Avoid memory leaks

Easier said than done. Memory leaks are always an offense.  There's  nothing one can really do to never have a memory leak. However take  these hints. You can use a language which has a garbage collector, like  Eiffel.  In C, your only option is to be more careful and always write  pairs of malloc and free or use reference counting. If you use C++ there  exist the techniques of auto pointers and smart pointers. For how to  use them, see the corresponding literature.

-- There are garbage collectors for C and C++ – Jakov

Put plenty of comments into your code

Think of block comments which explain a whole module and the play tog.

Find the right balance between abstraction and straight forward

Abstraction is a useful programming concept. It's easy to overdo it though.

Don't make redundant copies of program data

If you copy a piece of code you will have to maintain the code twice.  Typically people working on your code will only work on one of the  copies, so you have to make sure they keep in sync.

to be continued.......

If using assembler, always implement an alternative way in C or what you use

ReactOS is or will be a multi platform OS. Having nice fast assembler  pieces for time critical operations is good. However, there has to be  an alternative written in C, always. Only this guarantee enables ROS to  compile on every of it's target platforms. At last one hint: The first  goal is always to make a piece of code running. If we find this piece to  be a bottleneck, we'll optimize it and possibly write it in assembler.

Preserve the meta information of files you use

On ReactOS file systems, files may have lots of additional  information. This includes timestamps, attributes, access control lists,  multiple filestreams and extended attributes. Not every file system  provides every feature. But if it does, it is annoying for a user who  works with these pieces of information, if a program destroys the meta  information. At most this happens when copying or packing files or when  saving a file. These programs just create a new file and store the  content from the old one to the new. Afterwards they delete the old and  rename the new one. Generally the optimal way would be to memory map the  original and use its mapped data to directly for r/o access. If changes  are required, because the user works with the program, a self made  copy-on-write mechanism goes on and makes a working copy. If the user  wants to save his changes, the changed structures are written back  through the memorymapping.

If this is not an alternative for you, remember to use the parameter hTemplateFile of the CreateFile call.

Do not use absolute paths; don't imply path names

Ever used one of these programs that can only be installed on the C drive? Obvious but quite annoying, isn't it?

As you know, drive letters (what a pain) vary from system to  system. So including drive letters in paths is bad. Rather use relative  paths or local paths. Also paths are no axioms on windows systems. The  „Program files“-directory for example changes it's spelling from  localization to localization. On Win32 there do exist the functions  GetWindowsDirectory, GetSystemDirectory, SHGetPathFromIDList,  SHGetSpecialFolderPath and SHGetFolderPath. With these functions you can  resolve the differences between the single installations and  localizations.

You can use environment variables too.

Avoid directly assigning allocated memory to a pointer

Avoid things like this:

char *ptr; ptr = malloc(size);

functions like malloc can return NULL anytime and unfortunately it  will happen when you least expect it. Remember that in kernel mode there  are malloc equivalents that behave the same way and that means a BSOD.

One suggestion is to call a function that always check for null  before calling malloc like functions, that throws an exception back so  you don't forget about the check. Of course you have to remember now to  write the exception handler but this way you cover large blocks and save  yourself some code and make the compiler throw a lot less conditional  jumps. That means you write less code, with more chances to be correct  and also faster!

Beware with the C syntax

One thing C syntax never needed is the possibility to write code like this:

if (a = 5)  {  ...  }

What does that means! To a programmer used to C that is very simple but for beginners is really confusing.

But the worst side effect is that probably you wanted to simply  test if a was equal to 5 and is a common error to not write it the way  you really wanted.

if (a == 5)  {  ...  }

Hungarian Notation Prefixes

Main article: Hungarian Notation

Large projects with many folks working on them should have common  ground. Hunagrian Notation Prefixes naming convention provides  marvelous consistency and self documentation.

Type

Prefix

Example

int    n    nCount    

float    f    fBaby    

double    d    dRoot    

char    c    cKey    

string (null terminated)    sz    szInput[]    

global    g_    g_nScore    

pointer    p (always first)    *pnArray    

struct    S    SCast    

long    l    lHours    

unsigned    u    uAge