1、内存
存储器:
外存:外存又叫外部存储器,长期存放数据,掉电不丢失数据。常见的外存设备:硬盘,flash,rom,u盘,光盘,磁带。
内存:内存又叫内部存储器,暂时存放数据,掉电数据丢失。常见的内存设备:ram,DDR。
物理内存:实实在在的存储设备。
虚拟内存:操作系统虚拟出来的内存。当一个进程被创建的时候,或者程序运行的时候都会分配虚拟内存,虚拟内存和物理内存之间存在映射关系。
在运行程序的时候,操作系统会将虚拟内存进行分区:1、堆:在动态申请内存的时候,在堆里开辟内存。2、栈:主要存放局部变量(在函数内部,或复合语句内部定义的变量)。3、静态全局区:1)、未初始化的静态全局区:静态变量(定义的时候,前面加static修饰),或全局变量,没有初始化的,存在此区。2)、初始化的静态全局区:全局变量、静态变量、赋过初值的,存放在此区。4、代码区:存放程序代码。5、文字常量区:存放常量的。
2、指针的相关概念
操作系统给每个存储单元分配了一个编号,从0x00000000-0xffffffff。这个编号称之为地址,指针就是地址。
注意:1、无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节的大小。
2、对应类型的指针变量,只能存放对应类型的变量的地址。举例:整型的指针变量只能存放整型变量的地址。
3、指针的定义方法
3.1、简单的指针
数据类型 * 指针变量名;
int * p; //定义了一个指针变量p
在定义指针变量的时候,*是用来修饰当前定义的变量是一个指针变量。变量名是p。
3.2、关于指针的运算符
& 取地址、 * 取值
如果在一行中定义了多个指针变量,每个指针变量前面都要加*来修饰。
int *p,*q;//定义了两个整型的指针变量p和q
int *p,q;//定义了一个整型指针变量p和整型变量q。
注意:在定义时,如果指针变量是个局部变量,且没有赋初值的话,它的值就是随机的,这个指针变量就是一个野指针。
3.3、指针大小
在32位的系统下,所有类型的指针都是4个字节。
4、指针的分类
按指针指向的数据类型来区分
4.1、字符指针
字符型数据的地址:char *p;//定义了一个字符指针变量,只能存放字符型数据的地址编号。
4.2、短整型指针
short int *p;//定义了一个短整型的指针变量p,只能存放短整型变量的地址。
4.3、整型指针
int *p;//定义了一个整型的指针变量p,只能存放整型变量的地址。
还有:长整型指针、float型指针、double型指针、函数指针、结构体指针、指针的指针、数组指针
无论什么类型的指针变量,在32位系统下,都是4个字节,只能存放对应类型的变量的地址编号。
5、指针和变量的关系
对应类型的指针只能保存对应类型数据的地址,如果想让不同类型的指针相互赋值的时候,需要强制类型转换。
注意:1、*+指针 取值,取几个字节,由指针类型决定的指针为字符指针则取一个字节,指针为整型指针则取4个字节,指针为double型指针则取8个字节。
2、指针++ 指向下个对应类型的数据。
字符指针++,指向下个字符数据,指针存放的地址编号加1.
整型指针++,指向下个整型数据,指针存放的地址编号加4。
6、指针与数组元素之间的关系
1、变量存放在内存中,有地址编号,咱们定义的数组,是多个相同类型的变量的集合,每个变量都占内存空间,都有地址编号,指针变量当然可以存放数组元素的地址。
2、数组元素的引用方法
方法1:数组名[下标]
int a[10];
a[2]=100;
方法2:指针名加下标
int a[10];
int *p;
p=a;
p[2]=100;//因为p和a等价
补充:c语言规定:数组的名字就是数组的首地址,即第0个元素的地址,是个常量。
注意:p和a的不同,p是指针变量,而a是个常量。所以可以用等号给p赋值但不能给a赋值。
方法3:通过指针运算加取值的方法来引用数组的元素
int a[10];
int *p;
p=a;
*(p+2)=100;//相当于a[2]=100;
7、指针的运算
1、指针可以加一个整数,往下指几个它指向的变量,结果还是个地址。前提:指针指向数组的时候,加一个整数才有意义。
2、两个相同类型的指针可以比较大小
前提:只有两个相同类型的指针指向同一个数组的元素的时候,比较大小才有意义。指向前面元素的指针小于指向后面元素的指针。
3、两个相同类型的指针可以做减法
前提:必须是两个相同类型的指针指向同一个数组的元素的时候,做减法才有意义。做减法的结果是,两个指针指向的中间有多少个元素。
4、两个相同类型的指针可以相互赋值
注意:只有相同类型的指针才可以相互赋值(void *类型的除外)
int *p;
int *q;
int a;
p = &a;//p保存a的地址,p指向了变量a
q = p;//用p给q赋值,q也保存了a的地址,指向a
注意:如果类型不相同的指针要想相互赋值,必须进行强制类型转换。
8、指针数组
1、指针和数组的关系:1、指针可以保存数组元素的地址。2、可以定义一个数组,数组中有若干个相同类型指针变量,这个数组被称为指针数组。
指针数组的概念:指针数组本身是个数组,是个指针数组,是若干个相同类型的指针变量构成的集合。
2、指针数组的定义方法:类型说明符 * 数组名[元素个数];
int *p[10];//定义了一个整型的指针数组p,有10个元素p[0]-p[9];
每个元素都是int *类型的变量
int a;
p[1]=&a;
int b[10];
p[2]=&b[3];
p[2]和*(p+2)是等价的,都是指针数组中的第二个元素
3、指针数组的分类:字符指针数组char *p[10];、短整型指针数组、整型的指针数组、长整型指针数组、float型的指针数组、double型的指针数组、结构体指针数组、函数指针数组。
9、指针的指针——二级指针
即指针的地址。
int a;
int *p = &a;
int **q = &p;//*q=p **q=*p=a
10、字符串和指针
字符串的概念:字符串就是以'\0'结尾的若干个字符的集合。
字符串的存储形式:数组、字符串指针、堆。
字符数组:在内存(栈、静态全局区)中开辟了一段空间存放字符串。
字符串指针:在文字常量区开辟了一段空间存放字符串,将字符串的首地址赋给str
堆:使用malloc函数在堆区申请空间,将字符串拷贝到堆区
1、char string[100]="I love c":定义了一个字符数组string,用来存放多个字符,并且用“I love c”给string数组初始化,字符串“I love c”存放在string中。
2、char *str = “I love c”:定义了一个指针变量str,只能存放字符地址编号,所以说I love c这个字符串中的字符不能存放在str指针变量中。str只是存放了字符I的地址编号,“I love c”存放在文字常量区。
3、char *str = (char*)malloc(10*sizeof*(char));//动态申请了10个字节的存储空间,首地址给str赋值。 strcpy(str,"I love c");//将字符串“I love c”拷贝到str指向的内存里。
11、数组指针
1、二维数组:二维数组a中,a+1指向下一个元素,即下一个一维数组,即下一行。
2、数组指针的概念:本身是一个指针,指向一个数组,加1跳一个数组,即指向下一个数组。数组指针的作用就是可以保存二维数组的首地址。
3、数组指针的定义方法:指向的数组的类型(*指针变量名)[指向的数组的元素的个数]。
int (*p)[5];//定义了一个数组指针变量p,p指向的是整型的有5个元素的数组。p+1往下指5个整型,跳过一个有5个整型元素的数组。
4、数组名字取地址:变成数组指针。一维数组名字取地址,变成一维数组指针,即加1跳一个一维数组。int a[10];
a+1跳一个整型元素,是a[1]的地址。
a和a+1相差一个元素,4个字节
&a就变成了一个一维数组指针,是int(*p)[10]的类型
(&a)+1和&a相差一个数组即10个元素即40个字节
5、多维数组中指针的转换:在二维数组中,行地址取*不是取值的意思,而是指针降级的意思,由行地址(数组指针)变成这一行第0个元素的地址。取*前后还是指向同一个地方,但是指针的类型不一样了。
12、指针和函数的关系
1、指针作为函数的参数,可以给一个函数传一个整型、字符型、浮点型的数据,也可以给函数传一个地址。
函数的传参方式:复制传参、地址传参
2、地址传参:
3、传数组:给函数传地址的时候没法一下将数组的内容作为整体传进去,只能传数组的地址。
传一维数组:void fun1(int *p)
传二维数组:void fun2(int (*p)[4])
传指针数组:void fun3(char **q)
13、指针函数
指针函数:指针作为函数的返回值。一个函数可以返回整型数据、字符数据、浮点型的数据,也可以返回一个指针。
14、函数指针
函数指针:指针保存函数的地址。定义的函数在运行程序的时候,会将函数的指令加载到内存的代码段。所以函数也有起始地址。c语言规定:函数的名字就是函数的首地址,即函数的入口地址,就可以定义一个指针变量,来存放函数的地址。这个指针变量就是函数指针变量。
1、函数指针变量的定义方法:返回值类型 (*函数指针变量名)(形参列表);
int (*p)(int,int);//定义了一个函数指针变量p,p指向的函数必须要有一个整型的返回值,有两个整型参数。
int max(int x,int y){}
int min (int x,int y){}
可以用这个p存放这类函数的地址。p=max,p=min.
2、调用函数的方法:通过函数指针变量去调用。
15、特殊指针
1、空类型指针(void *):void*:表示通用指针,任何类型的指针都可以给void*类型的指针变量赋值。 int *p; void *q; q=p;是允许的。不用强制类型转换。主要用在函数的参数和返回值的位置。
2、NULL空指针:char *p = NULL;可以认为p哪里都不指向,也可以认为p指向内存编号为0的存储单元。在p的四个字节中,存放的是0x00000000. 一般NULL用在给指针的初始化。
16、main函数传参
int main(int argc, char *argv[])
argc:是一个int类型的变量,表示命令终端传入的参数的个数
argv:是一个指针数组,用于保存每一个命令终端传入的参数