指针——小甲鱼
01
- 内存区的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
- 在c语言中,对变量的访问有两种方式,直接访问和间接访问。
直接访问如:a=5;
系统在编译时,已经对变量分配了地址,例如,若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元。
间接访问如:scanf(“%d”,&a);
调用函数,把变量a的地址传递给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。
- 在c语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量int *i_pointer用来存放整型变量i的地址,则有:i_pointer=&i。
- 将i的地址(2000)存放到i_pointer中。这时,i_pointer的值就是(2000)。即变量i所占用单元的起始地址。要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer,从中取出i的地址(2000),然后取出i的值3。
- *:取值操作符,指针定义符。
&:取址操作符。
int i = 2000; //假设i的地址为1000h
int *pointer;
pointer = &i; //pointer=1000h
printf(“%d\n”,*pointer); //输出2000
- 知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的“指针”。
- c语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,成为指针变量。
注:指针变量的值(即指针变量中存放的值)是地址(即指针)。
- 定义指针变量:指针变量前面的“*”,表示该变量的类型为指针型变量。其一般形式为:类型说明符 *变量名; 其中,“*”表明这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。例如:
float *pointer;指针变量名是pointer。
- 只有整型变量的地址才能放到指向整型变量的指针变量中。
- 指针变量中只能存放地址(指针)。
02
- 如果已执行了语句:pointer_1=&a;
- &*pointer_1的含义是什么?
答:&和*两个运算符的优先级相同,但按自右向左方向结合,因此先进行*pointer_1的运算,它就是变量a,再执行&运算。因此,&*pointer_1与&a相同,即变量a的地址。
如果有:pointer_2=&*pointer_1; 它的作用就是将&a(a的地址)赋给pointer_2。
- *&a:先进行&a运算,得a的地址,再进行*运算。即a所指向的变量,也就是变量a。
*&a和*pointer_1的作用是相同的,它们都等价于变量a。即*&a与a等价。
- (*pointer_1)++相当于a++
- 数组:一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。
- 指针变量可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。
- 数组元素的指针就是数组元素的地址。
- 定义一个指向数据元素的指针变量的方法,与指向变量的指针变量相同。
- 引用一个数组元素,可以用:(1)下标法,如a[i]形式
- 指针法,如*(a+1)或*(p+1)
其中的a是数组名,p是指向数组元素的指针变量,其初值p==a(p==&a[0])。数组名即数组的第一个元素的地址。
注:在指针法中,*(a+i)表示指向的是第i个元素,而不是加了多少个地址。
03
- int arr[]:在编译时是将arr按指针变量处理的,即
f(int arr[],int n)相当于f(int *arr,int n)
2、c语言调用函数时,当用变量名作为函数参数时,传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址。因此传递的值是地址,所以要求形参为指针变量。
04
- 如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
- 形参和实参都用数组名:
void main(){ int a[10]; f(a,10); } void f(int x[],int n){ } |
- 实参用数组名,形参用指针变量:
void main(){ int a[10]; f(a,10); } void f(int *a,int n){ } |
- 实参形参都用指针变量:
void main(){ int a[10],*p=a; f(p,10); } void f(int *x,int n){ } |
- 实参为指针变量,形参为数组名:
void main(){ int a[10],*p=a; f(p,10); } void f(int x[],int n){ } |
- 可以认为二维数组是“数组的数组”,例:
int a[3][4]={{1,3,5,7},{},{}};
则二维数组a是由3个一维数组所组成的。
设该二维数组的首行的首地址为2000,则有:
第一行地址a[0] |
2000 a[0][0] |
2004 a[0][1] |
2008 a[0][2] |
2012 a[0][3] |
存放数据 |
1 |
3 |
5 |
7 |
第二行地址 a[1] |
2016 a[1][0] |
2020 a[1][1] |
2024 a[1][2] |
2028 a[1][3] |
存放数据 |
9 |
11 |
13 |
15 |
第三行地址 a[2] |
2032 a[2][0] |
2036 a[2][1] |
2040 a[2][2] |
2044 a[2][3] |
存放数据 |
17 |
19 |
21 |
23 |
表示形式 |
含义 |
地址 |
a |
二维数组名,指向一位数组a[0],即0行首地址。 |
2000 |
a[0] *(a+0) *a |
0行0列元素地址 |
2000 |
a+1 &a[1] |
1行首地址 |
2016 |
a[1] *(a+1) |
1行0列元素a[1][0]的地址 |
2016 |
a[1]+2 *(a+1)+2 &a[1][2] |
1行2列元素a[1][2]的地址 |
2024 |
*(a[1]+2) *(*(a+1)+2) a[1][2] |
1行2列元素a[1][2]的值 |
元素值为13 |
- 把二维数组a分解为一维数组a[0]、a[1]、a[2]之后,设p为指向二维数组的指针变量,可定义为:int (*p)[4]。它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a[0],其值等于a、a[0]或&a[0][0]等。而p+i则指向一维数组a[i]。
- 从前面的分析可得出*(p+i)+j是二维数组i行j列的元素的地址,而*(*(p+i)+j)则是i行j列元素的值。(先取出第i个一维数组,再取出一维数组中的第j个元素)
- 二维数组指针变量说明的一般形式为:
类型说明符 (*指针变量名)[长度]
其中,类型说明符为所指数组的数据类型。“*”表示其后的变量是指针变量。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。
注:要为指针变量赋初值。
05
字符串与指针
无笔记
06
在c/c+中,内存分成5个区,它们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区,里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,它们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,它和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的c语言中,全局变量又分为初始化的和未初始化的,在c++里面没有这个区分了,它们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,它们里面存放的是常量,不允许修改(当然,通过非正当手段也可以修改,而且方法很多)
07
1、字符数组和字符指针变量的区别:
1>字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),而不是将字符串放到字符指针变量中。
2>赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值:
char str[20];
str = “just do it!”;
而对字符指针变量,可以采用下面的方法赋值:
char *a;
a = “just do it!”;
注意赋给a的不是字符串,而是字符串第一个元素的地址。
3>对字符指针变量赋初值:
char *a=”just do it!”; 等价于char *a; a = “just do it!”;
而对数组的初始化:
char str[20]=”just do it!”; 不能等价于
char str[20]; str[]=”just do it!”;
4>如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址。也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。
5>指针变量的值是可以改变的。
6>若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。
2、一个函数在编译时就分配给一个入口地址。这个函数的入口地址就成为函数的指针。
3、做注释:#if(1/0) #endif
4、函数指针变量常用的用途之一是把指针作为参数传递到其他函数。指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
- 格式:
函数指针变量的声明格式:
返回类型 (*函数指针变量)(参数列表)
调用格式:(*函数指针变量)(实参列表)
08
- 一个函数可以带回指针型的数据,即地址。
形式为:类型名 *函数名(参数列表)
int *a(int x,int y);
- 指针函数是指带指针的函数,即本质是一个函数。
- 函数指针是指向函数的指针变量,因而函数指针本身首先应是指针变量,只不过该指针变量指向函数。
- 指针数组:一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。
类型名 *数组名[数组长度];
int *name[4];
- 指向指针的指针:定义一个指向指针数据的指针变量,形式如:char **p; p的前面有两个*号。*运算符的结合性从右到左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的。*p就是p所指向的另一个指针变量。
- 指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式:void main();
括号中是空的。实际上,main函数可以有参数:
void main(int argc,char *argv[]); argc和argv就是main函数的形参。
- main函数是由操作系统调用的。那么,main函数的形参的值从哪里来?显然不是在程序中得到。实际上,实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传递给main函数的参数。
09
1、
定义 |
含义 |
int i; |
定义整型变量i |
int *p; |
p为指向整型数据的指针变量 |
int a[n]; |
定义整型数组a,它有n个元素 |
int *p[n]; |
定义指针数组p,它由n个指向整型数据的指针元素组成 |
int (*p)[n]; |
p为指向含n个元素的一维数组的指针变量 |
int f(); |
f为带回整型函数值的函数 |
int *p(); |
p为带回一个指针的函数,该指针指向整型数据 |
int (*p)(); |
p为指向函数的指针,该函数返回一个函数值 |
int **p; |
p是一个指针变量,它指向一个指向整型数据的指针变量 |
- 指针变量加(减)一个整数:p++、p--、p+i、p-i、p+=i、p-=i等。
- 指针变量赋值:将一个变量地址赋给一个指针变量。如:
p=&a;(将变量a的地址赋给p)
p=array;(将数组array首元素地址赋给p)
p=&array[i];(将数组array第i个元素的地址赋给p)
p=max;(max为已定义的函数,将max的入口地址赋给p)
p1=p2;(p1和p2都是指针变量,将p2的值赋给p1
指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p=NULL;
- 两个指针变量可以相减。相减的结果是地址在内存空间中相差的指针类型的字节倍数。
- 两个指针变量比较:若两个指针指向同一个数组元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。
- void真正发挥的作用在于:(1)对函数返回的限定(2)对函数参数的限定。 例:void abc(void);
- ANSI C新标准增加了一种“void”指针类型,即不指定它是指向哪一种类型数据的指针变量。
例如:void *p; 表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。
- void指针它可以指向任何类型数据。也就是说,可以用任何数据类型的指针直接给void指针赋值。但是,如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。