Linux_C编(1)数据类型

一、数据类型

数据类型包含两方面的内容——数据的表示和对数据加工的操作。数据的全部可能表示构成数据类型的值的集合,数据全部合理的操作构成数据类型的操作集合。
在 C语言中,把整型、实型和字符型称为基本数据类型,又称整型和实型为数值型。C语言中还有构造类型、指针类型、枚举类型和空类型。

1、变量

(1)什么是变量
其值在其作用域中可以改变的量称为变量。每一个变量都有自己的名字,在内存中占据一定的存储空间。变量在使用前必须先定义,每一个变量都有自己的地址。变量根据其定义的类型又可以分为整型变量,字符型变量,浮点型变量和指针型变量等。变量的值可以发生改变,意味着它可以被覆盖、被写入、被幅值。每个变量必须要有一个名字和它所在内存空间绑定。
代码中声明整型变量a,它的类型已经决定,那么它的大小也就是4个字节(32位机),那么在内存中就是有连续的四个字节与之对应,a变量名就代表了这4字节的空间。a变量的地址就是连续4字节的开始的地址0x000。

(2)变量名和变量值
变量名是在变量的声明的时候,该名字就和内存中的一块地址绑定在一起了,可以通过变量名直接找到对应的内存区域,也可以通过地址找到其内存区域。
变量的值是变量所对应的内存区域内存放的二进制序列,变量的值不会因为变量的类型发生了改变而改变,当变量被转换为对应类型时,内存区域内的二进制序列以该类型的形式翻译出来,这也就是强制类型转换存在的原因
例如:

int a = 97;
char ch1 = 'a';
char ch2 = (char) a;
char *p = (cahr *) a;

第一行代码:整型变量a在内存中是以97的二进制形式存放的,当使用它时,会被以十进制形式打印出来。
第二行代码:字符变量 ch1 的ASCII值是97,也是以97的二进制存放的,使用时,会被以字符“a”的形式打印出来。
第三行代码:将整型变量a强制类型转换成字符型,a变量里的值没有发生变化,变的是它的类型,它里面的值还是97的二进制,它类型变成了char,97的二进制变成了char,表现出来就是字符“a”。
第四行代码:声明一个字符型指针变量p,p是个变量,它里面的值可变,它的值是整型变量a的值强制类型转换成了字符型指针类型,这个时候p里的值还是97的二进制,只不过这个97的意义已经代表了一个字符型指针,也就是一个指向字符的地址了。

由此可见:变量在内存中存放和它的值没有关系,而是和它的类型相关的,同样我们可以得出:一个二进制序列对于计算机本身而言没有意义,计算机根本不知道这个二进制数据是干嘛用的,只有具体到它的类型的时候或者出现在合适的场合时,才能代表具体的意义。如果一串二进制的数据出现在地址总线上,那么它代表一个地址,如果该相同的数据出现在数据总线上,那么它就代表一个数据。
例如:test1.c

char ch = 'a';
int a = (int)ch;
printf("%d %c \n",a,ch);

(3)局部变量和全局变量
函数形参变量只有在被调用期间才能分配内存单元,调用结束立即释放。这一点表明形参变量只有在函数内才是有效的,离开该函数就不能再使用了。这种变量有效性的范围被称为变量的作用域。不仅对于形参变量,C语言中的所有的变量都有自己的作用域,按作用域范围可分为两种,即局部变量和全局变量。
(a)局部变量
局部变量也称为内部变量,局部变量是在函数内作定义说明的,其作用域仅限于函数内,离开函数后再使用这种变量是非法的。
例如:

int f1(int a)  //函数f1
{
	int b,c;
	......
}

这个函数内a、b、c有效

int f2(int x)       //函数f2
{
	int y,z;
	......
}

这个函数内x、y、z有效

int main()
{
	int m,n;
	......
}

这个函数内m、n有效
在函数f1中,a为形参,b、c为一般变量,在函数f1中a、b、c有效,在其他函数中a、b、c无效,其他同理。

关于局部变量的作用域:

  1. 主函数中定义的变量也只能在主函数中使用,不能在其他函数中使用。同时,主函数也不能调用其他函数的变量。因为主函数也是一个函数,与其他函数一样。
  2. 形参变量是属于被调函数的局部变量,实参变量则是属于主函数的局部变量。
  3. 允许在不同的函数中使用相同的变量名,他们代表不同的对象,分配不同的单元。互不干扰,也不会发生混淆。
  4. 在复合语句中也可以定义变量,其作用域只在复合语句范围内。

(b)全局变量
全局变量也称外部变量,它是在函数外部定义中的变量,它不属于那一个函数,它属于一个源程序文件,其作用域是整个源程序。在函数中使用全局变量,一般应做全局变量说明,只有在函数内经过说明的全局变量才会被其他函数使用。全局变量的说明符是extern,但在一个函数前定义的全局变量,在该函数内使用就可以不用加以说明。
例如:

int a,b;     //外部变量
void f1()
{
	......
}
float x,y;
int  f2()
{
 	......
}
int main()
{
	......
}

2、常量

其值不会发生改变的量称为常量,它们可以和数据类型接合起来分类,如整型常量,浮点型常量,字符常量等等。常量是可以不经过定义和初始化,而直接使用的,常量又分为直接常量和符号常量,直接常量又叫做字面常量,如12,0,4.6,‘a’,“abcd”;符号常量如宏定义。
常量的值在其作用域内不会发生改变,也不能被赋值,在其出现时就被当成一个直接数使用,它可以被访问,被读取,而不能被写,被赋值。

3、基本内置类型

整型
整型数据在其存储在内存中的二进制信息的最高位是当做数值信息为还是当做数据的符号位,可以将整型数据分为带符号整型和无符号整型两种。每种整型又按照所需的字节个数的多少分为三种,所以整型共有六种:带符号整型(int),带符号短整型(short int),带符号长整型(long int 或long),无符号整型(unsigned int)、无符号短整型(unsigned short int),以及无符号长整型(unsigned long)。

实型
实型数据有表示范围和精度两个不同的特征,为了适应数的范围和精度的不同要求,实型数据分为三种类型:单精度型(float,也称浮点型),双精度型(double),长双精度型(long double)。

构造类型
构造类型是指由若干个相关的数据组合在一起形成的一种复杂数据类型,构造数据类型的成分数据可以是基本数据类型的,也可以是别的构造类型的。按构造方式和构造要求区分,构造类型主要有数组类型、结构类型和共用类型。数组类型是由相同类型的数据组成;结构类型可以由不同类型的数据组成;当不同数据类型不会同时使用时,以节约内存,让不同数据占用同一区域,这就是共用类型。

指针类型
指针类型是取程序对象在内存中占据的地址为值的一种特殊的数据类型。

枚举类型
当变量只取很少几种可能的值,并分别用标识符对值命名时,这种变量的数据类型可以用枚举类型来表示,如变量表示一个星期的某一天,就可以用枚举类型来描述该变量的类型。

void类型
用保留字void表示的数据类型有两种完全相反的意思,可以表示没有数据(没有结果),也可以表示某种任意类型的数据,如void *,可以指向任意的数据类型,但是最好要强制转换一下。
void表示空类型。

数据类型的大小

类型说明符数的范围
int-2^31 ~2^31-1
unsigned short0~65535(2^16-1)
short int-32768~32767(2^15-1)
unsigned short int0~65533(2^16-1)
long int-2147483648~2147483647(2^31-1)
unsigned long0~4294967295(2^32-1)
char-128~127(2^7-1)
unsigned char0-255(2^8-1)

4、声明与定义

声明与定义
例如:

int i;
extern int i;

1、定义
所谓的定义就是编译器创造一个对象,并为这个对象分配一块内存并且为它取上一个名字,这个名字就是我们经常说的变量名。一个变量或者对象在一定的区域只能被定义一次,如果多次定义,编译器会报错。
2、声明
声明有两重含义
第一重含义:告诉编译器,这个名字已经匹配到一块内存上了,上面第2行代码用到变量或者对象是在别的地方定义的,声明可以出现多次。
第二重含义:告诉编译器,这个名字已经预定了,别的地方再也不能用它来作为变量名和对象名。

5、static和extern

static
简单来说static修饰变量,就是指该变量空间独立于函数的auto变量或者栈变量,static变量空间在内存中的静态区分配

  1. 修饰局部变量
    一般情况下,局部变量是存放在栈区的,并且局部变量的声明周期在该语句块执行结束便结束了。但是如果用static进行修饰的话,该变量变存放在静态数据区,其生命周期一直持续到整个函数程序执行结束为止。但是值得注意的是,该局部变量的作用域不变。
#include <stdio.h>
void fun()
{
	static int a = 1; 
	a++;
	printf("%d\n",a);
}
int main()
{
	fun();
	fun();
	return 0;
}

程序执行结果为 2 3
说明在第二次调用fun()函数时,a的值为2,并没有进行初始化赋值,直接进行自增运算,所以得到的结果是3.

  1. 修饰全局变量
    对于一个全局变量,它既可以在本源文件中被访问,也可以在同一个工程的其他源文件中被访问,记住需要extern声明。
    (1)file1.c
int a = 1;

(2)file2.c

#include <stdio.h>
extern int a; //这里不是定义,而是声明,声明使用。
int main()
{
	printf("%d\n",a);
	return 0;
}
// 执行结果为 1;

以上的结果为1,但是如果把int a = 1 改成static int a = 1;那么在file2.c是无法访问到变量a的

  1. 修饰函数
    修饰函数和全局变量类似,改变了函数的作用域。

extern
extern 是指当前变量或者函数不是在本源文件中声明的,它是外部变量或者外部函数,当我们试图引用一个外部声明的全局变量或者函数时,可以在前面加上extern。
extern可以修饰变量和函数,表示该变量或者函数在其他地方被定义过,在这里声明使用它。
1、extern 变量名
在任何函数体外声明或者定义变量时,不加extern可能是定义也可能是声明,但是加extern肯定是声明,如果不想被其他源文件链接到,需要使用static关键字。
在函数体内声明(注意是声明,在函数体内不能定义全局变量)使用其他源文件的定义的变量时,必须使用extern关键字,因为在函数体内默认为局部变量。
3、extern 函数
函数默认是外部的(在函数体内或者函数体外声明一个外部函数,extern关键字均可以省略),如果不想被其他源文件链接到,在函数前面加上关键字extern。

extern int fun1();

6、const

const是constant的简写,表示海枯石烂,只要一旦给变量前面加上关键字const修饰,那么意味着该变量里的数据可以被访问,不能被修改。

const int a = 1;

7、auto

auto一般我们看不见,但是它又是无处不在的,在函数生命周期中声明的变量通常叫做局部变量,也叫作自动变量。

int fun()
{
	int a = 10; //auto int a = 10;
	// do something
	return 0;
}

编译器会在int a = 10前面加上auto关键字。auto的出现意味着当前变了的作用域为当前函数或者代码段的局部变量。

8、register

  1. 作用
    如果一个变量被register修饰,那意味着该变量会作为一个寄存器变量,让该变量的访问速度达到最快。例如,一个程序逻辑中有一个很大的循环,循环中有几个变量要频繁进行操作,这些变量可以声明为register变量。
  2. 寄存器变量
    寄存器变量是指一个变量直接引用寄存器,也就是对变量名的操作的结果是直接对寄存器进行访问。寄存器是CPU的亲信,CPU操作的每个操作数和操作结果,都由寄存器来暂时保存,最后才写入内存中或者从内存中读取出来。
    在使用寄存器变量时,应注意:
    (1) 待声明为寄存器变量类型应该是CPU寄存器所能接收的类型,意味着寄存器变量单个变量,变量长度应该小于等于寄存器长度;
    (2)不能对寄存器变量使用取地址符“&”,因为该变量没有内存地址;
    (3)尽量在大量频繁操作时使用寄存器变量,且声明的变量个数应该尽可能少。

9、volatile

volatile修饰的变量表示该变量的值很容易由于外部因素发生变化,强烈请求编译器要老老实实地在每次对变量进行访问时去内存中读取。
例如:

int a = 10;
int b = a;
int c = a;

编译器扫描代码发现第一行将10赋值给a,接着a的值没有发生变化,直接赋值给b,接着将a赋值给c,因为CPU访问内存的速度较慢,编译器为了提高效率,直接将10赋值给c。
注:volatite告诉编译器,每行都要执行,按照顺序执行,不要给我直接优化代码。

10、typedef

作用:为一种数据类型定义一个新的名字,简化一些比较复杂的类型声明。
1、typedef与结构的问题

typedef struct tag_node
{
	char *p_item;
	p_node p_next;
} *p_node;

typedef的最简单使用 typedef long byte_4
给已知数据类型long起个新的名字,叫做byte_4;

typedef  struct tag_my_struct
{
	int i_num;
	long l_length;
} my_struct;

第一步,定义一个新的结构类型
第二步,typedef为这个新的结构起了一个名字,叫做my_struct

2、typedef与#define的问题

typedef char * p_str;
#define p_str char *;

上面两种定义p_str数据类型的方法,一般来说typedef比#define好。
因为#define只是替换,而typedef是起一个新的名字。例如

#define f(x) x*x;

就需要注意括号的问题,但是typedef不需要注意括号的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值