📄 第2章 变量和数据存储.txt
字号:
for(f=first; f<last; f+=small)
;
你甚至可以预先算出循环次数,然后通过这个整数进行循环计数:
float f;
int count=(last-first)/small;
for(f=first;count-->0;f+=small)
;
请参见:
2.11 对不同类型的变量进行算术运算会有问题吗?
2.10 怎样判断一个数字型变量可以容纳的最大值?
要判断某种特定类型可以容纳的最大值或最小值,一种简便的方法是使用ANSI标准头文件limits.h中的预定义值。该文件包含一些很有用的常量,它们定义了各种类型所能容纳的值,下表列出了这些常量:
----------------------------------------------------------------
常 量 描 述
----------------------------------------------------------------
CHAR—BIT char的位数(bit)
CHAR—MAX char的十进制整数最大值
CHAR—MIN char的十进制整数最小值
MB—LEN—MAX 多字节字符的最大字节(byte)数
INT—MAX int的十进制最大值
INT—MIN int的十进制最小值
LONG—MAX long的十进制最大值
LONG—MIN long的十进制最小值
SCHAR—MAX signedchar的十进制整数最大值
SCHAR—MIN signedchar的十进制整数最小值
SHRT—MIN short的十进制最小值
SHRT—MAX short的十进制最大值
UCHAR—MAX unsignedchar的十进制整数最大值
UINT—MAX unsignedint的十进制最大值
ULONG—MAX unsignedlongint的十进制最大值
USHRT—MAX unsignedshortint的十进制最大值
-----------------------------------------------------------------
对于整数类型,在使用2的补码运算的机器(你将使用的机器几乎都属此类)上,一个有符号类型可以容纳的数字范围为-2位数-1到(+2位数-1-1),一个无符号类型可以容纳的数字范围为0到(+2位数-1)。例如,一个16位有符号整数可以容纳的数字范围为--215(即-32768)到(+215-1)(即+32767)。
请参见:
10.1用什么方法存储标志(flag)效率最高?
10.2什么是“位屏幕(bitmasking)”?
10.6 16位和32位的数是怎样存储的?
2.11 对不同类型的变量进行算术运算会有问题吗?
C有三类固有的数据类型:指针类型、整数类型和浮点类型;
指针类型的运算限制最严,只限于以下两种运算:
- 两个指针相减,仅在两个指针指向同一数组中的元素时有效。运算结果与对应于两个指针的数组下标相减的结果相同。
+ 指针和整数类型相加。运算结果为一个指针,该指针与原指针之间相距n个元素,n就是与原指针相加的整数。
浮点类型包括float,double和longdouble这三种固有类型。整数类型包括char,unsigned char,short,unsigned short,int,unsigned int,long和unsigned long。对这些类型都可进行以下4种算术运算:
+ 加
- 减
* 乘
/ 除
对整数类型不仅可以进行上述4种运算,还可进行以下几种运算:
% 取模或求余
>> 右移
<< 左移
& 按位与
| 按位或
^ 按位异或
! 逻辑非
~ 取反
尽管C允许你使用“混合模式”的表达式(包含不同类型的算术表达式),但是,在进行运算之前,它会把不同的类型转换成同一类型(前面提到的指针运算除外)。这种自动转换类型的过程被称为“运算符升级(operator promotion)”。
请参见:
2.12什么是运算符升级(operatorpromotion)?
2. 12 什么是运算符升级(operatorpromotion)?
当两个不同类型的运算分量(operand)进行运算时,它们会被转换为能容纳它们的最小的类型,并且运算结果也是这种类型。下表列出了其中的规则,在应用这些规则时,你应该从表的顶端开始往下寻找,直到找到第一条适用的规则。
-------------------------------------------------------------
运算分量1 运算分量2 转换结果
-------------------------------------------------------------
long double 其它任何类型 long double
double 任何更小的类型 double
float 任何更小的类 float
unsigned long 任何整数类 unsigned long
long unsigned>LONG_MAX unsigned long
long 任何更小的类型 long
unsigned 任何有符号类型 unsigned
-------------------------------------------------------------
下面的程序中就有几个运算符升级的例子。变量n被赋值为3/4,因为3和4都是整数,所以先进行整数除法运算,结果为整数0。变量f2被赋值为3/4.0,因为4.0是一个float类型,所以整数3也被转换为float类型,结果为float类型0.75。
#include <stdio.h>
main ()
{
float f1 = 3/4;
float f2 = 3/4.0
printf("3/4== %g or %g depending on the type used. \n",f1, f2);
}
请参见:
2.11对不同类型的变量进行算术运算会有问题吗?
2.13什么时候应该使用类型强制转换(typecast)?
2.13 什么时候应该使用类型强制转换(typecast)?
在两种情况下需要使用类型强制转换。第一种情况是改变运算分量的类型,从而使运算能正确地进行。下面的程序与2.12中的例子相似,但有不同之处。变量n被赋值为整数i除以整数j的结果,因为是整数相除,所以结果为0。变量f2也被赋值为i除以j的结果,但本例通过(float)类型强制转换把i转换成一个float类型,因此执行的是浮点数除法运算(见2.11),结果为0.75。
#include <stdio.h>
main ( )
{
int i = 3;
int j = 4
float f1 =i/j;
float f2= (float) i/j;
printf("3/4== %g or %g depending on the type used. \n",f1, f2);
}
第二种情况是在指针类型和void * 类型之间进行强制转换,从而与期望或返回void指针的函数进行正确的交接。例如,下述语句就把函数malloc()的返回值强制转换为一个指向foo结构的指针:
struct foo *p=(struct foo *)malloc(sizeof(struct foo));
请参见:
2.6什么时候应该使用volatile修饰符?
2.8什么时候应该使用const修饰符?
2.11对不同类型的变量进行算术运算会有问题吗?
2.12 什么是运算符升级(operator promotion)?
2.14 什么时候不应该使用类型强制转换(typecast)?
7.5 什么是void指针?
7.6 什么时候使用void指针?
7.21 什么是堆(heap)?
7.27 可以对void指针进行算术运算吗?
2.14 什么时候不应该使用类型强制转换(typecast)?
不应该对用const或volatile说明了的对象进行类型强制转换,否则程序就不能正确运行。
不应该用类型强制转换把指向一种结构类型或数据类型的指针转换成指向另一种结构类型或数据类型的指针。在极少数需要进行这种类型强制转换的情况下,用共用体(union)来存放有关数据能更清楚地表达程序员的意图。
请参见:
2. 6什么时候应该使用volatile修饰符?
2. 8什么时候应该使用const修饰符?
2. 15 可以在头文件中说明或定义变量吗?
被多个文件存取的全局变量可以并且应该在一个头文件中说明,并且必须在一个源文件中定义。变量不应该在头文件中定义,因为一个头文件可能被多个源文件包含,而这将导致变量被多次定义。如果变量的初始化只发生一次,ANSIC标准允许变量有多次外部定义;但是,这样做没有任何好处,因此最好避免这样做,以使程序有更强的可移植性。
注意:变量的说明和定义是两个不同的概念,在2.16中将讲解两者之间的区别。
仅供一个文件使用的“全局”变量应该被说明为static,而且不应该出现在头文件中。
请参见:
2. 16 说明一个变量和定义一个变量有什么区别?
2. 17 可以在头文件中说明static变量吗?
2.16 说明一个变量和定义一个变量有什么区别?
说明一个变量意味着向编译程序描述变量的类型,但并不为变量分配存储空间。定义一个变量意味着在说明变量的同时还要为变量分配存储空间。在定义一个变量的同时还可以对变量进行初始化。下例说明了一个变量和一个结构,定义了两个变量,其中一个定义带初始化:
extern int decll; / * this is a declaration * /
struct decl2 {
int member;
} ; / * this just declares the type--no variable mentioned * /
int def1 = 8; / * this is a definition * /
int def2; / * this is a definition * /
换句话说,说明一个变量相当于告诉编译程序“在程序的某个位置将用到一个变量,这里给出了它的名称和类型”,定义一个变量则相当于告诉编译程序“具有这个名称和这种类型的变量就在这里”。
一个变量可以被说明许多次,但只能被定义一次。因此,不应该在头文件中定义变量,因为一个头文件可能会被一个程序的许多源文件所包含。
请参见;
2.17可以在头文件中说明static变量吗?
2.17 可以在头文件中说明static变量吗?
如果说明了一个static变量,就必须在同一个文件中定义该变量(因为存储类型修饰符static和extern是互斥的)。你可以在头文件中定义一个static变量,但这会使包含该头文件的源文件都得到该变量的一份私有拷贝,而这通常不是你想得到的结果。
请参见:
2.16 说明一个变量和定义一个变量有什么区别?
2.18 用const说明常量有什么好处?
使用关键字const有两个好处;第一,如果编译程序知道一个变量的值不会改变,编译程.序就能对程序进行优化;第二,编译程序会试图保证该变量的值不会因为程序员的疏忽而被改变。
当然,用#define来定义常量也有同样的好处。用const而不用#define来定义常量的原因是const变量可以是任何类型(如结构,而用#define定义的常量不能表示结构)。此外,const变量是真正的变量,它有可供使用的地址,并且该地址是唯一的(有些编译程序在每次使用用#define定义的字符串时都会生成一份新的拷贝,见9.9)。
请参见:
2.7 一个变量可以同时被说明为const和volatile吗?
2.8 什么时候应该使用const修饰符?
2.14 什么时候不应该使用类型强制转换(typecast)?
9.9 字符串和数组有什么不同?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -