<C专家编程>
auto关键字:
c中的auto关键字是一个累赘,他的作用是"在进入程序块的时候自动进行内存分配",他只对创建符号表入口的编译器设计有意义
嵌套函数:
c是不容许嵌套函数的,这简化的编译器,并提高了C运行时候的组织结构
register关键字:
为编译器设计者提供帮助,如果程序中哪些变量比较热门就可以放入寄存器中。事实上是C的一处设计失误,通常由程序自动处理变量在寄存器中的动态分配工作比,一经声明,整个生命周期就被放在寄存器中要好。register关键字,简化了编译器,却把包袱丢给了程序员
Steven Bourne的编码方式:
Bourne在70年代后期编写第七代Bourne Shell的时候,采用了C预处理方法使得C看起来很想Algol-68语言,例如
#define STRING char *
#define IF if{
#define THEN }{
#define ELSE }else{
#define FI ;}
#define WHILE while{
#define DO }{
#define OD ;}
#define INT int {
#define BEGIN {
#define END }
并且使用sbrk进行堆的管理,而不是malloc.
steven声称自己当时之所以采用这种内存分配方式,是为了提高字符串处理的效率,他从来不曾想到有人会阅读他的代码。
steven bourne创立的这种c语言事实上促进了The international Obfuscated C Code Competition(国际C语言混乱代码大赛)
复合赋值符:
C语言从Algol-68继承了一个特性,就是复合赋值符。例如:b+=3
约束条件:
不存在约束条件的都是允许的。例如,虽然规定说关键字是保留的,用户不能定义一个malloc()函数,但是这只是规定而不是约束条件
ANSI对于编译器的要求:
原型的来源:
原型声明的作用是方便编译器进行函数结构检查。可以省略形参名,但是最好不要,因为形参名可以传递一些信息给程序员
类型转换:
数据类型一般朝着浮点精度更高,长度更长来转换,整数型如果转换为signed,则转换为signed,否则为unsigned
unsigned int/unsigned char:
#include
using namespace std;
main()
{
printf("unsigned int -1 is converted to %d\n",(unsigned int)-1);
printf("unsigned char -1 is converted to %d\n",(unsigned char)-1);
printf("unsigned int (-1-30) is converted to %d\n",(unsigned int)(-1-30)); // -30
printf("unsigned char (-1-30) is converted to %d\n",(unsigned char)(-1-30)); //255
}
pragma标识符:
ANSI C标准中的pragma标识符来自于Ada,用于向编译器提供一些信息。例如将某个特定函数扩展为内联函数,或者取消边界检查。
由于pragma并非C固有。遭到了gcc编译器设计者的积极抵制。在gcc 1.34版中,如果使用pragma会导致编译器停止运行而运行一个 计算机游戏。
malloc(strlen(str))错误:
无论何时,malloc(strlen(str))几乎肯定是错误的,malloc(strlen(str)+1)才是正确的,因为字符串都包含一个“\0”作为一个结尾符
NUL和NULL的区别:
一个'L'用于结束一个ASCII字符串
两个'LL'表示什么也不指向(空指针)
MS-DOS的运行时检查:
空指针是程序员的噩梦,支持虚拟内存的系统可以通过检查指针地址是否超过虚拟内存空间来决定是否中止进程。但是MS-DOS并不支持虚拟内存,即使内存访问失败也无法立刻捕捉到这种情况。Microsoft和Borland C采用比较进程执行前后,内存地址为零的值是否相同来查找空指针
C++变量声明:
C++变量声明特别之处在于,允许语句和声明以任意的顺序交叉出现。甚至允许变量声明在for表达式内部
default和defau1t(字母l为数字1):
switch变换中default如果错误写成default也能编译通过
switch 的fall through现象:
switch表达式最大的问题是当执行完某个case表达式的时候,不会自动跳出
fall through现象指的是,如果case表达式后面不加break,就会顺序执行下面所有的case语句
位域:
struct table_entry
{
int count ;
// char c;
unsigned int f1 :4;
unsigned int f2 :1;
};
为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为"位域"或"位段"。所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct 位域结构名
{ 位域列表 };
其中位域列表的形式为: 类型说明符 位域名:位域长度
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态, 用一位二进位即可。
struct table_entry
{
int count ;
char c;
unsigned int f1 :4;
unsigned int f2 :1;
};
这个内存分配问题是要和机器和系统有关,比如有些机器(Intel/windows32)通常是32位对齐,有些(linux)是16位对齐。有的只需要8为对齐,有了这个依据我们就可以分析上题了:
32位对齐:占总内存 = 4 + 4 + 4 = 12
16位对齐:占总内存 = 4 + 2 + 2 = 8
8 位对齐:占总内存 = 4 + 1 + 1 = 6
字符串的合并
ANSI C的特点之一就是相邻的字符串会被合并起来
main()
{
printf("link1""link2""link3""\n");//link1link2link3
}
首次输出不同:
使用static,使得首次输出与之后几次的输出不同
#include
using namespace std;
void generate_initializer()
{
static char separator = ' ';
printf("%c\n",separator);
separator = ',';
}
main()
{
printf("First function call!\n");
generate_initializer();//输出''
printf("Second function call!\n");
generate_initializer();//输出','
printf("Third function call!\n");
generate_initializer();//输出','
}
printf("Second function call!\n");
generate_initializer();
printf("Third function call!\n");
generate_initializer();
}
C的缺省可见性:
C函数缺省全局可见,也可以加一个extern的冗余关键字,如果要限制在文件之外不可见,就必须加一个static关键字
C函数只能要么全局可见,要么其他文件都不可见
C的符号重载:
static:1.定义变量时,表示变量具有连续性
2.在函数级,表示只对该文件可见
extern:1.用于变量时,表示变量在其他地方定义
2.用于函数时,表示全局可见
void:1.作为函数返回值,表示不返回任何值
2.指针定义中,表示通用指针类型
3.位于参数列表,表示没有参数
*:1.乘法运算符
2.用于指针表示间接引用
3.声明中表示指针
&:1.位的AND操作符
2.取地址操作符
操作符优先级:
1..操作符的优先级大于*;
例如:*p.f
误会:p所指对象的字段f (*p).f
实际:p取f,作为偏移,然后进行解除引用操作 *(p.f)
2. []操作符的优先级大于*;
例如:int * ap[]
误会:ap是一个指向int数组的指针 int (*ap) []
实际:ap是一个元素为int指针的数组 int *(ap[])
3.()操作符优先级大于*;
例如: