C语言数据类型与变量是基础内容,同时也是非常重要的内容,正如上篇所说,它是程序加工的“原料”,只有了解他们的运算规则,熟练运用它们,才能在以后的程序设计中编写出功能强大、健壮且高效的程序,本篇将继续介绍数据类型与变量相关的内容。
1数据类型的别名
1.1 typedef定义类型别名
typedef的使用方式为:
typedef 原类型 别名
typedef实例如下:
typedef char *ptr//声明ptr为指向char类型的指针变量
1.2 #define定义类型别名
#define使用方式如下:
#define 别名 原类型
#define实例如下:
#define PTR char *
1.3 typedef 与 #define区别
typedef一般用来定义关键字、冗长的类型的别名;#define为宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它在预编译阶段只是简单的字符替换。
两者区别:typedef定义比较彻底、#define定义比较局限,如下:
typedef char *ptr;
ptr p1,p2,p3;//定义三个指向char类型的指针变量p1、p2、p3
#define PTR char *;
PTR p1,p2,p3;
由于#define定义只是简单的字符替换,所以“PTR p1,p2,p3”相当于“char *p1,p2,p3”,因此这条语句其实是定义了指向char类型的指针变量p1以及char变量p2、p3......
2.常量
2.1 const定义常量
const用于定义常量,除定义时对const变量赋初值,否则不能再对const变量赋值,如:
int const tmp;
tmp = 3;
定义常量tmp并赋值为3,但是报错,因为const变量不能在声明之后再对其赋值。
易混淆定义形式分析:
int const tmp;
const int tmp;
上面两种定义表示的内容一致,都是定义整型的只读变量。
int const *ptr;
ptr为指向整型常量的指针,即ptr地址指向的内容不能更改,但ptr本身可改变。
int * const ptr;
声明ptr为常量指针,此时指针不能修改,但指针指向的整型变量值可以修改。
int const * const ptr;
该例中,无论指针还是指针指向的变量都是只读的,不允许修改。
2.2 #define定义常量 与const常量区别
#define MAX_ELEMENTS 50;
int cosnt max_elements = 50;
#define定义的常量名一帮为全大写,而const定义常量则无此原则。
上面例子中,使用#define定义常量要比const定义变量好,因为只要允许使用字面值常量的地方均可以使用前者,如声明数组长度;而const只能适用于允许使用变量的地方。
3.作用域
标识符的作用域就是程序中该标识符可以被使用的区域,这也就意味着不同作用域的同名标识符表示不同的含义,因此不同的作用域定义相同的标识符是完全合法的。
标识符声明的位置决定了它的作用域,主要有一下四种作用域:
文件作用域、函数作用于、代码块作用域、原型作用域
3.1代码块作用域
所有位于一对花括号之间的标识符都具有代码块作用域,表示这些标识符只能在该花括号中被识别。
3.2文件作用域
所有在代码块之外声明的标识符都具有文件作用域,表示这些标识符从他们声明开始,知道文件结尾处都可以访问。
3.3原型作用域
原型作用域适用于在函数原型中声明的参数名,它允许你在声明和定义函数时使用不同的参数名称,但都表达同样的含义,如:
int add(int a,int b);
int add(int c,int d){语句块;}
上面表示同样的函数,前者表示函数add的声明,后者表示add函数的定义。
3.4函数作用域
函数作用域表示标识符在该函数内有效。
4.链接属性
链接属性决定如何处理不同文件中出现的标识符,它表示不同位置声明的同名标识符是否具有联系(即是否代表同一个标识符)。
链接属性一共三种--external(外部)、internal(内部)、none(无),其中:
external(外部):表示位于不同源文件的相同标识符表示同一个实体。
internal(内部):表示位于不同源文件中的相同标识符表示不同的实体,同一源文件中的相同标识符表示相同的实体。
none(无):表示该标识符的所有同名标识符表示不同的实体。
关键字extern与static用于在声明中修改标识符的链接属性。如果某个标识符在正常情况下具有external链接属性,在它前面加上static关键字可以使他的链接属性变为internal。
【注意】static可改变external的链接属性为internal;但是extern不能改变static修饰的internal链接属性为external。一下对后者(extern不能改变static声明的链接属性)举例,至于前者可自行运行下看看效果。
static int i;
extern int i;
经过上述两条执行语句后,标识符i的链接属性是external还是internal呢?答案是:“static”,即后来对标识符i声明的链接属性external将不起作用,以下写程序验证一下:
定义两个文件111.c 与 222.c:
/*
* 文件名:111.c
* 程序功能:输出变量i的值
*/
#include <stdio.h>
//static int i =1;
extern int i;
int main(void)
{
printf("i = %d\n",i);
return 0;
}
/*
* 文件名:222.c
* 功能:定义变量i的取值
*/
int i = 3;
程序运行结果:
此时运行程序,结果为“i = 3”,这点不难理解,因为extern表链接属性为external(即不同文件的表示符i表示同一标识符),所以文件111.c输出的变量i的值其实就是在文件222.c中定义的变量值。
然后将上述源码文件111.c中的第七行注释拿掉,如下:
程序运行结果:
直观上认为,虽然程序中先对变量i进行了static声明,但在其后又对其进行了extern声明,变量i的取值应该为3才对。之所以出现这种情况,正验证了上面得出的结论:
extern不能改变由static修饰的internal链接属性。
5.存储类型
变量的存储类型是指存储变量的内存类型。变量可存储于:普通内存、运行时堆栈、硬件寄存器。
变量的缺省存储类型取决于它被声明的位置。
变量的存储类型有动态与静态之分。
动态变量:
在代码块内部声明的变量缺省为动态变量,它门存在于堆栈中,当程序执行到该变量时变量才被创建,当代码块运行完毕时变量作用域结束,变量被自动销毁。当下次再执行到该语句块时,变量再次重复“创建-销毁”的流程。【注意:】多次执行语句块中的变量时,对变量的创建不保证位置上的相同,因此你不能试图保留该变量的位置在语句块外对其进行访问。
静态变量:
在代码块外声明的变量以及在代码块内通过关键字static声明的变量属于静态变量。静态存储类型的变量在整个程序生命周期内一直有效,而不仅仅局限于声明变量的作用域。
6.总结
作用域:
作用域表示空间概念,表示程序可以被识别的地方,如程序中有A与B两个代码块,那么A中定义的变量只能在A中被访问,B中则无法访问。你或许会说,A、B中分别定义同名变量不就实现了A中变量能在B中访问,B变量能在A中访问了吗?非也,从本质上来说,不同语句块中的同名变量就不是同一个变量。
存储属性:
存储属性表示时间概念,表示变量在何时被创建、何时销毁。
链接属性:
链接属性表示相同或不同文件中的同一标识符(变量、函数)本质上是否是同一个对象。
【注意】
1.当对标识符进行访问时,只有当其满足了作用域(空间概念)、存储属性(时间概念)上的要求。如下代码:
/*
* 文件名:333.c
* 程序功能:验证作用域与存储属性对程序的影响
*/
#include <stdio.h>
int i = 5;
int main(void)
{
{
static int i =2;
}
printf("i = %d\n",i);
}
本实例程序中,程序运行结果如下: