在上一篇文章中我们通过一个经典的C语言入门程序认识了C语言,算是一个小小的入门,也是我对C语言的一次系统的复习,讲的可能不够详细,在接下来的文章中我将尽可能详细的讲述我在学习C语言基础的过程中接触到的知识点,本篇文章主要讲述C语言中的数据类型。
同样我们先引入一个例子,代码如下:
include <stdio.h>
include <malloc.h>
int main()
{
int age; //定义一个名为age的整数型变量,用于存放年龄
double weight,height; //定义一个名为weight和一个名为height的双精度浮点型变量,用于存放体重和年龄
float salary; //定义一个单精度浮点型变量,用于存放工资
char name[8]; //定义一个8字节的字符数组,用于存放姓名(注意一个中文字符占两个字节,因此该变量中最多可存放四个中文字符,而一个英文字符占用一个字节,因此这里可存放8个英文字符)
char *motto; //定义一个字符指针,该指针指向字符常量或字符串常量,用于存放座右铭
printf("Please enter your name\n");//打印字符串,上一篇文章中已经说过一部分
scanf("%s",name); //从控制台读取字符串,并存到字符数组name中(注意读取字符串时变量前不加&符号,而读取其他类型变量时需要加&符号,关于&符号的解释及地址相关的内容会在以后的文章中提到)
printf("Plese enter your age\n");
scanf("%d",&age); //从控制能读取一个整数,并存到age中,这里的age变量前有&符号
printf("Please enter your salary!\n");
scanf("%f",&salary);
printf("Please enter your height and weight!\n");
scanf("%lf %lf",&height,&weight);
printf("Please enter your motto!\n");
motto = malloc(sizeof(char) * 20);//分配内存空间,用char *x定义的变量必须分配空间才能使用,sizeof()函数用来取得当前类型或变量的长度,即占用字节数
scanf("%s",motto);
printf("Name:%s\nAge:%d\nHeight:%.2lf\nWeight:%.2lf\nSalary:%.2f\nMotto:%s\n",name,age,height,weight,salary,motto);//上次提到printf()函数是参数数量可变的函数,这里的参数数量是任意的,与要格式化的占位符一一对应
free(motto);
return 0;
}
前两行为头文件的引入,stdio.h文件在Helloworld的例子中已经说过,它是标准输入输出相关的头文件,malloc.h是第一次遇到,这个头文件主要包含了C语言内存分配相关的函数的声明,例如上面例子上用到的malloc()和free()两个函数,malloc()函数是内存分配函数,该函数接收一个参数,该参数为要分配内存的字节数,内存分配是匿名的,maclloc()函数在内存中找到合适大小的位置后会返回该块内存的第一个字节的地址,但如果malloc()找不到合适空间,则会返回空指针。一般来说malloc()函数带用于动态分配内存,即在运行时决定,如下面的例子:
int n = rand() % 100;
char name[n];
这在C99之前是不被支持的,而用malloc()函数可成功分配到内存:
int n = rand() % 100;
char name = malloc(sizeof(char) * n);
这里有一个sizeof()的函数,这个函数的作用是用来取某一类型的大小或某一具体量的大小,这里的大小指的是字节数,如char 占用一个字节,int占用4个字节,float占用4个字节,double占用8个字节,char、int、float、double*在64位系统中都是占用4个字节等。该函数在取类型占用的字节时参数必须用圆括号包围,即必须是sizeof(char)的形式,而在取具体变量的长度时则可以不用括号,如
char name[8];
printf("%d",sizeof name);
这里将打印出8,不过笔者推荐在所有情况下都加上圆括号,以免出现奇怪的问题,同时也可增强代码可读性
接着向下看,第三行语句
int age;
这里的int 是integer的意思,即这是一个整数类型的变量,它占用4个字节,它是一个有符号的整数变量,因此符号位占用一位,在32位系统中取值范围在-2^31 ~ 2^31 -1之间,也就是可存储值的大小为2^32。
整数相关的基本数据类型还有短整数类型short,它在系统中占用2个字节,每个字节占8位,因此取值范围在 -2^15 ~ 2^15 -1;长整数类型long,在系统中占用8个字节因此取值范围为-263~263-1,无符号短整数类型unsigned short,占用字节数与short是相同的,但由于没有符号位因此取值范围为0 ~ 2^16 -1。同理无符号整型unsigned int 取值范围为 0 ~ 2^32 -1,无符号长整数类型unsigned long 取值范围为0 ~ 2 ^64 -1,因为long long 也占用8个字节,因此long long 与long 的取值范围相同,unsigned long long 与 unsigned long取值范围相同。
double weight,height;
float salary;
double为双精度浮点型,相比float(单精度浮点型)存储的数据更准确,占用的空间也大一倍,单精度浮点数在机器中占用4个字节,占32位;双精度浮点数占用8位,点64位。浮点数在系统内是用指数形式存储,分为数符,尾数,指数符,指数四部分组成;数符即符号位占一位,表示正负,指数符即指数符号位,表示指数的正负。尾数表示浮点数的有效数字,指数位存放指数的有效数字。一般的float类型尾数点24位指数点8位,double类型尾数点48位,指数点16位。在C语言中float可保存有效数字7位,double可保存有效数字15位,double的内存消耗是float的两倍,double数据的计算速度也比float慢的多,因此,要根据实际情况来决定是否用double,如果数据不会溢出尽量使用float类型,在本例中定义的weight,height两个变量就应该使用float。
char name[8];
char 即character,字符的意思,char在内存中只占用一个字节,一般用来存放字母、标点等。严格来讲,其实char也是整数类型,因为char保存的其实就是一个整数,计算机通过ASCII编码方式来将某个字符保存为一个整数,ASCII码的范围是0 ~ 127 只需要7位就可以保存,而char型数据占用8位,足以保存全部的ASCII值。 这里的name[8]代表一个可保存8位char型数据的数组。这里的数据是一块连续的存储空间,方括号里的数字为数组的下标,表示这8位连续空间的编号,在C语言中数组的下标是从0开始的,因此在存储的时候最后一个有效元素的下标为7,例如"c is ammizing"一共有13个字符(空格也算在其中),但它将占用14个字节,最后一个字节用来存放"\0",这里的"\0"是系统自动添加的。
char *motto;
代表指针,指针是一个变量,这个变量用于指向另一个变量所在内存中的位置,这里的motto是一个指向char型变量或数组的指针,它里面只保存了这个char型变量或char型数组首元素的地址,而motto中存放的是才是变量的值。在下面这句
motto = malloc(sizeof(char) * 20);
是用来给这个char型变量或数组分配内存的语句,上面已经提到过的malloc语句,它将这个char型数组的首地址存到motto这个变量中。在C语言中指针是一门艺术,指针使用不当往往会也遇到很多问题,本文不打算深究C语言指针方面的知识,如果您对这方面感兴趣可以看一下《C和指针》(英文名《Pointers on C》By Kenneth A. Reek)。
scanf("%s",name);
scanf("%d",&age);
scanf("%lf %lf",&height,&weight);
scanf("%s",motto);
这几条语句的作用是从控制台读取值存入相应的变量中,scanf()也是一个参数数量可变的函数,每一个参数与相应的占位符对应。但注意到在读取字符串值存到char数组或char *类型的变量中时是没有&符号的,其他就是都要加一个&符号,这里的&符号是用来取到该变量的首地址的,因为数组的首就是数组的第一个元素,而数组变量本身就代表数组的第一个元素因此不加&符号,对于指针型变量也是相同道理,指针型变量本身就指向这块内存的首地址,因此也不用加&符号。(这里"%s"对应字符串或字符数组,"%d"对应整数类型,"%lf"对应double类型,"f"对应float类型,还有"%c"对应char类型等)。
总结:
C语言中的基本数据类型分为数值型和字符型两大类,数值型又分为整型和浮点型两类,整型又分:短整型(short),整型(int),长整型(long)三类,浮点型又分为:单精度浮点型(float)和双精度浮点型(double)两类。C语言的数据类型还包括构造类型,指针类型和空类型三类,这三类在以后会慢慢接触到。
要注意的时每种数据类型对应的取值范围,在选择数据类型的时候一定要考虑到当前要存储的数据最大值和最小值能达到多少,再来选择相应的数据类型存储,在使用指针动态分配内存的时候一定要在用完之后第一时间释放内存,否则可能会导致这块内存一直被占用无法被其他应用或进程访问,即造成内存泄漏。