引言
程序对数据进行操作,需要了解数据各种类型、特点及如何声明;同时还会理解变量的三者属性:做用域、链接属性和存储类型,这三个属性决定一个变量的“可视性”(在什么地方使用)和“生命期”(值可以保持多久)。
基本数据类型
在C语言中,仅有4种基本数据类型---整数、浮点型、指针和聚合类型(如数组和结构体等),其他的类型都是在4中基本数据类型的某种组合派生而来。
整型家族包括字符、短整型、整型、长整型,且分为有符号(signed)和无符号(unsigned)两种;
规定整型值互相之间大小规则:
长整型至少应该和整型一样长,而整型至少和短整型一样长。
ANSI标准加入一个规范,说明各种整型值的最小允许范围:
类型 | 最小范围 |
char | 0~127 |
signed char | -127~127 |
unsigned char | 0~255 |
short int | -32767~32767 |
unsigned short int | 0~65535 |
int | -32767~32767 |
unsigned int | 0~65535 |
long int | -2146483647~2146483647 |
unsigned long int | 0~4294967295 |
各个变量在内存中所占空间
#include<stdio.h>
int main(){
char c;
int a = 10;
long b = 20;
char *p;
int *q;
printf("sizeof(char):%d\n",sizeof(c));
printf("sizeof(int):%d\n",sizeof(a));
printf("sizeof(long):%d\n",sizeof(b));
printf("sizeof(float):%d\n",sizeof(float));
printf("sizeof(double):%d\n",sizeof(double));
printf("char *p: %d\n",sizeof(p));
printf("int *q : %d\n",sizeof(q));
return 0;
}
64位系统运行结果
sizeof(char):1
sizeof(int):4
sizeof(long):4
sizeof(float):4
sizeof(double):8
char *p: 8
int *q : 8
32位系统运行结果
sizeof(char):1
sizeof(int):4
sizeof(long):4
sizeof(float):4
sizeof(double):8
char *p: 4
int *q : 4
注意:指针变量在32位系统占4个字节,在64位系统8个字节;
常量与变量
首先看C语言中的关键字:
C的关键字共有32个
1)数据类型关键字(12)
char,short,int,long,float,double,unsigned,signed,struct,union,enum,void
2)控制语句关键词(12)
if,else,switch,case,default,for,do,while,break,continue,goto,return,
3)存储类关键词(5)
auto,extern,register,static,const,
4)其他关键词
sizeof,typedef,volatile
数据类型
作用:编译器预算对象(变量)分配的内存空间大小
常量
程序运行中,其值不能被改变,且常量一般出现在表达式或赋值语句中
整型常量 | 100,200,-100,0 |
实型常量 | 3.14 , 0.125,-3.123 |
字符型常量 | ‘a’,‘b’,‘1’,‘\n’ |
字符串常量 | “a”,“ab”,“12356” |
变量
程序运行中,其值可以被改变,且使用必须先定义;
标识符规则:只能由字母、下划线、数字,不能是关键字;
第一个必须为字母或下划线,且区分大小写;
特点:变量在编译时为其分配内存空间,可通过其名字和地址访问相应的内存;
声明和定义
声明变量不需要建立存储空间,如extern int a;
定义变量需要建立存储空间,如int b;
#include<stdio.h>
int main((){
//extern 关键字只做声明,不能做任何定义
//声明一个变量a,a在这里没有建立储存空间
extern int a;
a =10;//erro,没有空间,不能赋值
int b=10; //定义一个变量b,b的类型为int,b赋值为10
return 0;
}
区分声明和定义:定义是声明的一个特例,一般将建立储存空间的声明称为定义,不需要建立存储空间称为声明。
示例
#include<stdio.h>
#define MAC 10 //声明一个常量,名为MAC 值为10,常量的值一旦初始化不可改
int main()
{
int a;//定义一个变量
const int b =10;//定义一个const 常量,名为b,值为10
b=11;//erro,常量的值不能改变
MAC =10;//erro:常量的值不能改变
a = MAC;
a =12;
printf("%d\n",a);
}
进制
进制即进位制,一种人为规定的进位方法,对于X进制表示:数运算时逢X进一位。十进制时逢十进一,二进制、十六进制类似
十进制 | 二进制(0b) | 八进制(0) | 十六进制(0x) |
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | A |
11 | 1011 | 13 | B |
12 | 1100 | 14 | C |
13 | 1101 | 15 | D |
14 | 1110 | 16 | E |
15 | 1111 | 17 | F |
16 | 10000 | 20 | 10 |
二进制
二进制时计算机系统中采用的一种数制。二进制数据时用0和1两个数码表示数。基数为2,逢二进一,借位规制时借一当二
数据在计算机中主要以补码的形式存储。
术语 | 含义 |
bit(比特) | 一个二进制代表一位,一个位只能表示0或1两种状态。数据传输是习惯以“位”(bit)为单位。 |
Byte(字节) | 一个字节为8个二进制,称为8位,计算机中存储的最小单位是字节。数据存储是习惯以“字节”(Byte)为单位。 |
WORD(双字节) | 2个字节,16位 |
DWORD | 两个WORD,4个字节,32位 |
1b | 1bit,1位 |
1B | 1Byte,1字节,8位 |
1k,1K | 1024 |
1M(1兆) | 1024k, 1024*1024 |
1G | 1024M |
1T | 1024G |
1Kb(千位) | 1024bit,1024位 |
1KB(千字节) | 1024Byte,1024字节 |
1Mb(兆位) | 1024Kb = 1024 * 1024bit |
1MB(兆字节) | 1024KB = 1024 * 1024Byte |
十进制转二进制方法:用十进制数除以2,分别取余数和商数,商数为0时,将余数倒着数即为结果
十进制的小数部分转换为二进制:小数部分和2想乘,取整数,不足1取0,每次相乘时小数部分,顺序看取整后的数就是转化后的结果
八进制
八进制,Octal,缩写OCT或0,一种以8为基数的计数法,采用0,1,2,3,4,5,6,7八数字,逢八进一。编程中以数字0开始表明为八进制;
八进制一位对应二进制三位。
十进制转化为八进制方法
用十进制数除以8,分别取余数和商数,商数为0的时候,将余数倒着的数即为结果。
十六进制
英文:Hexadecimal,它有0-9,A-F组成,字母不区分大小写。与10进制的对应关系:0-9对应0-9,A-F对应10-15;
十六进制数一位对应二进制的四位数。
十进制转十六进制:
用十进制数除以16,分别取余数和商数,商数为0的时候,将余数倒着数即结果。
接下来看下C语言中如何表示各个进制数
十进制 | 以正常数字1-9开头,如123 |
八进制 | 以数字0开头,如0123 |
十六进制 | 以0x开头,如0x123 |
二进制 | 以0b开头,C语言不能直接书写二进制数,如0b00111100.... |
#include<stdio.h>
int main()
{
int a=12; //十进制赋值
int b = 021; // 八进制赋值,以数字0开始
int c = 0xAB; //十六进制赋值
//在printf()中输出一个十进制数那么用%d,八进制用%o,十六进制用%x
printf("十进制: %d\n",a);
printf("八进制: %o\n",b);
printf("十六进制: %x\n",c);
return 0;
}
结果
十进制: 12
八进制: 21
十六进制: ab
计算机内存数值存储方式
原码
一个数的原码(原始的二进制码):最高位作为符号位,0表示正,1表示负;其他数值部分就是数值本身绝对值的二进制;
负数的原码是在其绝对值的基础上,最高位变为1。
十进制数 | 原码 |
+15 | 0000 1111 |
-15 | 1000 1111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或者不同符号数相加时,必须比较两个数那个绝对值大,这决定谁减谁,确定结果是正是负,故原码不便于加减运算;
反码
对于正数,反码与原码相同;
对于负数,符号位不变,其他部分取反(1变0,0变1)。
十进制数 | 反码 |
+15 | 0000 1111 |
-15 | 1111 0000 |
+0 | 0000 0000 |
-0 | 1111 1111 |
补码
在计算机系统中,数值一律用补码来存储;
- 对于正数,原码、反码、补码相同;
- 对于负数,其补码为它的反码加1;
补码符号位不动,其他位求反,最后整个数加1,得到原码;
十进制数 | 补码 |
+15 | 0000 1111 |
-15 | 1111 0001 |
+0 | 0000 0000 |
-0 | 0000 0000 |
#include<stdio.h>
int main(){
int a =-14;
printf("%x\n",a); //打印十六进制数
printf("%o\n",a);//打印八进制数
printf("%d\n",a);
//结果为fffffff2
//fffffff1对应的二进制: 1111 1111 1111 1111 1111 1111 1111 0001
//符号位不变,其他取反: 1000 0000 0000 0000 0000 0000 0000 1110
//上面加1最高位1代表负数: 1000 0000 0000 0000 0000 0000 0000 1111
return 0;
}
补码的意义
例1:用8位二进制数分别表示+0和-0
十进制数 | 原码 |
+0 | 0000 0000 |
-0 | 1000 0000 |
十进制数 | 反码 |
+0 | 0000 0000 |
-0 | 1111 1111 |
0以原码或反码方式存储,存在两种表现形式,补码统一了0的编码:
十进制数 | 补码 |
+0 | 0000 0000 |
-0 | 10000 0000由于只用8位描述,最高位1丢弃,变为0000 0000 |
例2补码方式相加:
十进制数 | 补码 |
9 | 0000 1001 |
-6 | 1111 1010 |
最高位的1溢出,剩余8位二进制表示的是3,正确。
在计算机系统中,数值一律用补码来存储,主要原因是:
- 统一了零的编码
- 将符号位和其它位统一处理
- 将减法运算转变为加法运算
- 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
sizeof关键
- sizeof不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节;
- sizeof的返回值为size_t;
size_t类型在32位操作系统下是unsigned int,是一个无符号的整数字;
#include<stdio.h>
int main()
{
int a;
int b = sizeof(a);//sizeof得到指定值占用内存的大小,单位:字节
printf("b = %d\n", b);
size_t c = sizeof(a);
printf("c = %u\n", c);//用无符号数的方式输出c的值
return 0;
}
练习
将十进制111书分别用二进制、十六进制表示;此数如果为一个unsigned int类型,在32bit宽little-endian模式CPU中,按byte的存放方式为:
111二进制为0b01101111;(高位0省略)
111十六进制为0x006f;(高位0省略)
一个unsigned int类型占4个字节即32bit,
二进制方式在32bit宽little-endian模式CPU中按byte的存放方式为:...,0000 0000,01101111(假设地址从高到底)
二进制方式在32bit宽little-endian模式CPU中按byte的存放方式为:00,6f (假设地址从高到底)
CPU存储方式—Big Endian、Little Endian
为什么会有Little-Endian和Big-Endian之分?
在计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节存储的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式,有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。