1 字符和字符串
在 C 语言中字符用它们的字符集值对应的小整数表示。因此, 你不需要任何转换函数: 如有你有字符, 你就有它的值。
数字字符和它们对应的 0-9 的数字之间相互转换时, 加上或减去常数 ’0’, 也就是说, ’0’ 的字符值。
字符串用字符数组表示; 通常你操作的是字符数组的第一个字符的指针,C语言从来不会把数组作为一个整体操作 (赋值, 比较等)。
strcat(string, "!"); //正确
strcat(string, '!'); //错误, strcat() 用于连接字符串
if(string == "value") //错误
//上面代码段中的 == 操作符比较的是两个指针 —— 指针变量 string 的值和字符串常数 “value” 的指针值 —— 看它们是否相等,
//也就是说, 看它们是否指向同一个位置。它们可能并不相等, 所以比较决不会成功。
// 较两个字符串, 一般使用库函数 strcmp():
if(strcmp(string, "value") == 0)
{
/* string matches "value" */
}
char a[] = "Hello, world!";
char b[14];
b = "Hello, world!"; //错误,字符串是数组, 而你不能直接用数组赋值。
strcpy(b, "Hello, world!"); //正确,使用 strcpy() 代替
cout << sizeof("a") << endl; //2,"a" 是字符串
cout << sizeof('a') << endl; //1, 'a' 是字符
char r = 'abc';
cout << sizeof(r) << endl; //1, ???
cout << sizeof('abc') << endl; //4, ???
2 布尔表达式和变量
C 语言没有提供标准的布尔类型,可以使用以下任何一种形式。
#define TRUE 1
#define FALSE 0
#define YES 1
#define NO 0
enum bool {false, true}; //默认地,第一个枚举成员赋值为0,后面的每个枚举成员赋的值比前面的大1
enum bool {no, yes};
//或直接使用 1 和 0 , 只要在同一程序或项目中一致即可。
//如果你的调试器在查看变量的时候能够显示枚举常量的名字, 可能使用枚举更好。
//有些人更喜欢这样的定义
#define TRUE (1==1)
#define FALSE (!TRUE)
//或者定义这样的 “辅助” 宏
#define Istrue(e) ((e) != 0)
C 语言中的确任何非零值都都被看作真, 但这仅限于 “输入”, 也就是说, 仅限于需要布尔值的地方。内建操作符生成布尔值时, 可以保证为 1 或 0。
一般规则是只在向布尔变量赋值或函数参数中才使用 TRUE 和 FALSE (或类似的东西), 或者用于函数的返回值, 但决不用于比较
预处理宏 TRUE 和 FALSE (当然还有 NULL) 只是用于增加代码可读性, 而不是因为其值可能改变
尽管使用 TRUE 和 FALSE 这样的宏 (或者 YES 和 NO) 看上去更清楚, 布尔值和定义在 C 语言中的复杂性让很多程序员觉得 TRUE 和 FALSE 宏不过更令人迷惑, 因而更喜欢使用 1 和 0
3 头文件
(1)程序分成多个源文件, 把什么放到 .c 文件, 把什么放到 .h 文件
作为一般规则, 把如下东西放入头 (.h) 文件中:
· 宏定义 (预处理 #defines)
· 结构、联合和枚举声明
· typedef 声明
· 外部函数声明
· 全局变量声明
当声明或定义需要在多个文件中共享时, 尤其需要把它们放入头文件中。特别是, 永远不要把外部函数原型放到 .c 文件中。
另一方面, 如果定义或声明为一个 .c 文件私有, 则最好留在 .c 文件中。
(2)头文件定义技巧是:
#ifndef HFILENAME_USED
#define HFILENAME_USED
//... 头文件内容 ...
#endif
每一个头文件都使用了一个独一无二的宏名。这令头文件可自我识别,以便可以安全的多次包含; 而自动 Makefile 维护工具可以很容易的处理嵌套包含文件的依赖问题
(3 ) #include <> 语法通常用于标准或系统提供的头文件, 而 #include "" 通常用于程序自己的头文件。
用 <> 括起来的头文件会先在一个或多个标准位置搜索。用 "" 括起来的头文件会首先在 “当前目录” 中搜索, 然后 (如果没有找到) 再在标准位置搜索。
(4) 根据 “缺少的” 头文件的种类, 有几种情况。
如果缺少的头文件是标准头文件, 那么你的编译器有问题。你得向你的供货商或者精通你的编译器的人求助。
对于非标准的头文件问题更复杂一些。有些完全是系统或编译器相关的。某些是完全没有必要的, 而且应该用它们的标准等价物代替。例如, 用 <stdlib.h> 代替 <malloc.h>。其它的头文件, 如跟流行的附加库相关的, 可能有相当的可移植性。
标准头文件存在的部分原因就是提供适合你的编译器, 操作系统和处理器的定义。你不能从别人那里随便拷贝一份就指望它能工作, 除非别人跟你使用的是同样的环境.
(5 )参数个数可变的宏
用一个单独的用括弧括起来的的 “参数” 定义和调用宏, 参数在宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。
#define DEBUG(args) (printf("DEBUG: "), printf args)
if(n != 0) DEBUG(("n is %d\n", n));
//明显的缺陷是调用者必须记住使用一对额外的括弧。
#if 预处理指令只处理整数
#pragam 指令提供了一种单一的明确定义的 “救生舱”,
可以用作各种 (不可移植的) 实现相关的控制和扩展: 源码表控制、结构压缩、警告去除 (就像 lint 的老 /* NOTREACHED */ 注释), 等等
#pragma once 某些预处理器实现的扩展用于使头文件自我识别
4 输入输出
scanf("%d", i); //错误,传给 scanf() 的参数必须是指针。
scanf("%d", &i) // 正确
char s[30]; scanf("%s", s);
// 不用 & 也可以。需要指针; 并不表示一定需要 & 操作符。
// 当你向 scanf() 传入一个指针的时候, 你不需要使用 &, 因为不论是否带 & 操作符, 数组总是以指针形式传入函数
在 scanf() 转换数字的时候, 它遇到的任何非数字字符都会终止转换并被保留, 除非采用了其它的步骤, 那么未预料到的非数字输入会不断“阻塞” scanf():
scanf() 永远都不能越过错误的非数字字符而处理后边的合法数字字符。如果用户在数字格式的 scanf 如 %d 或 %f 中输入字符 ‘x’, 那么提示后并用同样的 scanf() 调用重试的代码会立即遇到同一个 ’x’。
用 scanf %d 读取一个数字, 然后再用 gets() 读取字符串, 但是编译器好像跳过了 gets() 调用?
scanf %d 不处理结尾的换行符。如果输入的数字后边紧接着一个换行符, 则换行符会被 gets() 处理。
作为一般规则, 你不能混用 scanf() 和 gets(), 或任何其它的输入例程的调用; scanf 对换行符的特殊处理几乎一定会带来问题。要么就用 scanf() 处理所有的输入, 要么干脆不用
在 printf 的格式串中输出一个 ’%’
只需要重复百分号: %%,而 \%不行, 因为反斜杠 \是编译器的转义字符, 而这里我们的问题最终是 printf 的转义字符
读取二进制数据文件的时候你应该用 “rb” 调用 fopen(), 确保不会发生文本文件的解释。类似的, 写二进制文件时, 使用 “wb”。
注意文本/二进制区别只是发生在文件打开时: 一旦文件打开之后, 在其上调用何种 I/O 函数无关紧要。
同时向两个地方输出, 如同时输出到屏幕和文件?
直接做不到这点。但是你可以写出你自己的 printf 变体, 把所有的内容都输出两次。下边有个简单的例子:
#include <stdio.h>
#include <stdarg.h>
void f2printf(FILE *fp1, FILE *fp2, char *fmt, ...)
{
va_list argp;
va_start(argp, fmt); vfprintf(fp1, fmt, argp); va_end(argp);
va_start(argp, fmt); vfprintf(fp2, fmt, argp); va_end(argp);
}
//这里的 f2printf() 就跟 fprintf() 一样, 除了它接受两个文件指针并同时输出到两个文件
5 产生标准分布或高斯分布的随机数?
#include <stdlib.h>
#include <math.h>
double gaussrand()
{
static double V1, V2, S;
static int phase = 0;
double X;
if(phase == 0)
{
do {
double U1 = (double)rand() / RAND_MAX;
double U2 = (double)rand() / RAND_MAX;
V1 = 2 * U1 - 1;
V2 = 2 * U2 - 1;
S = V1 * V1 + V2 * V2;
}
while(S >= 1 || S == 0);
X = V1 * sqrt(-2 * log(S) / S);
}
else
{
X = V2 * sqrt(-2 * log(S) / S);
}
phase = 1 - phase;
return X;
}