基本数据类型
数据类型是程序的基础:他告诉我们数据的意义以及我们能在数据上执行的操作。
C语言中定义了很多数据类型,有基本数据类型,自定义的构造数据类型(C语言中的结构体,C++中的类),还有指针类型.......这次主要讲的是基本数据类型,结构体和指针会在后面进行复习阐述。
不同的基本数据类型所能寻址的范围也不一样,在内存中占用的字节数也不一样。
带符号类型和无符号类型
(1)有符号数
有符号数最高位为符号位,0代表正数,1代表负数,所有的整型数据类型都默认是有符号数。
(2)无符号数
无符号数最高位不是符号位,而是数的一部分,无符号数不可能是负数。
(3)有符号和无符号整型取值范围
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | -32768 到 32767 (-215 ~ 215-1) |
int | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
long | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
unsigned short | 2字节 | 0 到 65535 (0 ~ 216-1) |
unsigned int | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
unsigned long | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
以char类型为例:signed char(有符号字符型):
1 0000000 ----1 1111111 ---> -0 -- -127
0 0000000 ---- 0 1111111 ---> +0 -- +127 这里+0和-0都表示的同一个数字0,所以就有了一个规定,将有符号数的-0全部表示成 -2^n(n是该类型的位数),本例中char类型占8位,所以-0就表示成-128。
unsigned char(无符号字符型):
0 0000000 ---- 11111111 --->0 -- 256 可以看出,有符号数和无符号数能够表示的数的个数是相同的,但是表示的数的范围不相同。
类型转换
自动类型转换
自动类型转换是编译器默认的、隐式的进行的数据类型转换,这种转换不需要程序员的干预,会自动发生。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告,例如将一个double类型的数据转化为int类型的数据,double占8个字节,int占四个字节,强制类型转换其实就是把double多余的部分剪掉,让8字节的数强制去掉很多位数编程4字节的数,必然会造成精度缺失。
在不同类型的混合运算中,编译器也会自动地转换数据类型,将参与运算的所有数据先转换为同一种类型,然后再进行计算。转换的规则如下:
- 转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算。
- 所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型,也要先转换为 double 类型,才能进行运算。
- char 和 short 参与运算时,必须先转换成 int 类型。
强制类型转换
自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。
自动类型转换是编译器默默地、隐式地进行的一种类型转换,不需要在代码中体现出来;强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。
强制类型转换的格式为:
(type_name) expression
type_name
为新类型名称,expression
为表达式。例如:
(float) a; //将变量 a 转换为 float 类型
(int)(x+y); //把表达式 x+y 的结果转换为 int 整型
(float) 100; //将数值 100(默认为int类型)转换为 float 类型
注意事项
-
切勿混用带符号类型和无符号类型
如果表达式里面既有带符号类型又有无符号类型,当带符号类型取值为负数的时候就会出现异常结果,这是因为带符号数会自动的转化为无符号数。
int i = -42; unsigned u = 10; printf("i+i = %d\n", i + i);//输出-84 printf("i+u = %d\n", i + u);//输出-32 printf("i+u = %u", i + u);//输出4294967264
从上面代码可以看出如果要求u+i输出的是有符号数,结果没问题;但是要求u+i输出的是无符号数,那么i就会自动转化为无符号数(i的最高位不再表示正负)。这两句看似在C语言中没有问题,但是如果在C++中直接输出u+i,最后输出的就会是那个无符号数,需要注意一下。
变量
在程序运行过程中,其值可以改变的量。
1.变量的定义
`type variable_list;`
type 必须是一个有效的 C 数据类型,可以是 char、w_char、int、float、double 或任何用户自定义的对象。
variable_list 可以由一个或多个标识符名称组成
多个标识符之间用逗号分隔。
例如:
int i, j, k;
char c, ch;
float f, salary;
double d;
2.变量初始化
变量提供一个具体的、可供程序操作的存储空间。在C++中每个变量都有其数据类型,数据类型决定了变量所占空间的大小和布局方式、该控件能存储的值的范围,以及变量能参与的运算。对于C++来说,“变量”和“对象”一般可以互换使用。例如:C++中有String数据类型,而String数据类型其实就是char封装的类模板,所以这些基本的数据类型实际上就是一个个封装好的类模板。
初始化不是赋值,初始化的含义是创建变量的时候赋予其一个初始值,而赋值的含义是又把对象的当前值擦除,而以一个新值来代替。
type variable_name = value;
例如:
extern int d = 3, f = 5; // d 和 f 的声明与初始化
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
注意:
- 如果定义变量的时候没有指定初值,则变量被默认初始化,此时变量被赋予了“默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。
默认初始化
默认初始化,顾名思义,即为在定义变量时如果没有为其指定初始化值,则该变量会被编译器赋予默认的值。而变量被赋予的默认值到底是什么,则取决于变量的数据类型和变量的定义位置。
(1)规则1:
内置类型的变量如果初始化,则它的默认初始化值取决于定义它的位置
- 定义在任何函数之外的未初始化的内置类型变量(也就是全局变量)会被默认初始化为0
#include<iostream>
using namespace std;
int n;
double d;
int main(){
cout<<"int类型的全局变量的默认初始化值:"<<n<<endl;
cout<<"double/float类型的全局变量的默认初始化值:"<<d<<endl;
return 0;
}//默认初始化为0
- 定义在函数体内部的(包括main函数)未初始化的内置类型变量(也就是局部变量)的默认初始值是未定义的(也就是一个随机数)。如果试图拷贝或以其他方式访问该变量的值,此时会引发编译错误
#include<iostream>
using namespace std;
int main() {
int n;
double value;
cout << n << " " << value << endl; //错误
return 0;
}//编译报错
(2)规则2:
未初始化的内置类型的全局变量的默认初始化值还取决于变量的数据类型
- 数值数据类型的未初始化全局变量的默认初始值为0
#include<iostream>
using namespace std;
short a;
int b;
long c;
long long d;
float e;
double f;
int main() {
cout << "short类型的默认初始值为:" << a << endl;
cout << "int类型的默认初始值为:" << b << endl;
cout << "long类型的默认初始值为:" << c << endl;
cout << "long long类型的默认初始值为:" << d << endl;
cout << "float类型的默认初始值为:" << e << endl;
cout << "double类型的默认初始值为:" << f << endl;
system("pause");
return 0;
}//全部输出0
- bool类型的未初始化的全局变量的默认初始化值为false(也就是0)
#include<iostream>
using namespace std;
bool flag;
int main() {
cout << "bool类型的默认初始值为:" << flag << endl;
system("pause");
return 0;
}//输出0
- char类型的未初始化的全局变量的默认初始化值为‘\0’(ASCII码值为0)
#include<iostream>
using namespace std;
char c;
int main() {
if (c == '\0') {
cout << "char类型的默认初始值为\'\\0\'" << endl;
}
else {
cout << "char类型的默认初始值不是\'\\0\'" << endl;
}
system("pause");
return 0;
}//输出0
- string类型(姑且当成内置类型)的未初始化的全局变量的默认初始值为“”
#include<iostream>
#include<string>
using namespace std;
string str;
int main() {
if (str == "") {
cout << "string类型的默认初始值为\"\"" << endl;
}
else if(str==" ") {
cout << "string类型的默认初始值是\" \"" << endl;
}
else {
cout << "string类型的默认初始值既不是\"\",也不是\" \"" << endl;
}
system("pause");
return 0;
}
(3)规则3:
静态变量无论是全局变量还是局部变量,编译器都会给其默认初始化值,值为多少取决于变量的数据类型
#include<iostream>
using namespace std;
static int value1;
int main() {
static int value2;
cout << "全局静态变量的默认初始化值为:" << value1 << endl;//输出0
cout << "局部静态变量的默认初始化值为:" << value2 << endl;//输出0
system("pause");
return 0;
}
(4)规则4:
指针类型的全局未初始化的变量的默认初始值为NULL,而指针类型的局部未初始化变量的默认值这是未定义的(在有些编译器下定义为初始化的局部指针变量会报错)
#include<iostream>
using namespace std;
int* ptr;
int main() {
int* local_ptr;
if (ptr == NULL) {
cout << "全局指针变量的默认初始值为NULL" << endl;
}
else {
cout << "全局指针变量的默认初始值不为NULL" << endl;
}
if (local_ptr == NULL) {
cout << "局部指针变量的默认初始值为NULL" << endl;
}
else {
cout << "局部指针变量的默认初始值不为NULL" << endl;
}
return 0;
}
PS:建议对所有的变量在其定义的时候就对其进行初始化,这样可以避免许多无意的错误
3.变量的声明和定义
变量的声明
变量的声明有两种情况:
-
一种是需要建立存储空间的,例如int j。
-
另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。除非有extern关键字,否则都是变量的定义。例如extern int i。
extern int i;//声明i而非定义i
int j;//声明并且定义j
变量如果用extern对变量进行声明实际上就是告诉编译器,我有这个变量,但是并没有在内存中开辟空间。而定义则是真正建立了内存空间的。
如果需要在一个源文件中引用另外一个源文件中定义的变量,我们只需在引用的文件中将变量加上 extern 关键字的声明即可。
变量声明和定义的区别(总结)
-
变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。
-
变量声明:用于向程序表明变量的类型和名字。
-
定义也是声明:当定义变量时我们声明了它的类型和名字。
-
extern 声明不是定义:通过使用 extern 关键字声明变量名而不定义它。
-
一个文件不允许在同一个区域内定义两个相同的变量。
注意:变量在使用前就要被定义或者声明。在一个程序中,变量只能定义一次,却可以声明多次。定义分配存储空间,而声明不会。
4.变量在内存中的地址
-
内存寻址由大到小,优先分配内存地址比较大的字节给变量,所以说变量越先定义,内存地址就越大。
-
变量地址的获取方式:& 变量名。
-
输出地址的方式:%p。
常量
在程序运行过程中,其值不能被改变的量。
1.常量的定义
使用 #define 预处理器
使用 const 关键字
#define LENGTH 10 //用define定义常量
const int LENGTH = 10;//用const关键字定义常量
define和const的区别
(1)const
- const定义的是常量不是变量,只是这个变量的值不允许改变是常变量(是可以取地址的)!
- const实际上是把该变量的内存变为只读,不允许修改变量内存存储的内容。
(2)define
- define 定义的是不带类型的常数,只进行简单的字符替换,在预编译的时候起作用,它不能定义常量。
- 但宏定义可以实现在字面意义上和其它定义常量相同的功能,本质的区别就在于 ,define 不为宏名分配内存。
2.常量在内存中的地址
- 用#define定义的宏,此时其实它是立即数,在编译的时候,会直接写入程序不会占空间,立即数存储在寄存器当中,所以对define类型的量取地址会编译报错。
- const关键字定义的常量实际上是常变量,在内存中把变量的内存设置为只读模式,所以对const修饰的常量可以进行取地址操作。
- C/C++存在文字常量区,保存的是"hello world"这类字符串,是可以进行取地址操作的。
- 对于1、2、3、'a'这类的数,它们是存放在寄存器上的,没有所谓的内存地址,所以对这一类常量取地址会报错。