字符串常量是可以在编译时连接,常量表达式在编译时求值。
变量名首字符一定要为字母,下划线"_"被视为字母,但是库例程序名字通常以"_"开头,所以不建议使用"_"作为变量名首字母。
局部变量一般使用较短的变量名,尤其是循环变量。
但是外部变量应该使用较长且有意义的单词组合作为变量名。
带前缀0的整形常量表示八进制的数。
带前缀0X或0x的整型常量表示十六进制的数。
类似的可以将八进制或十六进制书写转义字符序列。
比如:
'\ooo' ooo代表1-3个八进制数字
'\xhh' hh代表1个或多个十六进制数字
测试程序:
#define A '\141'
#define B '\x62'
int main()
{
int num = 0X12;
printf("%d\n", num);
num = 012;
printf("%d\n", num);
printf("%c\n", A);
printf("%c\n", B);
return 0;
}
C语言在编译时可以将多个字符串常量连接起来。
printf("hello, world.\n");
printf("hello, ""world.\n");
以上两种写法是等价的。
枚举是一个常量整型值的列表。
在没有显示说明的情况下,enum类型中第一个枚举名的值为0,第二个为1,以此类推。如果只指定了部分枚举名的值将依着最后一个指定值向后递增。
enum col {WHITE, BLACK, BLUE = 10, RED};
int main()
{
enum col color;
color = RED + 1; //RED + 1已经不是属于col范围的值,但还是合法的。
printf("%d\n", color); //RED为10+1=11,所以color等于12。
color = BLACK;
printf("%d\n", color);//WHITE默认为0,BLACK为1。
return 0;
}
C语言没有指定char类型的变量是signed还是unsigned。当将一个char类型转化成int的时候,结果是否为负数取决于机器。某些机器,char类型的最左为1,某些则是0。所以转化时,最左边添的数字也不同。为了保证可移植性,如果要在char类型的变量中存储非字符数据,最好指定signed或unsigned限定符。
#include <stdio.h>
int main()
{
char a=-1;
signed char b=-1;
unsigned char c=-1;
printf("a = %d\nb = %d\nc = %d\n",a,b,c);
return 0;
}
以上程序可以测试在你的机器中,char等同于signed char还是unsigned char。
unsigned是一个很有用的类型,比如定义unsigned x;这样可以保证x >> 1无论在什么机器上运行,左边空出的位都用0填补,而不是符号位。
表达式中的float类型的操作数不会自动转换为double类型,这一点与最初的定义有所不同。float对于double类型而言,有点在于省时间和空间。
当表达式中包含unsigned类型的操作数时,转换规则要复杂一些。因为带符号与无符号值之间的比较运算是与机器相关的,因为它们取决于机器中不同整数类型大小。
#include <stdio.h>
int main()
{
printf("%d %d %d %d\n", sizeof(int), sizeof(unsigned), sizeof(long), sizeof(unsigned long));
printf("-1U < 1U : %d\n", (-1U < 1U));
printf("-1L < 1U : %d\n", (-1L < 1U));
printf("-1L > 1UL : %d\n", (-1L > 1UL));
return 0;
}
对于不同类型的比较,首先要弄清楚它们在机器中但多少位,然后记住负数在机器中最高位为1即可。
#include <stdio.h>
#include <math.h>
int main()
{
int n = 2;
double ans;
printf("size = %d\n", sizeof(n));
ans = sqrt(n);
printf("size = %d\n", sizeof(n));
return 0;
}
库函数sqrt的参数为double类型,在把n传递给函数sqrt之前先将其转为double类型。注意,强制类型转只是生成一个指定类型的n的值,n本身的值并没有改变,类型当然也没变。
C语言中的位运算只能作用于整数操作数,即只能作用于带符号或无符号的char short int long类型。
在做位运算时,要考虑代码的可移植性。即不能假定所有机器都是16位或者32位。举个具体的例子,x & ~077 要比 x & 0177700 的表达式要好,因为后者假定x是16位的数值。这种可移植性的形式并没有增加额外的开销,因为常量表达式都在编译时求值。
x *= y + 1的含义为 x = x * (y + 1) ,而不是x = x * y + 1。
赋值表达式的类型是它的做操作数的类型,其值是赋值操作完成后的值。
条件表达式可以编写出很简洁的代码。例如:
for (i = 0;i < n;i++)
printf("%6d%c", a[i], (i%10==9 || i == n - 1)? '\n' : ' ');
printf("You have %d item%s.\n", n, n == 1 ? "" : "s");
C语言中没有指定同一运算符中多个操作数的计算顺序(&& || ?: 和 ,运算符除外)。例如:x = f() + g(); f()可以在g()之前计算,也可以在g()之后计算。
C语言也没有指定函数各参数的求值顺序。
所以:
printf("%d %lf/n", ++n, pow(2, n));/*错*/
要写成:
++n;
printf("%d %lf/n", n, pow(2, n));
在任何一种编程语言中,如果代码的执行结果与求值顺序相关,则都是不好的程序设计风格。
写得很棒的简单C函数:
/* squeeze函数:从字符串s中删除字符c */
void squeeze(char s[], int c)
{
int i, j;
for(int i = j = 0; s[i] != '\0';i++)
if (s[i] != c)
s[j++] = s[i];
s[j] = '\0'
}
/* strcat函数:将字符串t连接到字符串s的尾巴;s必须有足够大的空间*/
void strcat(char s[], char t[])
{
int i, j;
i = j = 0;
while (s[i] != '\0')
i++;
while ((s[i++] = t[j++]) != '\0')
;
}