目录
[案例] 从键盘上输入1个正整数给int变量num,按二进制位输出该数。
一、关键字
1、register
register:请求编译器尽可能地将变量存在CPU内部寄存器中;
使用注意: register修饰变量的类型必须是CPU所接受的;
register变量可能不是在内存中存储,所以不能使用&来获取变量的地址(内存中含有地址)
2、static
全局静态变量:作用范围局限于它的源文件,即只有本文件内的代码才可以访问它,变量名在其他文件内不可见
局部静态变量:局限于特定函数,但出作用域并不释放,在函数体内的静态变量的值也能够维持
静态函数:作用范围仅限于它的源文件,即只有本文件内才能够调用,函数名在其他文件不可见
存放位置(static):程序开始时,存放在全局数据区,程序结束时释放空间,默认初始化值是0,使用时可改变其值;
局部变量 ——静态局部变量(由栈区 到 全局数据区;默认值由 垃圾值 到 0;作用范围特定函数内(不变));
全局变量 ——静态全局变量(作用范围由 全部工程文件 到 当前文件;默认值为 0(不变);位置:全局数据区不变)
普通函数 ——静态函数(作用范围由 全部工程文件 到 当前文件)
函数不允许嵌套定义
举例:认真体会:
3、const
在面向对象语言当中(C++)中,const用于定义 常量;
在C语言中,const用来定义只读变量
strcpy( chsr *dest , const char *src) 指:源字符串(不可修改)复制到 (可改的目标文件中)
const int* a; int const *a;
是一个指向const int型的指针,a所指向的内存单元不可改写,所以(*a)++是不允许的,但a可以改写,所以a++是允许的。(若 int i;a = &i ; *a = 5(错误);所以不能通过*a 改变i 的值; 但是可以改变 i 的值;)
int* const a;
a是一个指向int型的const指针,*a是可以改写的,但a不允许改写。
int const * const a;
a是一个指向const int型的const指针,因此*a和a都不允许改写。
const给读代码的人传达非常有用的信息。比如一个函数的参数是const char *,你在调用这个函数时就可以放心地传给它char *或const char *指针,而不必担心指针所指的内存单元被改写。 尽可能多地使用const限定符,把不该变的都声明成只读,这样可以依靠编译器检查程序中的Bug,防止意外改写数据。
const对编译器优化是一个有用的提示,编译器也许会把const变量优化成常量
4、volatile
在寄存器定义(0x86);用volatile修饰,能较好的避免被优化掉;
5、extern
extern:表明变量或函数的定义在别的文件中,下面用到的这些变量或是函数是外来的,不是本文件定义的,提示编译器遇到此变量或函数时,在其他模块中寻找定义;
扩展使用范围:extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。
如下图所示:
static 与 extern :static int a :静态全局变量 。如:下图所示:
加上static,限制了a的作用域,在file2.c中引用失败;但不管是否加上static(不加是全局变量),a都按静态存储方式(全局数据区)存放。
如果一个函数只能被本文件中其它函数所调用,称为内部函数(或静态函数)。定义时在函数类型前加static。(函数定义时,加与不加extern ,都是一样的)
6、struct(结构体)
多种数据组合起来的一个整体,其表现形式是一个结构体。
内存空间是分配给变量的,不分配给数据类型(不见变量,不分配)
结构体变量的内存空间是由内部各个变量所占空间之和
1、结构体创建(创建一个新的类型)
方法一:
struct node
{ char a;
short b;
char c;
}; (注意:;不能省)
struct nade link;(数据结构类型 定义变量(link))
struct student
{ int num;
char name[20];
float score;
}stu1, stu2; (等于:定义结构体类型 并且struct student stu1,stu2;)
方法二:
typedef struct
{ char a;
short b;
char c;
}node;
nade link;(数据结构类型 定义变量(link))
无名结构体定义:
struct { int num;
char name[20];
float score;
}stu1, stu2;(无名结构体类型 定义变量 stu1 ,stu2)
2、结构体的意义
结构体的花括弧内是该结构体中的各个成员,由它们组成一个结构体;在结构体内对各成员都应进行类型声明;
“成员表列”也称为域表。每个成员也称为结构体中的一个域,成员名命名规则与变量名一样;成员也可以是一个已定义的结构体类型变量:
成员也可以是指向本结构体类型的指针变量(结构体指针只能指向自己):
每个成员名前的类型标识符可以为已经定义了的任意类型,当然可以是结构体类型标识符,即结构体成员也可以是另一个结构体变量。
在程序中可以定义多个结构体类型,不同结构体类型用不同的结构体名来区分。
3、结构体的引用形式
结构体变量名. 成员名(“.”是成员运算符,在所有的运算符中优先级最高; ”.“一般变量用;—> 指向结构体的指针用)
对结构体的操作就是对成员的一个个操作。而对结构体的整体操作只有两种情况:
(初始化时,变量直接赋值; 两个同类型的结构体相互赋值)
结构体数组的赋值尤其要注意(strcpy(stu1.name, "Li Ming");)
指向结构体变量指针的定义:
注意:不能用指向结构体变量的指针指向该结构体变量的某个成员。
p是结构体类型,而stu.num属于 int 型(不匹配);
4、访问结构体成员变量的三种方法:
说明:
“->”为指向运算符,是优先级最高的运算符
成员运算符“.”的优先级高于指针运算符“*”,因此采用 “(*p).成员名” 形式时,括号不能省略;
注意以下几种运算:
5、在定义结构体变量的同时,可以进行初始化
注意:结构体变量的各个初值用花括号{、}括起来,大括号内各个成员变量的值之间用逗号分隔,其值必须与成员变量一一对应,且数据类型应与成员变量一致。
6、结构体数组的初始化
数组中各个元素的初值用大括号{、}括起来,同一数组元素的各个成员变量的初值用逗号分隔。stu[ ]:省略数组大小,必须提供所有的初始值;数组的定义(初始化下标大小;初始值数值;(必须有其中一个))。a[ ][ ],第一个方括号可以为空,第二个不行;a[ ][ ][ ]也是一样。
7、字对齐与半字对齐
字对齐:int型变量占四个字节,其他数据类型都按照四个字节的倍数分配(会产生内存空洞)。同类型的变量放在一起可以减少内存空洞
后一个变量若能在前一个变量分配的空间中存放下,则后一个变量不再重新分配空间。
半字对齐: short型变量占2个字节
若即存在int 与 short 则 按照 int 的字节(字对齐);若 int 与 short 都不存在则按类型相加。
7、union(共用体)
1、union的含义
union(共用体) 使用方法与struct(结构体) 的使用方法相同
当多个基本数据类型或复合数据结构要占用同一片内存时,我们要使用联合体(共用体);
当多种类型,多个对象,多个事物只取其一时(我们姑且通俗地称其为“n 选1”),我们也可以使用联合体来发挥其长处;
2、大端字节序(网络字节序)
CPU是大端字节序还是小端字节序:通过公用体可以知晓(因为共用体的变量占同一段内存)
试用共用体验证大小端字节序:
union node
{
int a;
char b; (int a ; 与 char b ; 占同一段旅内存空间。a 占四个字节;b 占一个字节;)
}; (故通过 b 取出 a 的第一个字节,自然可以知道内存的存储顺序)
int main()
{
union node c;
c.a =1;
if(c.b == 1)
printf("small!\n");
else
printf("big!\n");
return 0;
}
8、enum
枚举类型声明代表常量的符号名称。
enum的默认值
enum kids{nippy, slats, skippy, nina ,liz};
enum kids a; a = nippy;
enum的指定值
enum levels {low=100,medium=500,high=2000};
enum levels a; a = low; (a = 100 (错误))a 不是整形 是枚举类型
若枚举常量没有取值:默认:第一个为 0;后一个枚举常量是前一个值加1;
enum的用法
作为switch的标签
9、typedef
typedef是C语言的关键字,其作用是为一种数据类型定义一个新名字
格式:typedef 数据类型 自定义数据类型
typedef unsigned long uint32;
在嵌入式的开发中,由于涉及到移植问题,typedef的功能就更引人注目了。
如:
typedef struct
{ char a;
short b;
char c;
}node;
nade link;(数据结构类型 定义变量(link))
int * ( * ( * fpl)( int ))[10] (见:教你理解复杂的C-C++声明)
10、宏—#define
1、带参数
定义常量与命令(避免幻数)
#define MAX 100 (MAX:宏名 100:宏体)
#define SIZE 1024
#define LOGIN_SUCCESS 1
#define LOGIN_FAIL 0
注意:在宏定义的命名时,尽量能够清晰的表明其用途;
预处理 == 宏展开 == 宏替换(字符替换;不做语法分析、不做运算)
2、定义函数
#define MAX(a, b) ((a)>(b)?(a):(b))
宏函数 宏体
宏体 :所有形参 ,以及宏体都要加括号
普通函数:做运算、做语法分析;
宏函数:字符替换;不做语法分析、不做运算(多用于执行频率高且结构简单)
#define语句:
#define __DEBUG_
#ifndef __DEBUG_
#define debug_msg(fmt,args...)
#else #define debug_msg(fmt, args...) printf(fmt,
##args)
#endif
#if 0/1
..........
#endif //用于注释代码是否执行
二、表达式及语句
1、选择语句(if)
if 语句是C 语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if 语句
本节以“与零值比较”为例,展开讨论
布尔变量与零值比较
整型变量与零值比较
浮点变量与零值比较 (用绝对值 :fads 做差,与1e-6比较)
指针变量与零值比较 (与 null 相比较)
2、循环语句(for while do ...while)
for 语句使用频率最高,while 语句其次,do 语句很少用。
重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。
在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU 跨切循环层的次数;
a与b交换(三种):
a = a+b; b = a -b; a = a - b;
a = a^b; b = a^b; a = a^b;
t = a ; a = b; b = t;
3、switch分支语句
switch语句可以产生具有多个分支的控制流程。
它的格式是:
switch(控制表达式)
{
case常量表达式:语句列表 ;break
case常量表达式:语句列表 ; break
...
default:语句列表 ; break;
}
每个case 语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠);
不要忘记最后那个default 分支。即使程序真的不需要default 处理,也应该保留语句 default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了default 处理。
4、goto语句
自从提倡结构化设计以来,goto 就成了有争议的语句。首先,由于goto 语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。其次,goto 语句经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句,例如:
goto state; char s1, s2; // 被goto 跳过
int sum = 0; // 被goto 跳过
state:
如果编译器不能发觉此类错误,每用一次goto 语句 都可能留下隐患。
goto只用于跳出多循环
三、位运算
1、按位与──&
主要用途:清0(写0),保留某些位(写1)
2、按位或──|
主要用途:置1(写1),保留某些位(写0)
3、按位异或──^
主要用途:使1个数的某(些)位翻转(即原来为1的位变为0,为0的变为1),其余各位不变。
4、按位取反──~
主要用途:间接地构造一个数,以增强程序的可移植性。
5、按位左移──<<
6、按位右移──>>
说明:
(1)x、y和“位数”等操作数,都只能是整型或字符型数据。除按位取反为单目运算符外,其余均为双目运算符。
(2)参与运算时,操作数x和y,都必须首先转换成二进制形式,然后再执行相应的按位运算。 例如,5<<2=20:0101 → 10100,20 >> 2=5:10100 → 00101
(3)实现&、|、^运算主要用途的方法
1)构造1个整数:该数在要取(或保留)的位、或要置1的位、或要翻转的位上为1,其余均为0。
2)进行按位与、或按位或、或按位异或操作。
(4)实现按位取反主要用途的方法
1)求~0,间接地构造一个全1的数;
2)按需要进行左移或右移操作,构造出所需要的数。 例如,直接构造一个全1的数,在IBM-PC机中为0xffff(2字节),而在VAX-11/780上,却是0xffffffff(4字节)。如果用~0来构造,系统可以自动适应。
应用举例 [案例]
从键盘上输入1个正整数给int变量num,输出由8~11位构成的数(从低位、0号开始编号)。
程序说明:~ ( ~0 << 4)
按位取0的反,为全1;左移4位后,其低4位为0,其余各位为1;再按位取反,则其低4位为1,其余各位为0。这个整数正是我们所需要的。
[案例] 从键盘上输入1个正整数给int变量num,按二进制位输出该数。
1.复合赋值运算符 除按位取反运算外,其余5个位运算符均可与赋值运算符一起,构成复合赋值运算符: &=、|+、^=、<<=、>>=
2.不同长度数据间的位运算──低字节对齐,短数的高字节按最高位补位:
(1)对无符号数和有符号中的正数,补0;
(2)有符号数中的负数,补1。