看完这些问题后,你还会说自己懂 C 语言么?

发表于:2015-10-13 11:00

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:xxmen    来源:51Testing软件测试网采编

  这篇文章的目的是让每个程序员(特别是 C 程序员)说:我真的不懂 C。我想要让大家看到 C 语言的那些阴暗角落比我们想象中更近,甚至那些平常的代码中就包含着未定义的行为。
  这篇文章设置了一系列的问题和答案。所有的例子都是从源代码中单独分离出来的。
  1.
  int i;
  int i = 10;
  Q:这段代码正确吗?是否会因为变量被定义了两次而导致错误的出现?注意这是源于同一个源码文件,而不是函数体或代码段的一部分。
  A:是的,这段代码是正确的。第一行是临时的定义直到编译器处理了第二行的定义之后才成为正式的“定义”。
  2.
  extern void bar(void);
  void foo(int *x)
  {
  int y = *x;
  /* (1) */
  if(!x)
  /* (2) */
  {
  return;
  /* (3) */
  }
  bar();
  return;
  }
  Q: 这样写的结果是即使 x 是空指针 bar() 函数都会被调用,并且程序不会崩溃。这是否是优化器的错误,或者全部是正确的?
  A: 全部都是正确的。如果 x 是空指针,未定义的行为出现在第 (1) 行, 没有人欠程序员什么,所以程序并不会在第 (1) 行崩溃, 也不会试图在第 (2) 行返回假如已经成功运行第 (1) 行。让我们来探讨编译器遵循的规则,它都按如下的方式进行。在对第 (1) 行的分析之后,编译器认为 x 不会是一个空指针,于是第 (2) 行和 第 (3) 行就被认定为是没用的代码。变量 y 被当做没用的变量去除。从内存中读取的操作也会被去除,因为 *x 并不符合易变类型(volatile)。
  这就是无用的变量如何导致空指针检查失效的例子。
  3.有这样一个函数:
  #define ZP_COUNT 10
  void func_original(int *xp, int *yp, int *zp)
  {
  int i;
  for(i = 0; i < ZP_COUNT; i++)
  {
  *zp++ = *xp + *yp;
  }
  }
  有人想要按如下方式来优化它:
  void func_optimized(int *xp, int *yp, int *zp)
  {
  int tmp = *xp + *yp;
  int i;
  for(i = 0; i < ZP_COUNT; i++)
  {
  *zp++ = tmp;
  }
  }
  Q:调用原始的函数和调用优化后的函数,对于变量 zp 是否有可能获得不同的结果?
  A:这是可能的,当 yp == zp 时结果就不同。
  4.
  double f(double x)
  {
  assert(x != 0.);
  return 1. / x;
  }
  Q: 这个函数是否可能返回最大下界(inf) ?假设浮点数运算是按照IEEE 754 标准(大部分机器遵循)执行的, 并且断言语句是可用的(NDEBUG 并没有被定义)。
  A:是的,这是可以的。通过传入一个非规范化的 x 的值,比如 1e-309.
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号