C程序通常将一个字符串转换为一个以空字符尾的字符数。假有两个这样的字符串st,并且我想要将它们连一个独的字符串r。我通常使用函数strcpy()strcat()来完成。下面这种的方法并不会工作:
char *r;
strcpy(r, s);
strcat(r, t);
是因r没有被初始化指向任何地方。尽管r可能潜在地表示某一内存,但并不存在,直到你分配它。
    试试r分配一些内存:
char r[100];
strcpy(r, s);
strcat(r, t);
只有在st所指向的字符串不很大的候才能工作。不幸的是,C要求我们为指定的大小是一个常数,因此无法确定r是否足大。然而,很多C实现带有一个叫做malloc()函数,它接受一个数字并分配这么多的内存。通常有一个函数称strlen(),可以告一个字符串中有多少个字符:因此,我可以写:
char *r, *malloc();
r = malloc(strlen(s) + strlen(t));
strcpy(r, s);
strcat(r, t);
    然而个例子会因两个原因而失。首先,malloc()可能会耗尽内存,而个事件静静地返回一个空指来表示。
    其次,更重要的是,malloc()并没有分配足的内存。一个字符串是以一个空字符束的。而strlen()函数返回其字符串参数中所包含字符的数量,但不包括尾的空字符。因此,如果strlen(s)ns需要n + 1个字符来盛放它。因此我需要r分配外的一个字符。再加上检查malloc()是否成功,我得到:
char *r, *malloc();
r = malloc(strlen(s) + strlen(t) + 1);
if(!r) {
    complain();
    exit(1);
}
strcpy(r, s);
strcat(r, t);
 法(Synecdoche, sin-ECK-duh-key)是一文学手法,有点似于明或暗,在牛津英文典中解如下:“a more comprehensive term is used for a less comprehensive or vice versa; as whole for part or part for whole, genus for species or species for genus, etc.(将全面的位用作不全面的位,或反之;如整体局部或局部整体、一般特殊或特殊一般,等等。)
住的是,制一个指并不能制它所指向的西
将一个整数转换为一个指果是实现的(implementation-dependent),除了一个例外。个例外是常数0,它可以保转换为一个与其它任何有效指都不相等的指通常这样
#define NULL 0
但其效果是相同的。要住的一个重要的事情是,当用0针时它决不能被解除引用话说,当你将0赋给一个指针变量后,你就不能访问它所指向的内存。不能这样写:
if(p == (char *)0) ...
也不能这样写:
if(strcmp(p, (char *)0) == 0) ...
strcmp()是通其参数来看内存地址的。
如果p是一个空指这样写也是无效的:
printf(p);
printf("%s", p);
C于整数操作的上溢或下溢定得非常明确。
    只要有一个操作数是无符号的,果就是无符号的,并且以2n模,其中n。如果两个操作数都是符号的,则结果是未定的。
例如,假ab是两个非整型量,你希望测试a + b是否溢出。一个明法是这样的:
if(a + b < 0)
    complain();
通常,是不会工作的。
    一旦a + b生了溢出,果的任何注都是没有意的。例如,在某些机器上,一个加法运算会将一个内部寄存器:正、、零或溢出。这样的机器上,编译器有将上面的例子实现为首先将ab加在一起,然后检查内部寄存器状是否为负。如果运算溢出,内部寄存器将于溢出状测试会失
    使个特殊的测试成功的一个正确的方法是依于无符号算的良好定,即要在有符号和无符号之间进转换
if((int)((unsigned)a + (unsigned)b) < 0)
    complain();
两个原因会令使用移位运算符的人感到烦恼
1.    在右移运算中,空出的位是用0填充是用符号位填充?
2.    移位的数量允使用哪些数?
第一个问题的答案很简单,但有实现的。如果要行移位的操作数是无符号的,会移入0。如果操作数是符号的,则实现决定是移入0是移入符号位。如果在一个右移操作中你很心空位,那unsigned来声明量。这样你就有空位被0
    第二个问题的答案同样简单:如果待移位的数n移位的数量必大于等于0并且格地小于n。因此,在一次独的操作中不可能将所有的位从量中移出
例如,如果一个int32位,且n是一个int,写n << 31n << 0是合法的,但n << 32n << -1是不合法的。
    注意,即使实现将符号移入空位,一个符号整数的右移运算和除以2的某次也不是等价的。一点,考(-1) >> 1是不可能0的。[注:(-1) / 2果是0]
下面的程序:
#include

main() {
    char c;
//int c


    while((c = getchar()) !=
EOF)
        putchar(c);
}
    段程序看起来好像要将制到出。实际上,它并不完全会做些。
    原因是c被声明字符而不是整数。意味着它将不能接收可能出的所有字符包括EOF
因此里有两可能性。有一些合法的入字符会cEOF相同的,有又会使c无法存放EOF。在前一情况下,程序会在文件的中停止制。在后一情况下,程序会陷入一个无限循
    实际上,存在着第三可能:程序会偶然地正确工作。C言参考手册格地定了表达式
((c = getchar()) != EOF)
果。其6.1中声明:
当一个较长的整数被转换为一个短的整数或一个char,它会被截去左;超出的位被简单弃。
7.14声明:
存在着很多赋值运算符,它都是从右至左合的。它都需要一个左的操作数,而赋值表达式的型就是其左的操作数的型。其就是已经赋过值的左操作数的
两个条款的合效果就是必过丢getchar()果的高位,将其截短字符,之后个被截短的再与EOF行比。作为这个比的一部分,c一个整数,或者采取将左的位用0填充,或者适当地采取符号展。
然而,一些编译器并没有正确地实现这个表达式。它getchar()的低几位赋给c。但在cEOF的比中,它却使用了getchar()这样做的编译器会使个事例程序看起来能正确地工作。
立即安排出的示通常比将其暂时保存在一大一起出要昂得多。因此,C实现通常允程序控制生多少出后在实际地写出它
    个控制通常一个称setbuf()函数。如果buf是一个具有适当大小的字符数
setbuf(stdout, buf);
将告I/O写入到stdout中的出要以buf一个冲,并且等到buf了或程序直接fflush()实际写出。冲区的合适的大小在中定义为BUFSIZ
因此,下面的程序解了通使用setbuf()讲标制到出:
#include

main() {
    int c;

    char buf[BUFSIZ];
    setbuf(stdout, buf);

    while((c = getchar()) !=
EOF)
        putchar(c);
}
    不幸的是,个程序是错误的,因一个微的原因。
    要知道毛病出在哪,我需要知道冲区最后一次刷新是在什么时候。答案;主程序完成之后,将控制交回到操作系之前所行的清理的一部分。在刻,冲区已放了
    有两方法可以避免问题
    首先,使用静态缓冲区,或者将其式地声明
static char buf[BUFSIZ];
或者将整个声明移到主函数之外。
    另一可能的方法是动态地分配冲区并且从不放它:
char *malloc();
setbuf(stdout, malloc(BUFSIZ));
注意在后一情况中,不必检查malloc()的返回,因如果它失了,会返回一个空指。而setbuf()可以接受一个空指其第二个参数,将使得stdout成非冲的。会运行得很慢,但它是可以运行的。
由于宏可以象函数那,有些程序就会将它们视为等价的。因此,看下面的定
#define max(a, b) ((a) > (b) ? (a) : (b))
注意宏体中所有的括号。它了防止出ab有比>低的表达式的情况。
    一个重要的问题是,像max()这样个操作数都会出两次并且会被求两次。因此,在个例子中,如果ab大,a就会被求两次:一次是在比候,而另一次是在max()候。
    是低效的,错误
biggest = x[0];
i = 1;
while(i < n)
    biggest = max(biggest, x[i++]);
max()是一个真正的函数会正常地工作,但当max()是一个宏的候会失。譬如,假x[0]2x[1]3x[2]1。我来看看在第一次循环时生什赋值语句会被
biggest = ((biggest) > (x[i++]) ? (biggest) : (x[i++]));
首先,biggestx[i++]行比。由于i1x[1]3系是。其副作用是,i2
    由于系是x[i++]赋给biggest。然而,这时i2了,因此赋给biggestx[2],即1
避免问题的方法是保max()宏的参数没有副作用:
biggest = x[0];
for(i = 1; i < n; i++)
    biggest = max(biggest, x[i]);
有一个危的例子是混合宏及其副作用是来自UNIX第八版的中putc()宏的定
#define putc(x, p) (--(p)->_cnt >= 0 ? (*(p)->_ptr++ = (x)) : _flsbuf(x, p))
putc()的第一个参数是一个要写入到文件中的字符,第二个参数是一个指向一个表示文件的内部数据构的指。注意第一个参数完全可以使用如*z++西,尽管它在宏中两次出,但只会被求一次。而第二个参数会被求两次(在宏体中,x了两次,但由于它的两次出在一个:的两,因此在putc()的一个例中它之中有且有一个被求)。由于putc()中的文件参数可能有副作用,会出现问题。不,用手册文档中提到:由于putc()实现为宏,其stream可能会具有副作用。特putc(c, *f++)不能正确地工作。但是putc(*c++, f)实现中是可以工作的。
有些C实现很不小心。例如,没有人能正确putc(*c++, f)。另一个例子,考很多C中出toupper()函数。它将一个小写字母转换为的大写字母,而其它字符不。如果我所有的小写字母和所有的大写字母都是相的(大小写之可能有所差距),我可以得到这样的函数:
toupper(c) {
    if(c >= 'a' && c <= 'z')
        c += 'A' - 'a';
    return c;
}
在很多C实现中,了减少比实际计要多的开销,通常将其实现为宏:
#define toupper(c) ((c) >= 'a' && (c) <= 'z' ? (c) + ('A' - 'a') : (c))
很多比函数要快。然而,当你着写toupper(*p++),会出奇怪的果。
    另一个需要注意的地方是使用宏可能会生巨大的表达式。例如,继续max()的定
#define max(a, b) ((a) > (b) ? (a) : (b))
们这个定abcd中的最大。如果我直接写:
max(a, max(b, max(c, d)))
它将被
((a) > (((b) > (((c) > (d) ? (c) : (d))) ? (b) : (((c) > (d) ? (c) : (d))))) ?
 (a) : (((b) > (((c) > (d) ?
(c) : (d))) ? (b) : (((c) > (d) ? (c) : (d))))))
出奇的大。我可以通平衡操作数来使它短一些:
max(max(a, b), max(c, d))
会得到:
((((a) > (b) ? (a) : (b))) > (((c) > (d) ? (c) : (d))) ?
 (((a) > (b) ? (a) : (b))) : (((c) > (d) ? (c) : (d))))
看起来是写:
biggest = a;
if(biggest < b) biggest = b;
if(biggest < c) biggest = c;
if(biggest < d) biggest = d;
好一些。