5.
  int my_strlen(const char *x)
  {
  int res = 0;
  while(*x)
  {
  res++;
  x++;
  }
  return res;
  }
  Q: 上面提供的函数应该返回以空终止字符结尾的字符串长度,找出其中存在的一个 bug 。
  A: 使用 int 类型来存储对象的大小是错误的,因为无法保证 int 类型能够存下任何对象的大小,应该使用 size_t。
  6.
  #include <stdio.h>
  #include <string.h>
  int main()
  {
  const char *str = "hello";
  size_t length = strlen(str);
  size_t i;
  for(i = length - 1; i >= 0; i--)
  {
  putchar(str[i]);
  }
  putchar('n');
  return 0;
  }
  Q: 这个循环是死循环。这是为什么?
  A: size_t 是无符号类型。 如果 i 是无符号类型, 那么 i >= 0 永远都是正确的。
  7.
  #include <stdio.h>
  void f(int *i, long *l)
  {
  printf("1. v=%ldn", *l);
  /* (1) */
  *i = 11;
  /* (2) */
  printf("2. v=%ldn", *l);
  /* (3) */
  }
  int main()
  {
  long a = 10;
  f((int *) &a, &a);
  printf("3. v=%ldn", a);
  return 0;
  }
  这个程序分别用两个不同的编译器编译并且在一台小字节序的机器上运行。获得了如下两种不同的结果:
  1. v=10    2. v=11    3. v=11
  1. v=10    2. v=10    3. v=11
  Q:你如何解释第二种结果?
  A:所给程序存在未定义的行为。程序违反了编译器的强重叠规则(strict aliasing)。虽然 int 在第 (2) 行被改变了,但是编译器可以假设任何的 long 都没有改变。我们不能间接引用那些和其他不兼容类型指针相重名的指针。这是编译器之所以可以传递和在第一行的执行过程中被读取的相同的 long (第(3)行)的原因。
  8.
  #include <stdio.h>
  int main()
  {
  int array[] = { 0, 1, 2 };
  printf("%d %d %dn", 10, (5, array[1, 2]), 10);
  }
  Q: 这个代码是否是正确的?如果不存在未定义行为,那么它会输出什么?
  A: 是的, 这里使用了逗号运算符。首先,逗号左边的参数被计算后丢弃,然后,右边的参数经过计算后被当做整个运算符的值使用,所以输出是 10 2 10。
  注意在函数调用中的逗号符号(比如 f(a(), b()))并不是逗号运算符,因此也不会保证运算的顺序,a() 和 b() 会以随机的顺序计算。
  9.
  unsigned int add(unsigned int a, unsigned int b)
  {
  return a + b;
  }
  Q: 函数 add(UINT_MAX, 1) 的结果是什么?
  A:对于无符号数的溢出结果是有定义的,结果是 2^(CHAR_BIT * sizeof(unsigned int)) ,所以函数 add 的结果是 0 。
  10.
  int add(int a, int b)
  {
  return a + b;
  }
  Q:函数 add(INT_MAX, 1) 的结果是什么?
  A:有符号整数的溢出结果是未定义的行为。
  11.
  int neg(int a)
  {
  return -a;
  }
  Q:这里是否可能出现未定义的行为?如果是的话,是在输入什么参数时发生的?
  A:neg(INT_MIN)。如果 ECM 用附加码(补码)表示负整数, 那么 INT_MIN 的值比 INT_MAX 的值大一。在这种情况下,-INT_MIN 造成了有符号整数的溢出,这是一种未定义的行为。
  12.
  int div(int a, int b)
  {
  assert(b != 0);
  return a / b;
  }
  Q:这里是否可能出现未定义的行为?如果是的话,是在什么参数上发生的?
  A:如果 ECM 用附加码表示负数, 那么 div(INT_MIN, -1) 导致了与上一个例子相同的问题。