《The C Programming Language, 2nd Edition》K&R Chapter 2 - Types, Operators and Expressions 笔记
名称
变量名由数字、字母、下划线组成,其中数字不能开头,下划线不建议开头,因库例程通常以下划线开头。
内部名(内部链接)至少前31个字符有效,外部名(外部链接)至少前6个字符有效。
变量类型
C语言只提供四种基本类型:
- char,占1 byte,能够容纳本地字符集的一个字符。
- int,整数,反映具体的机器整数的最自然长度。
- float,单精度浮点。
- double,双精度浮点。
short和long为限定符。
- short int和long int等价于short和long。
- short通常16位。int通常16或32位。long通常32位。
- short和int至少16位。long至少32位。
- short不大于int,int不大于long。
signed和unsigned,适用于char和int。
float、double、long double的长度可以相同。
取决于机器的类型的长度具体见<limits.h>和<float.h>。
char有无符号取决于机器。
char转换为int时,符号扩展还是0扩展也取决于机器。
C语言只保证可打印的字符为正值。
常量
整数,后辍为l或L为long,u或U为unsigned,ul或UL为unsigned long。
无后辍的整数,int能容纳为int,int不能容纳时,为能容纳它的最小整数类型(无法确定有无符号)。
浮点,无后辍为double,f或F为float,l或L为long double。
前辍0表示8进制,0x或0X为16进制,8和16进制数的后辍同理。
'\0xx'同前辍0,'\xhh'同辍0x。
'\0'为0。
常量表达式在编译时求值,比如char line[INT_MAX + 1];
。
字符串常量也称为字符串字面值,在编译时连接,比如"hello, " "world"
等价于"hello, world"
。
strlen()不包含字符串末尾的'\0'。
字符常量的值取决于字符集,推荐使用<ctype.h>库函数进行操作。比如转换为小写字母tolower(c)。在ASCII字符集中大小写字母的间隔是固定的,但EBCDIC字符集中不成立。
枚举
enum boolean {NO = 1, YES};
enum boolean b = NO;
定义枚举时,成员按顺序自增1,第一个成员默认为0。
与#define相比,枚举优势为:
- 常量值自动生成。
- 为枚举变量赋值时,编译器会进行类型检查。(不会进行枚举值是否有效的检查)
- Debug程序会使用枚举名显示枚举值。
声明与定义
变量必须先声明后使用(含隐式声明)。
变量声明时可以初始化,声明同时初始化是一个定义。
静态变量,在程序开始时初始化,初始化表达式必须为常量, 默认初始值为0。
自动变量,每次进入函数时初始化,初始化表达式任意,默认初始值未定义。
外部变量和static变量都是静态变量。
const变量,不能通过这个变量直接修改它的值。
const数组,不能通过这个数组直接修改它元素的值。
运算符
%不能用于浮点。
整数除法和%处理负数的结果取决于机器。
优先级(从高到低):
- 算术运算符「一元+和-」>「* / %」>「二元+和-」
- 关系运算符「> >= < <=」>「== !=」
- 逻辑运算符「&&」>「||」(短路运算符)
- 赋值运算符「 =」
关系和逻辑运算符结果为真返回1,为假返回0。
逻辑非运算符「!」将非0变为0,0变为1。
自增运算符的操作数只能是变量,(i+j)++是非法的。
按位操作符「& | ^ << >> ~」只用于整数。
「& | 」不是短路运算符。
「^」异或,相同为0,不同为1。
「>>」符号位扩展还是0扩展取决于机器。
复合赋值运算符的优势时,左边的表达式只求值一次。
条件运算符的后两个表达式只有一个被求值。
条件运算符的优先级仅比赋值运算符高。
C语言没有约定多元运算符中操作数的求值顺序,取决于机器。比如x = f() + g();
中f()和g()的求值顺序是未定义的。
函数调用,复合赋值,自增和自减运算符可能产生副作用(改变变量的值)。多元运算符中操作数的副作用生效的顺序,取决于机器,比如a[i] = i++;
。
C语言约定所有实参的副作用,在函数调用之前生效。
运算符优先级与结合性如下:
类型转换
二元运算符会将较窄的操作数,转换为较宽的操作数的类型,再进行运算。
- char和short运算时转换为int。
- float运算时不会转换为double。
- 整数与浮点运算时转换为浮点。
- 同类型运算时signed转换为unsigned。
假设int为16位,long为32位,那么-1L<1U,-1L>1UL。
整数较宽的类型转换为较窄的类型时高位丢弃。
浮点转换为整数时,小数丢弃。
double转换为float时,是四舍五入还是截取取决于机器。
没有函数原型的情况下,实参char、short转换为int,float转换为double。
强制转换运算符与一元运算符优先级相同。
强制转换不改变变量的值。
有函数原型的情况下,实参按形参类型,自动强制转换。
例程
strlen函数
/* strlen: return length of s */
int strlen(char s[])
{
int i;
i = 0;
while (s[i] != '\0')
++i;
return i;
}
lower函数(将字符转为小写,仅ASCII有效)
/* lower: convert c to lower case; ASCII only */
int lower(int c)
{
if (c >= 'A' && c <= 'Z')
return c + 'a' - 'A';
else
return c;
}
rand和srand函数
unsigned long int next = 1;
/* rand: return pseudo-random integer on 0..32767 */
int rand(void)
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
/* srand: set seed for rand() */
void srand(unsigned int seed)
{
next = seed;
}
字符串中删除所有指定字符
/* squeeze: delete all c from s */
void squeeze(char s[], int c)
{
int i, j;
for (i = j = 0; s[i] != '\0'; i++)
if (s[i] != c)
s[j++] = s[i];
s[j] = '\0';
}
strcat函数(字符串连接)
/* 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 */
;
}
返回整数x中从第p位(0位起向左)开始右n位(含p)
/* getbits: get n bits from position p */
unsigned getbits(unsigned x, int p, int n)
{
return (x >> (p+1-n)) & ~(~0 << n);
}
统计整数x中值为1的二进制位数
/* bitcount: count 1 bits in x */
int bitcount(unsigned x)
{
int b;
for (b = 0; x != 0; x >>= 1)
if (x & 01)
b++;
return b;
}