C语言数据类型和变量

数据类型介绍

C语言提供了丰富的数据类型来描述生活中的各种数据。使用整型类型来描述整数,使用字符类型来描述字符,使用浮点类型来描述小数。
所谓“类型”,就是相似的数据所拥有的共同特征,编译器只有知道了数据的类型,才知道怎么操作数据。

为什么要有数据类型?
假如说没有数据类型,那么现在有一个商城需要上架一本书,那么是不是要有书名,书的价格,书的出版社等信息。那么我们可以很快的知道书名肯定是一个文本,价格肯定是一串数字。但是计算机并不知道,而且计算机也不知道你想表达这是一本书。所以这个时候就需要数据类型,用字符类型组成的字符串来表示书名,计算机就知道这是一个字符串它应该怎么显示出来,怎么操作这个数据显示。用浮点类型来表示这本书的价格。因为一切数据在计算机中只是按照二进制的形式存储在计算机中,需要有数据类型告诉计算机来怎么识别处理这段二进制数据。

C语言各种数据类型 

C语言数据类型可以分为两类:

1.内置类型

  • 字符型
  • 整型
  • 浮点型
  • 布尔类型(C99标准引入)

2.自定义类型

  • 数组
  • 结构体-struct
  • 枚举-enum
  • 联合体-union 

本篇文章不探讨自定义类型,优先介绍C语言内置类型

字符型 
char //部分编译器下代表不一样,因为C语言没有规定char是不是等价于signed char,取决于当前系统
[signed] char //有符号char
unsigned char //无符号char

字符型在C语言中是char(全称character,意为字符。切记!声明字符类型变量时不可以使用character,只能使用char),signed unsigned 修饰符,下面会详细介绍的。

整型
//整型家族

//短整型
short [int]
[signed] short [int] //有符号short
unsigned short [int] //无符号short

//整型
int
[signed] int //有符号int
unsigned int //无符号int

//长整型
long [int]
[signed] long [int] //有符号long
unsigned long [int] //无符号long

//更长的整型  //C99中引入
long long [int]
[signed] long long [int] //有符号long long
unsigned long long [int] //无符号long long


//[]以及[]里面的内容可以不写 整型家族 有符号数据类型 等价于 数据类型。除int外整型家族都可以省略int的书写。

为什么会有整型家族呢?
因为在最初的时候计算机内存十分珍贵,为了让程序在设计的时候,不要过多的浪费内存。所以就出现了很多取值范围不同的整型数据类型。

 浮点型
//浮点型
float //单精度浮点型
double //双精度浮点型
long double //更高精度浮点型

浮点型是没有无符号的(unsigned)数据。这跟浮点型在内存中存储的形式有关。后面内存方面的文章会详细解释。这里暂时只需要记住浮点型在内存中是使用科学计数法存储的。且计算机会无法精确的保存这些小数。所以会有很多精度类型。

布尔类型 
//布尔型
_Bool //C语言中布尔型的定义是_Bool而不是bool。只不过C语言有把_Bool宏定义为bool了。布尔型只有两个值:true、false
true在C语言中的值是1,也就是非零,真
false在C语言中的值是0,也就是假

C语言中在C99标准之前是没有布尔类型的。而是使用整数0表示假,非零值表示真(此方法在C99标准之后一样可以使用)。即使在C99标准之后使用布尔类型也要引入头文件<stdbool.h>

 各种数据类型在内存中的长度

每一种数据类型都有自己的长度,使用不同的数据类型,能够创建出长度不同的变量,变量长度的不同,存储的数据范围就有所差异。那么怎么知道一个数据类型在内存中长度多少呢?

sizeof关键字

sizeof是一个关键字,在上篇文章中知道sizeof是计算数据类型或变量长度(即所占字节数)。那么这里就可以使用sizeof关键字来查看C语言内置类型在内存中的长度。

#include <stdio.h>

int main()
{
    //这里虽然说sizeof是关键字,但是在把sizeof和数据类型结合的时候要加上小括号,在要用sizeof查看变量或者表达式的长度的时候可以不加小括号。见下方举例
    /*int i = 0;
    printf("i在内存中的长度=%zd\n", sizeof i);*/
    printf("char在内存中的长度=%zd\n", sizeof(char));
    printf("_Bool在内存中的长度=%zd\n", sizeof(_Bool));
    printf("short在内存中的长度=%zd\n", sizeof(short));
    printf("int在内存中的长度=%zd\n", sizeof(int));
    printf("long在内存中的长度=%zd\n", sizeof(long));
    printf("long long在内存中的长度=%zd\n", sizeof(long long));
    printf("float在内存中的长度=%zd\n", sizeof(float));
    printf("double在内存中的长度=%zd\n", sizeof(double));
    printf("long double在内存中的长度=%zd\n", sizeof(long double));
    
    return 0;
}

tips:sizeof是有返回值的,返回值的类型是size_t,这里要在printf里面打印size_t类型的数据最好使用%zd占位符而不是%d占位符。 C语言中规定sizeof返回值类型必须要是一个无符号整数,并没有规定具体的类型,具体的类型而是留给系统自己去决定。sizeof到底返回什么类型。不同的系统中,返回值的类型可能是unsigned int,也有可能是unsigned long,甚至是unsigned long long,所以这里根据不同系统规定sizeof返回值类型就要使用对应的占位符,但是这样不利于程序的可移植性。
C语言(C99标准)提供了一个解决办法,创造了一个类型别名size_t,用来统一表示sizeof的返回值类型。对应当前系统的sizeof的返回值类型。这里size_t的占位符就是%zd。

运行上方代码得到如下结果

char在内存中的长度=1
_Bool在内存中的长度=1
short在内存中的长度=2
int在内存中的长度=4
long在内存中的长度=4
long long在内存中的长度=8
float在内存中的长度=4
double在内存中的长度=8
long double在内存中的长度=8

运行结果可以得知这些数据类型在内存中占用不同长度的字节。上面的结果可以发现int和long占用的字节数竟然是一样的,那么也就可以理解为long和int表示的取值范围一样呢?
C语言中规定:long的占用字节数要大于等于int。具体是大于还是等于取决于编译器。下面double和long double也是一样的。C语言只规定大于等于double,具体要取决于编译器。

1字节大约是多大呢?
计算机能够识别的是2进制(0/1),那么存放这个0/1的空间的单位就是bit,也就是计算机能够识别的最小单位。紧接着是Byte(字节),1Byte=8bit。再往上就是KB,MB,GB......。

sizeof中的表达式是不计算的

上面提到过sizeof也是可以计算变量和表达式在内存中所占的值。那么请看下面这段代码

#include <stdio.h>

int main()
{
	short s = 2;
	int b = 15;
	printf("%zd\n", sizeof(s = b + 10));
	//printf("%zd\n", sizeof s = b + 10);  err错误的书写方式
	printf("s = %d\n", s);//请问现在s的值是多少
}

运行上面的代码,得到输出结果s = 2。这个时候就会发现,sizeof计算表达式的数据类型长度时是不计算表达式的。也就是sizeof只计算了s数据类型的长度。而没有把b+10的值赋值给s。

signed和unsigned修饰符

C语言使用signed和unsigned关键字修饰字符型和整型类型。
signed关键字,表示一个类型带有正负号,包含负值;unsigned关键字,表示该类型不带有正负号,只能表示0和正整数。对于int类型默认是带有正负号也就是默认就有signed修饰。signed int 等同于 int。由于是默认情况,所以关键字signed一般都省略不写。但是写了也不会出错。

signed int //等同于 int

signed int a; //等同于 int a;

此时如果想让int类型不带有正负号,也就是只让此次声明的变量只有正整数和零,那就要使用unsigned修饰符,修饰int类型声明变量.

unsigned int a;//没有负数的整型变量a

为什么会有unsigned修饰符呢?
计算机程序是为了解决生活中的问题,那么生活中是不是会有一些数字只能是正整数呢?比如说身高,年龄,体重。所以当表示这些数字的时候要使用unsigned修饰符修饰数据类型(计算机中浮点数没有unsigned类型。这跟浮点数在计算机中存储形式有关,后面文章介绍。)
还有一个原因,数据在内存中以二进制形式保存,最高位作为这个数据的符号位,也就是正负位。举例:一个int类型的数据,占用4字节,32bit。那么它的最高位计算机在识别的时候有效的数据位只有0-30(内存中位是从0开始而不是1),第31位是这个数据的符号位。用来表示正负(0为正,1为负)。这样这个数据的有效取值范围就只有0-30位。但是此时这个数据是unsigned int类型那么它的最高位也就是第31位,也作为数据位。因为此时他只有正整数,根本不需要符号位也就多了一位的实际有效位。可以让取值范围更大,但是也意味着没有了负整数。

 tips:在声明unsigned int类型变量的时候,int可以省略的。unsigned a;等同于 unsigned int a;但是也只有int可以这样声明,其他的都必须要按照 修饰符 数据类型 变量名;所以这里还是建议申请unsigned int类型的时候,不要简写成unsigned。

字符类型char也可以设置signed和unsigned 修饰。

signed char c; //有符号char 不等同于 char c;取决于当前系统
unsigned char c; //无符号char

 注意,C语言规定char类型默认是否带有正负号,由当前系统决定。这就是说,char不等同于signed char,它有可能是signed char也有可能是unsigned char。但是这与int不同,int就等同于signed int。所以这里建议为了移植性考虑。在申请char类型的变量时,使用修饰符signed或unsigned(根据情况选择)。

数据类型的取值范围

上述的数据类型的取值范围规定在下面两个文件中。可以使用文件搜索软件找到这两个文件的所在位置:limits.h float.h。limits.h规定了整型家族的取值范围(char其实也算是整型),float.h存放浮点类型取值范围。为了代码的可移植性,需要知道每个数据类型的极限值,方便更好的使用这些类型。

SCHAR_MIN SCHAR_MAX :signed char 的最⼩值和最⼤值。
SHRT_MIN SHRT_MAX :short 的最⼩值和最⼤值。
INT_MIN INT_MAX :int 的最⼩值和最⼤值。
LONG_MIN LONG_MAX :long 的最⼩值和最⼤值。
LLONG_MIN LLONG_MAX :long long 的最⼩值和最⼤值。
UCHAR_MAX :unsigned char 的最⼤值。
USHRT_MAX :unsigned short 的最⼤值。
UINT_MAX :unsigned int 的最⼤值。
ULONG_MAX :unsigned long 的最⼤值。
ULLONG_MAX :unsigned long long 的最⼤值。

 变量

变量的创建

变量创建的语法形式要如下

data_type name;
    |       |
    |       |
数据类型   变量名;


//示例:
int age;//创建一个int类型变量 age;
signed char c;//创建一个signed char类型的变量,变量名c;
double weight;//创建一个double类型的变量,变量名weight;
int i = 0;//创建一个int类型变量,名字叫i,并初始化它的值为0
//在变量创建的时候就赋值叫做初始化。变量的创建和赋值可以不在一条语句内

什么是变量?
C语言中把经常变化的值称为变量,不变的值称为常量。举例:温度,就是一直变化的值。数字1就是常量它永远就是1无法改变。

 tips:在创建变量的时候最好直接把变量初始化了。因为局部变量(后续会说变量的种类)在创建的时候如果没有直接给他初始化,那么它的值就是随机的,不可控的。在一些编译器要求比较严格,如果一个局部变量没有被初始化或者使用赋值。是会报错的。如果你定义一个局部变量就立马赋值了,也可以先不初始化。

 变量的分类

C语言中变量的种类一共有两种:局部变量 和 全局变量

局部变量:局部变量,也称内部变量,是指在一个函数内部或复合语句内部定义的变量 [1]  。局部变量的作用域是定义该变量的函数或定义该变量的复合语句。局部变量的生存期是从函数被调用的时刻算起到函数返回调用处的时刻结束。通俗点说就是:在大括号内部定义的变量就是局部变量,局部变量的使用范围是比较局限,只能在自己所处的局部范围内使用

全局变量:全局变量既可以是某对象函数创建,也可以是在本程序任何地方创建。全局变量是可以被本程序所有对象或函数引用。通俗点说就是:在大括号外部定义的,且在整个工程内或者在整个程序内,定义的变量叫做全局变量,全局变量使用范围更广,整个工程中想使用,都是有办法使用的。

int b;//全局变量b,全局变量创建的时候计算机会自动初始化为0;所以这里可以不初始化。但是建议初始化
int main()
{
    int a = 0;//局部变量a
    return 0;
}

 全局变量和局部变量就像个人单车和共享单车一样。全局变量就像共享单车,只要生活在那个城市(工程)内人人都可以使用,局部变量就像你个人的单车,只有你自己或者你家里人可以使用。不是人人都能骑着你的单车就走吧。

tips:全局变量和局部变量可以重名(全局变量不可以和全局变量重名且不可以和关键字重名,局部变量同理。)。但是在调用同名变量的时候会优先使用局部变量。全局变量和局部变量可以重名与它们在内存中存储的位置有关。这里只是提一嘴,内存也分为很多个区域,栈区,堆区,静态区等等。全局变量是存放在静态区,局部变量是存放在栈区的。

---不要用人的思维去与计算机沟通,要把人的思维转换为计算机思维去与计算机沟通--- 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值