文章目录
1.基本数据类型
- 变量的三个属性:作用域,链接性,存储类型。
这三个属性决定了一个变量的可视性(可以在什么地方使用)和生命期(他的值将保持多久)。 -
C语言只有4种基本数据类型:整型,浮点型,指针,聚合类型(eg:数组和结构等)。
其他的类型都是从这4种基本数据类型的某种组合派生而来的。
(1)整型
-
整型家族包括:字符,短整型,整型和长整形,他们都分为有符号signed和无符号unsigned两种版本
-
整型值相互之间大小的规则是:长整型至少应该和整型一样长,而整型至少应该和短整型一样长(长整形>=整型,整型>=短整型)
-
变量的最小范围
eg:缺省的int是16bit还是32bit?或者其他值?是由编译器决定的。
如果某种机器的环境的子长是32bit的,而且没有什么指令能更有效地处理更短地整型值,他有可能把这3个整型值都设定为32bit。
eg:缺省地char要么是signed char,要么是unsigned char,这取决于编译器。当可移植问题比较重要地时候,字符是否为有符号数就会带来艰难地境地,最佳方案是:将char类型地变量限制在signed和unsigned char的交集内,这样可以获得最大程度的可移植性。
类型 | 最小范围 | 其他 |
char | 0到127 | – |
signed char | -127到127 | – |
unsigned char | 0到255 | – |
short int | -32767到32767 | 至少16bit |
unsigned short int | 0到65535 | – |
int | -32767到36767 | – |
unsigned int | 0到65535 | – |
long int | -2147483647到2147483647 | 至少32bit |
unsigned long int | 0到4294967295 | – |
– | – | – |
-
整型字面值
(a)字面值literal,即常量,命名常量named constant,声明为const的变量,与普通变量的区别是:当它初始化之后,他的值不能再改变。
在整数字面值后面添加字符L或者l(这是字母,不是数字1),可以使这个整数被解释为long整型值;
字符U或u用于把数值指定为unsigned整型值;
整数可以用十进制,八进制,十六进制.字符常量
eg1:十进制字面值,123.。。。十进制字面值可能是:int,long,unsigned long。在缺省情况下,他是最短类型,但能完整容纳该值。
eg2:八进制字面值,0173。。。八进制数值以0开头(数字8和9非法),16进制数值以0x开头:0x7b(可以使用ABCDEF,abcdef)。八进制和十进制字面值可能是:int,long,long,unsigned long。在缺省情况下,他是最短类型,但能完整容纳该值。
eg3:字符常量(用单引号括起来),’\2666’,类型总是int。不能在后面添加unsigned或long后缀。 -
当一个字面值用于确定一个字中某些特定位的位置时,将它写成十六进制或者八进制更加合适;
eg:十进制:983040,十六进制:0xF000
其15-12位都是1
- 如果一个被当作字符使用,那么这个值表示为字符常量可以使这个值的意思更加清晰
value=value-‘0’
- 枚举类型enumerated
指:它的值为符号常量而不是字面值的类型。
eg1:
声明了一个Jay_type类型
enum Jay_type {CPU, PINT, QUART};
该类型变量的声明:
enum Jay_type milk, gas_can;
eg2:只使用一个声明
enum {CPU, PINT, QUART}
milk, gas_can;
这里的CPU=0,PINT=1,QUART=2,若由多,依次类推
eg3:
enum {CPU=8, PINT=16, QUART=32}
milk, gas_can;
只对部分符号用这种方式赋值也是合法的。入宫某个符号未显示地指定一个值,那么它地值就比前面一个符号的值大1.
(2)浮点类型
- 家族包括:float, double, long double类型。ANSI规定:long double至少和double一样长,double至少和float一样长。同时,规定了一个最小范围:所有浮点类型至少能容纳从10的-37次方到10的37次方的任何值。
浮点型总是写成十进制的形式,必须有一个小数点或整数;
eg:
3.14
1E0
25.
浮点数自在缺省情况下都是double类型的,若后面有个L或l,则是long double,若后面跟着F或f,则是float类型的
(3)指针
- 变量的值存储在计算机的内存中,每个变量都占据一个特定的位置。每个内存位置都有地址唯一确定并引用。
指针只是地址的另一个名字;指针变量是内存地址中的变量。 - 指针常量pointer constant:
C语言不存在指针常量pointer constant的概念,因为函数调用的时候,其局部变量的分配的内存位置都不相同。 - 字符串常量string literal
C语言字符串概念:是一串以NUL字节结尾的零个或多个字符。NUL作为字符串的终止符,是因为它不是一个可打印的字符。
eg:
“hello”
“”
- 使用字符串常量会生成一个指向字符的常量指针。字符串常量的直接值是一个指针,而不是字符本身。
当一个字符串常量出现于一个表达式中,表达式所使用的值是这些字符所存储的地址,而不是这些字符本身。所以,字符串常量可以赋值给一个指向字符的指针,但不能赋值给一个字符数组。
2.基本声明
- 变量声明的基本形式是:
说明符(一个或者多个) 声明表达式列表
- 相等的整型声明如下:
signed关键字一般只用于char类型,因为其他整型类型在缺省情况下都是有符号的。
同一个框内的所有声明都是等同的。
short, short int, signed short, signed short int | unsigned short, unsigned short int | – |
int, signed int, signed | unsigned int, unsigned | – |
long, long int, signed long,signed long int | unsigned long, unsinged long int | – |
-
浮点类型的声明除了long double以外,其余的说明符short,signed,unsigned都是不可用的
-
声明简单数组
int values[20];
常见解释:声明一个整型数组,数组包含20个整型元素
更好解释:名字values+一个下标,产生了一个int的值(共有20个整型值)
要让某个数组的下标从10开始,只要在实际引用时,把下标值减去10即可。
- 声明指针
eg1:
int *a;
表达式*a产生的结果类型是int;
a是一个指向int的指针;
eg2:
声明三个指针
int *a,*b,*c;e而不要用int* a,b,c;
后面的表示:a是指针,b和c是普通的整型
eg3:
char *message="hello word";
等价于:
char *message;
message="hello word";
意思是:把message声明为一个指向字符的指针,并用字符串常量中的第一个字符的地址对该指针进行初始化。
3.typedef
- 在定义复杂的类型名字时,如函数指针或指向数组的指针,typedef更为合适
eg1:
typedef char *point;
这个声明把标识符point作为指向字符的指针类型的新名字;
声明a是一个指向字符的指针;
point a;
eg2:
应该使用typedef而不是#define来创建新的类型名,因为后者无法正确地处理指针类型。
eg:
#define point char*
point a,b;
正确声明了a,b却声明为了一个字符;
4.常量:const和define
- 如何让变量一开始只有一个值?
方法:
(1)在声明变量的时候进行初始化:int const a=1;
(2)在函数声明为const的形参,在函数被调用时会得到实参的值;
(1)const靠谁近,谁就是常量
int const a;
const int a;
上述都是将a声明为一个整数,它的值不能被修改;
(2)int const *pci;
表示:指向整型常量的指针,可以修改指针的值,但是不能修改它所指向的值;
int *const pcil
表示:声明pci是一个指向整型的常量指针,此指针式常量,其值无法修改,但是可以修改它所指向的整型的值。
int const *const cpi;
表示:无论式指针本身还是他所指向的值都是常量,不允许修改。
(3)
创建名字常量:
#define MAX 50
int const max=50;
这种情况下,使用#define更好,因为const变量只能用于允许使用变量的地方;
5.作用域
- 代码块作用域
位于一对花括号之间的所有语句称为一个代码块。
任何在代码块的开始位置声明的标识符都具有代码块作用域block scope:表示他们可被整个代码块中的所有语句访问。
- 文件作用域
任何在所有代码块之外声明的标识符都具有文件作用域file scope。
它表示这些标识符从他们的声明之外直到它所在的源文件结尾处都可以访问。
注意:在头文件中编写并通过#include指令包含到其他文件中的声明就好像是直接写在那些文件中一样,他们的作用域并不局限于头文件的文件尾!!
- 原型作用域
只适用于函数原型中声明的参数名;
与函数的定义不同,在原型中,参数的名字非必须,可以随意取名字。
原型作用域的作用:防止这些参数名与其他部分的名字冲突。
- 函数作用域
int a;//文件作用域eg
int b(int c);//文件作用域eg,函数原型的eg中的参数c
int d(int e)////文件作用域eg:在文件中定义的函数名也具有文件作用域,因为函数名本身并不属于任何代码块
{
int f;
int g(int h);//函数原型的eg中的参数h
.......
{
int f,g,i;//代码块作用域eg:该声明f将隐藏上面的声明f
....
}
{
int i;
....
}
}
6.链接性
- external外部的
属于external链接属性的标识符不论声明多少次,位于几个源文件都表示同一个实体。 - internal内部的
属于internal链接属性的标识符在同一个源文件内的所有声明中都指向同一个实体,但是位于不同源文件的多个声明则分属于不同的实体。 - none无
没有链接属性的标识符none总是被当作单独的个体,即该标识符的多个声明被当作独立不同的实体 - eg
typedef char *a;
int b;
int c(int d)
{
int e;
int f(int g);
。。。
}
标识符b,c,f的链接性式external;其余的标识符的链接属性为none;
若另外一个源文件也包含了标识符b的类似声明并调用函数c,则他们实际上访问的式这个源文件所定义的实体。
f之所以式external的,是因为它是个函数名。
7.存储类型
- 变量的存储类型storage class指的是:存储变量值的内存类型。
变量的存储类型决定了变量何时创建,何时销毁,以及它的值将保持多久。 - 有三个地方可以存储变量:普通内存,运行时的堆栈,硬件寄存器。
(1)静态内存: 凡是在任何代码块之外声明的变量总是存储于静态内存中,即不属于堆栈的内存,这类变量称之为静态static变量。
特点是:静态变量在程序运行之前创建,在程序的整个执行期间始终存在。
它始终保存原先的值,除非给他赋一个不同的值或程序结束。
其值在程序开始执行前初始化一次。
若不指定其初始值,静态变量将初始化为0。
(2)自动auto变量: 在代码块内部声明的变量的缺省存储类型是自动的,即存储于堆栈中。
若给函数块内的变量加上static,则使他的存储类型从自动变成静态;
函数的形参不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归;
(3)寄存器变量: 关键字regiser可以用于自动变量的声明,提示他们应存储于机器的硬件寄存器而不是内存中,这类变量称之为寄存器变量。
寄存器变量的创建和销毁时间和自动变量相同,但是却要一些额外的工作。eg:当函数执行时,所有寄存器的内容保存在堆栈中,当函数返回时,这些值再复制回寄存器。
8.static关键字
- static声明的函数或变量,只能在声明他们的源文件中访问。
- static可以改修变量的存储类型:从自动变量修改为静态变量。
9.作用域,存储类型的eg
//a,b,c存储类型为静态的,不存储于堆栈中。eg:当程序开始执行时,变量a将初始化为5。
//a,b,c的作用域一直延伸到该源文件结束为止
int a=5;//a的链接性(缺省):extern
extern int b;//b的定义在其他地方,b的链接性:external
static int c;//c的链接性:internal,只能由源文件访问
//函数d的作用域从该行直接到文件结束
//函数d的定义对于后面想要调用它的函数而言,起到了函数原型的作用
//函数d的存储类型(缺省)是external的,其他源文件只要在文件上存在d的原型,就可以调用d
int d(int e)
{
//变量f,b,g作用域到函数结束为止,不具有链接属性,他们是局部变量
int f=15;//f的存储类型是自动的
register int b;//局部变量b将隐藏同名的静态变量,存储类型是寄存器类型,初始化值是垃圾
static int g=20;//存储类型是静态的,程序开始执行时,它被初始化为20,函数每次被调用时,他并不会被初始化
extern int a;
...
{
int e;//e不具有链接属性,是自动存储类型
int a;//局部变量b将隐藏同名的静态变量,a不具有链接属性,是自动存储类型
extern int h;//该声明并不需要,整个代码块位于第1行的声明的作用域内,全局变量h在该代码块内
可以被访问,存储于静态内存中
...
}
{
//x,e是两个局部变量:自动,无链接性,作用域限于本代码块内
int x;
int e;
...
}
...
}
//函数i具有静态链接属性,可以防止它被该源文件之外的任何函数调用。
//事实上,其他源文件也可能声明自己的函数i,于该源文件的i是不同的。
//i的作用域是从它声明的位置直到整个源文件结束。函数d不可以调用i,因为在d之前不存在i的原型!!
static int i()
{
...
}
10.作用域,链接性,存储类型总结
变量类型 | 声明的位置 | 是否存于堆栈 | 作用域 | 若声明为static |
全局 | 所有代码块之外 | 否 | 从声明处到文件尾 | 不允许从其他源文件访问 |
局部 | 代码块起始处 | 是 | 整个代码块 | 变量不存储于堆栈中,它的值在程序整个执行期一直保持 |
形式参数 | 函数头部 | 是 | 整个函数 | 不允许 |
– | – | – | – | – |
参考:<C和指针>