C语言深度总结[全面认识强类型语言及赋值语句]

C 中的左值(Lvalues)和右值(Rvalues)

C 中有两种类型的表达式:

    (1)左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。

    (2)右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。

变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。

下面是有效的语句:

int tag = 10;

int bigInt = 2147483647 + 1;

char age = 10;

char name = 'A';

double core = 98.5;

double myCore = 98;

float money = 99.99;

float myMoney = 99;

int *ptr = 0;

//*ptr = 1; //知道这里为何可以编译通过,但是运行却出现段错误吗

int *ptr1 = 10;

int *ptr1 = NULL;

//*ptr1=100; //知道这里为何可以编译通过,但是运行却出现段错误吗

int *ptr2 = &tag;

char buff[10];

char *myBuff = buff;

10 = 10; 10=9; //作为左值不能是常量?No,const int MAX = 100

如何创造空间,使其编译可以通过呢?

简单做法:

int a10 = 10;

int a[10] = {10};

聪明厉害的你,有更好的做法吗?等你来战!

C 常量

常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。

常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。

常量就像是常规的变量,只不过常量的值在定义后不能进行修改。

整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

定义常量

在 C 中,有两种简单的定义常量的方式:

使用 #define 预处理器。

使用 const 关键字。

注意:严格来讲#define 是宏定义,它不能定义常量,但宏定义可以实现在字面意义上和其它定义常量相同的功能,本质的区别就在于 #define 不为宏名分配内存,而 const 也不为常量分配内存,怎么回事呢,其实 const 并不是去定义一个常量,而是去改变一个变量的存储类,把该变量所占的内存变为只读

const 定义的是变量不是常量,只是这个变量的值不允许改变是常变量!带有类型。编译运行的时候起作用存在类型检查。

define 定义的是不带类型的常数,只进行简单的字符替换。在预编译的时候起作用,不存在类型检查。

1、两者的区别

(1) 编译器处理方式不同

#define 宏是在预处理阶段展开。

 const 常量是编译运行阶段使用。

(2) 类型和安全检查不同

 #define 宏没有类型,不做任何类型检查,仅仅是展开。

 const 常量有具体的类型,在编译阶段会执行类型检查。

(3) 存储方式不同

#define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)

const常量会在内存中分配(可以是堆中也可以是栈中)。

(4) const 可以节省空间,避免不必要的内存分配。

例如:

#define NUM 3.14159 //常量宏

const double Num = 3.14159; //此时并未将Pi放入ROM中 ......

double i = Num; //此时为Pi分配内存,以后不再分配!

double I= NUM; //编译期间进行宏替换,分配内存

double j = Num; //没有内存分配

double J = NUM; //再进行宏替换,又一次分配内存!

const 定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象 #define 一样给出的是立即数,所以,const 定义的常量在程序运行过程中只有一份拷贝(因为是全局的只读变量,存在静态区),而 #define 定义的常量在内存中有若干个拷贝。

(5) 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

(6) 宏替换只作替换,不做计算,不做表达式求解;

宏预编译时就替换了,程序运行时,并不分配内存。

总结赋值语句充要条件:

赋值语句两边类型必须严格一致或编译器可做类型的隐式转化

左值必须有空间,且大小合适、地址合法;


C 中的变量声明

变量声明向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。

变量的声明有两种情况:

1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。

2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。

除非有extern关键字,否则都是变量的定义。

extern int batbattle; //声明,不是定义

int batbattle; //声明,也是定义

extern int a;    // 声明一个全局变量 a

int a;            // 定义一个全局变量 a

extern int a =0;  // 定义一个全局变量 a 并给初值。一旦给予赋值,一定是定义,定义才会分配存储空间

int a =0;        //定义一个全局变量 a,并给初值

声明之后你不能直接使用这个变量,需要定义之后才能使用。

第四个等于第三个,都是定义一个可以被外部使用的全局变量,并给初值。

糊涂了吧,他们看上去可真像。但是定义只能出现在一处。也就是说,不管是 int a 还是 int a=0 都只能出现一次,而那个 extern int a 可以出现很多次。

当你要引用一个全局变量的时候,你就要声明 extern int a 这时候 extern 不能省略,因为省略了,就变成 int a 这是一个定义,不是声明。

Demo实例演示

尝试下面的实例,其中,变量在头部就已经被声明,但是定义与初始化在主函数内:

#include<stdio.h> 

 // 变量声明extern int a, b;extern int c;extern float f;

int main (){  /* 变量定义 */  int a, b;

  int c;

  float f;

  /* 初始化 */  a = 10;

  b = 20;

  c = a + b;

  printf("value of c : %d \n", c);

  f = 70.0/3.0;

  printf("value of f : %f \n", f);

  return 0;

}

推荐阅读更多精彩内容