灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:4067回复:0

C语言的小技巧知识

楼主#
更多 发布于:2012-11-08 20:16

  1、用if(!strcmp(s1, s2)) 比较两个字符串等值,是否是个好风格?

  这并不是个很好的风格, 虽然这是个流行的习惯用法。如果两个字符串相等,

  这个测试返回为真, 但! (“非”) 的使用, 容易引起误会, 以为测试不等值情况。

  另一个选择是用一个宏:

  #define Streq(s1, s2) (strcmp((s1), (s2)) == 0)

  2、为什么有的人用if (0 == x) 而不是if (x == 0)?

  这是用来防护一个通常错误的小技巧:

  if (x = 0)

  如果你养成了把常量放在== 前面的习惯, 当你意外的把代码写成了:

  if (0 = x)

  那编译器就会报怨。明显的, 一些人会觉得记住反换测试比记住输入双= 号容易。当然这个技巧只对和常量比较的情况有用。

  3、*p++ 自增p 还是p 所指向的变量?

  后缀++ 和-- 操作符本质上比前缀一目操作的优先级高,即++与p结合优先级高于*与p的结合优先级, 因此*p++ 和*(p++) 等价, 它自增p 并返回p 自增之前所指向的值(也即先去内容,然后指针p才自加)。要自增p 指向的值, 使用(*p)++, 如果副作用的顺序无关紧要也可以使用++*p.

  4、我有一个char * 型指针正巧指向一些int 型变量, 我想跳过它们。为什么如下的代码((int *)p)++; 不行?

  在C 语言中, 类型转换意味着“把这些二进制位看作另一种类型, 并作相应的对待”; 这是一个转换操作符, 根据定义它只能生成一个右值(rvalue)。而右值既不能赋值, 也不能用++ 自增。(如果编译器支持这样的扩展, 那要么是一个错误, 要么是有意作出的非标准扩展。) 要达到你的目的可以用:

  p = (char *)((int *)p + 1);或者,因为p 是char * 型, 直接用p += sizeof(int);

  但是, 在可能的情况下, 你还是应该首先选择适当的指针类型, 而不是一味地试图李代桃僵。

  5、C 有“按引用传递” 吗?

  真的没有。

  严格地讲, C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用; 操作符。事实上, 当你向函数传入数组(传入指针的情况参见问题6.4 及其它) 时, 编译器本质上就是在模拟按引用传递。但是C 没有任何真正等同于正式的按引用传递或C++ 的引用参数的东西。另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。

  6、我们经常说空指针,那么那个“臭名昭着”的控制振动到底是什么呢?

  语言定义中说明, 每一种指针类型都有一个特殊值-- “空指针” -- 它与同类型的其它所有指针值都不相同, 它“与任何对象或函数的指针值都不相等”.也就是说, 取地址操作符; 永远也不能得到空指针,同样对malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法:表示“未分配” 或者“尚未指向任何地方” 的指针。空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方。如上文所述, 每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分。既然如此,那么怎样在一个程序里面获得一个空指针呢?

  根据语言定义, 在指针上下文中的常数0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数0 为空指针并生成正确的空指针值。因此下边的代码段完全合法:

  char *p = 0;

  if(p != 0)

  然而, 传入函数的参数不一定被当作指针环境,因而编译器可能不能识别未加修饰的0“表示”指针在函数调用的上下文中生成空指针需要明确的类型转换,强制把0看作指针。例如, Unix 系统调用execl接受变长的以空指针结束的字符指针参数它。应该如下正确调用:execl(“/bin/sh”, “sh”, “-c”, “date”,(char *)0);

  如果省略最后一个参数的(char *) 转换, 则编译器无从知道这是一个空指针,从而当作一个0 传入。(注意很多Unix 手册在这个例子上都弄错了。)如果范围内有函数原型, 则参数传递变为“赋值上下文”, 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的0 正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。看一下摘要:

  可以使用未加修饰的0 : 需要显示的类型转换:

  初始化 函数调用,作用域内无原型

  赋值 变参函数调用中的可变参数

  比较

  固定参数的函数调用且在作用域

  内有原型

  然而我们在平时使用的空指针NULL到底是什么含义,它是怎么定义的呢?

  作为一种风格, 很多人不愿意在程序中到处出现未加修饰的0.因此定义了预处理宏NULL (在 和其它几个头文件中) 为空指针常数, 通常是0 或者((void *)0)。希望区别整数0 和空指针0 的人可以在需要空指针的地方使用NULL.使用NULL 只是一种风格习惯; 预处理器把所有的NULL 都还原回0, 而编译还是依照上文的描述处理指针上下文的0.特别是, 在函数调用的参数里, NULL之前(正如在0 之前) 的类型转换还是需要。NULL 只能用作指针常数;

  其实对于空指针有两条潜在的的规则我们必须要明白:

  1、当你在源码中需要空指针常数时, 用“0” 或“NULL”.

  2、如果在函数调用中“0” 或“NULL” 用作参数, 把它转换成被调函数需要的指针类型

  7、数组和指针

  如果在一个源文件中定义了一个char a[6],而在另一个源文件中声明了extern char *a,这样可以吗?

  试了一下,结果显示的是不行,在一个源文件中定义了一个字符串,而在另一个文件中定义了指向字符的指针,.extern char * 的申明不能和真正的定义匹配。类型T 的指针和类型T 的数组并非同种类型。应该使用extern char a[ ].

  那么char *a和char a[]是一样的么?

  并非如此。数组不是指针。数组定义char a[6] 请求预留6 个字符的位置, 并用名称“a” 表示。也就是说, 有一个称为“a” 的位置, 可以放入6 个字符。而指针申明char *p, 请求一个位置放置一个指针, 用名称“p” 表示。这个指针几乎可以指向任何位置: 任何字符和任何连续的字符, 或者哪里也不指。

  如下面的语句所示:

  char a[] = “hello”;

  char *p = “world”;

  将会初始化下图所示的数据结果:

  +---+---+---+---+---+---+

  a: | h | e | l | l | o | |

  +---+---+---+---+---+---+

  +-----+ +---+---+---+---+---+---+

  p: | *======> | w | o | r | l | d | |

  +-----+ +---+---+---+---+---+---+

  根据x 是数组还是指针, 类似x[3] 这样的引用会生成不同的代码。认识到这一点大有裨益。以上面的声明为例, 当编译器看到表达式a[3] 的时候, 它生成代码从a 的位置开始跳过3 个, 然后取出那个字符。 如果它看到p[3], 它生成代码找到“p” 的位置, 取出其中的指针值, 在指针上加3 然后取出指向的字符。换言之, a[3]是名为a 的对象(的起始位置) 之后3 个位置的值, 而p[3] 是p 指向的对象的3 个位置之后的值。在上例中, a[3] 和p[3] 碰巧都是‘l' , 但是编译器到达那里的途径不尽相同。本质的区别在于类似a 的数组和类似p 的指针一旦在表达式中出现就会按照不同的方法计算, 不论它们是否有下标。

  如此说来,那么在C语言中“数组和指针等价”到底是什么意思呢?

  在C 语言中对数组和指针的困惑多数都来自这句话。说数组和指针“等价”不表示它们相同, 甚至也不能互换。它的意思是说数组和指针的算法定义可以用指针方便的访问数组或者模拟数组。特别地, 等价的基础来自这个关键定义:

  一个T 的数组类型的左值如果出现在表达式中会蜕变为一个指向数组第一个成员的指针(除了三种例外情况); 结果指针的类型是T 的指针。这就是说, 一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了;a[0] 一样。例外的情况是, 数组为sizeof 或;操作符的操作数,或者为字符数组的字符串初始值。作为这个这个定义的后果, 编译器并那么不严格区分数组下标操作符和指针

  在形如a 的表达式中, 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如p 那样寻址, 如问题6.2 所述, 尽管最终的内存访问并不一样。如果你把数组地址赋给指针:p = a;那么p[3] 和a[3] 将会访问同样的成员。

  那么数组和指针的区别是什么呢?

  数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间(可能使用malloc), 但是可以随意重新赋值(即, 指向不同的对象), 同时除了表示一个内存块的基址之外, 还有许多其它的用途。

  8、为什么strncpy() 不能总在目标串放上终止符''?

  strncpy() 最初被设计为用来处理一种现在已经废弃的数据结构--定长, 不必'n0' 结束的“字符串”.strncpy 的另一个怪癖是它会用多个'n0' 填充短串, 直到达到指定的长度。在其它环境中使用strncpy() 有些麻烦, 因为你必须经常在目的串末尾手工加'n0'.可以用strncat 代替strncpy 来绕开这个问题: 如果目的串开始时为空(就是说, 如果你先*dest ='n0’), strncat() 就可以完成你希望strncpy() 完成的事情。另外一个方法是用sprintf(dest, “%.*s”, n, source)。如果需要复制任意字节(而不是字符串), memcpy() 是strncpy()更好的选择。

  怎样在C 程序中取得当前日期或时间?

  只要使用函数time(), ctime(), localtime() 和/或strftime() 就可以了。下面是个简单的例子:

  #include

  #include

  int main()

  {

  time_t now;

  time(;now);

  printf(“It's %s”, ctime(;now));

  return 0;

  }

  怎样获得在一定范围内的随机数?

  直接的方法是rand() % N /* 不好*/

  试图返回从0 到N ? 1 的数字。但这个方法不好, 因为许多随机数发生器的低位比特并不随机, 参见问题13.16.一个较好的方法是:

  (int)((double)rand() / ((double)RAND_MAX + 1) * N)

  如果你不希望使用浮点, 另一个方法是:

  rand() / (RAND_MAX / N + 1)

  两种方法都需要知道RAND_MAX, 而且假设N 要远远小于RAND MAX.RAND MAX 在ANSI 里#define 在.顺便提一下, RAND_MAX 是个常数, 它告诉你C 库函数rand() 的固定范围。你不可以设RAND MAX 为其它的值, 也没有办法要求rand() 返回其它范围的值。如果你用的随机数发生器返回的是0 到1 的浮点值, 要取得范围在0 到N-1内的整数, 只要将随机数乘以N 就可以了。


喜欢0 评分0
游客

返回顶部