C语言标准

C语言的发展历史大致上分为三个阶段:Old Style C、C89和C99。Ken Thompson和Dennis Ritchie最初发明C语言时有很多语法和现在最常用的写法并不一样,但为了向后兼容性(Backward Compatibility),这些语法仍然在C89和C99中保留下来了。C89是最早的C语言规范,于1989年提出,1990年首先由ANSI(美国国家标准委员会,American National Standards Institute)推出,后来被接纳为ISO国际标准(ISO/IEC 9899:1990),因而有时也称为C90,最经典的C语言教材[K&R]就是基于这个版本的,C89是目前最广泛采用的C语言标准,大多数编译器都完全支持C89。C99标准(ISO/IEC 9899:1999)是在1999年推出的,加入了许多新特性,但目前仍没有得到广泛支持,在C99推出之后相当长的一段时间里,连gcc也没有完全实现C99的所有特性。

Hello World

1
2
3
4
5
6
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}

编译与运行

编译程序

如果使用的是GNU编译器

1
cc hello.c

如果使用的是微软编译器

1
cl -GX hello.c

运行程序

UNIX环境

1
./a.out

Windows

1
a.exe

返回值

访问main函数的返回值的方式和系统有关,不论UNIX还是Windows系统,执行程序后,必须发出一个适当的echo 命令。

UNIX系统查看状态

1
$ echo $?

Windows系统查看状态

1
echo %ERRORLEVEL%

注释

注释以字符 /* 开始, 以 */ 结束。注释不能够嵌套,也不能够出现在字符串字面值或字符字面值中。

有的C代码中有类似 // comment 的注释,两个/斜线(Slash)表示从这里直到该行末尾的所有字符都属于注释,这种注释不能跨行,也不能穿插在一行代码中间。这是从C++借鉴的语法,在C99中被标准化。

标识符

  1. 标识符是由字母和数字组成的序列,但其第一个字符必须为字母。下划线 _ 被看作是字母,通常用于命名较长的变量名,以提高可读性。
  2. 例程的名字通常以下划线开头,因此变量名不要以下划线开头。
  3. 大小写敏感;
  4. 在传统的C语言用法中,变量名使用小写字母,符号常量名全部使用大写字母;
  5. 对于内部名而言,至少前31个字符是有效的。对于外部名,ANSI标准只保证前6个字符的唯一性,并且不区分大小写。
  6. 类似于if、else、int、float等关键字是保留给语言本身使用的,不能把它们用作变量名。所有关键字中的字符都必须小写。

C99规定的关键字有:

auto break case char const
continue default do double
else enum extern float for
goto if inline int long
register restrict return short signed
sizeof static struct switch typedef
union unsigned void volatile while
_Bool _Complex _Imaginary

运算符

算术运算符

  1. 二元算术运算符包括:+-*/%(取模运算符);
  2. 整数除法会截断结果中的小数部分;
  3. %不能应用于float或double类型;
  4. 在有负操作数的情况下,整数除法截取的方向以及取模运算结果的符号取决于具体机器的实现,这和处理上溢或下溢的情况是一样的。
  5. 优先级:二元运算符+-具有相同的优先级,它们的优先级比*/%的优先级低,而运算符*/%的优先级又比一元运算符+-的优先级低。算数运算符采用从左到右的结合规则。

关系运算符与逻辑运算符

关系运算符包括下列几个运算符:

>>=<<=

它们具有相同的优先级。优先级仅次于它们的是相等性运算符:

==!=

关系运算符的优先级比算术运算符低 。因此,表达式 i < lim-1 等价于 i < (lim - 1)

逻辑运算符&&||有一些较为特殊的属性,由&&||连接的表达式按从左到右的顺序进行求值,并且,在知道结果值为真或为假后 立即停止运算 。逻辑运算符优先级:&&||的优先级高,但两者都比关系运算符和相等性运算符的优先级低。

自增运算符与自减运算符

  • ++n先将n的值递增1,然后再使用变量n的值。
  • n++则是先使用变量n的值,然后再将n的值递增1。

例:标准函数strcat(s,t),它将字符串t连接到字符串s的尾部。

1
2
3
4
5
6
7
8
9
10
/* strcat: concatenate t to end of s; s must be big enough */
void strcat(char s[], char t[])
{
int i, j;
i = j = 0;
while (s[i] != '\0') /* find end of s */
i++;
while ((s[i++] = t[j++]) != '\0') /* copy t */
;
}

按位运算符

C语言提供了6个位操作运算符。这些运算符只能作用于整型操作数,即只能作用于带符号或无符号char、short、int、long类型:

运算符 作用
& 按位与(AND)
| 按位或(OR)
^ 按位异或(XOR)
<< 左移
>> 右移
~ 按位求反(一元运算符)

按位与运算符&通常用于屏蔽某些二进制位,例如:

1
n = n & 0177;

该语句将n中除7个低二进制位外的其他各位均置为0。

按位或运算符|常用于将某些二进制位置为1,例如:

1
x = x | SET_ON;

该语句将x中对应于 SET_ON 中为1的那些二进制位置为1。

例如:函数getbits(x,p,n),它返回x中从右边数第p位开始向右数n位的字段。

1
2
3
4
5
/* getbits: get n bits from position p */
unsigned getbits(unsigned x, int p, int n)
{
return (x >> (p+1-n)) & ~(~0 << n);
}

三元运算符(? :)

1
expr1 ? expr2 : expr3

应该注意,如果expr2expr3的类型不同,结果的类型也将进行转换。例如,如果f为float类型,n为int类型,那么表达式 (n > 0) ? f : n 是float类型,与n是否为正值无关

运算符优先级与求值次序

运算符的优先级与结合性[1]

运算符 结合性
() [] -> . 从左至右
! ~ ++ -- + - * (type) sizeof 从右至左
* / % 从左至右
+ - 从左至右
<< >> 从左至右
< <= > >= 从左至右
== != 从左至右
& 从左至右
^ 从左至右
| 从左至右
&& 从左至右
|| 从左至右
?: 从右至左
= += -= /= ^= &= ^= |= <<= >>= 从右至左
, 从右至左

深入阅读

  1. 运算符总结

  1. 一元运算符+-&*比相应的二元运算符+-&*的优先级高。