一、指针的概念
1、什么是地址?
内存地址──内存中存储单元的编号
(1)计算机硬件系统的内部存储器中,拥有大量的存储单元(容量为1字节)。为了方便管理, 必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的 地址。
注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。
变量地址──系统分配给变量的内存单元的起始地址
(2)在地址所标识的存储单元中存放数据。
2、为什么要使用指针?
1、指针的基本概念?
在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为一个内存单 元,不同的数据类型所占用的内存单元数不等,如整型量占4个单元,字符量占1个单元等,为了 正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确地找 到该内存单元。 内存单元的编号也叫做指针。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通畅也把这个地址称为指针。
内存单元的指针和内存单元的内容是两个不同的概念。
2、使用指针好处
a.为函数提供修改调用变量的灵活手段;
b.让函数有多个返回值
c.可以改善某些子程序的效率 >>在数据传递时,如果数据块较大(比如说数据缓冲区或比较大的结构),这时就可以使用指针 传递地址而不是实际数据,即 高传输速度,又节省大量内存。
d.为动态数据结构(如二叉树、链表) 供支持
3、变量的存取方式
存取方式分为两种:直接存取和间接存取
int a = 3;
二、指针变量概念及定义方法
1、指针变量
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。注意:严格意义上说,指针是一个地址,是一个常量指针变量是存放一个地址,是一个变量。
2、定义一个指针变量
对指针变量的定义包括三个内容:
1)指针类型说明,即定义变量为一个指针变量;
2)指针变量名;
3)变量值(指针)
其一般形式为:
类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
注意:
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3)指针也可以被声明为全局、静态局部和局部的。
三、指针变量的初始化和引用
1、指针变量的初始化方法
设有指向整型变量的指针变量p,如要把整型变量a 的地址赋予p可以有以下两种方式:
指针变量初始化的方法有两种:定义的同时进行初始化和先定义后初始化
1)定义的同时进行初始化
inta = 5; int*p = &a;
2)先定义后初始化
inta; int*p; p=&a;
3)把指针初始化为
NULLint*p=NULL;
int*q=0;
不合法的初始化:
1)指针变量不能被赋值一个整数值(因为我们不知道这个整形常量是内存哪块地址
2)被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的
注意点:
1、多个指针变量可以指向同一个地址
2、指针的指向是可以改变的
1)可能会导致程序崩溃
2)访问你不该访问数据 所以指针必须初始化才可以访问其所指向存储区域
2、使用“*”获取指针对应存储区域的内容
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。未经赋值的 指针变量不能使用,否则将造成系统混乱,甚至死机。指针变量的赋值只能赋予地址,决不能赋 予任何其它数据,否则将引起错误。在C语言中,变量的地址是由编译系统分配的,对用户完全透 明,用户不知道变量的具体地址。
两个有关的运算符:
& :取地址运算符;
* :指针运算符(或称“间接访问” 运算符)。
C语言中 供了地址运算符&来表示变量的地址。其一般形式为:
&变量名;
*指针变量名
//获取指针变量指向的存储空间的内容
如&a表示变量a的地址,&b表示变量b的地址。变量本身必须预先说明。
关于*的使用注意:
1)在定义变量的时候 * 是一个类型说明符,说明定义的这个变量是一个指针变量
2)在不是定义变量的时候 *是一个操作符,访问指针所指向存储空间
指针变量的初始化
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a=4,b=5;
int a1;
a1 = 10;
int *p = &a;
int *p1 = &a,*p2 = &a;
int *p3 = &b,*p4;
p4 = &b;
int *p7;
p7 = NULL;
int *p5 = NULL;
int *p6 = 0;
p6 = p1;
int value = *p1;
*p1 = 100;
value = *p1;
printf("value = %d\n",a);
return 0;
}
四、指针常见的应用场景
1、指针常见的应用场景
1)在函数中间接访问调用者中的变量
voidchange(int*num)
{
*num = 10;
}
2)函数返回多个值
//写个一个函数,计算两个数的和、差、乘积、商
voidsumAndMinusAndJiAndShang(intnum1,intnum2,int*sum,int*minus,int*ji,int*shang)
{
*sum = num1 + num2;
*minus = num1 -num2;
ji = num1 num2;
*shang = num1 / num2;
}
应用:利用指针实现两个变量值得交换
void swap1(int a,int b){
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *p,int *p1){
int *temp;
temp = p;
p = p1;
p1 = temp;
}
void swap3(int *p,int *p1){
int temp;
temp = *p;
*p = *p1;
*p1 = temp;
}
int main(int argc, const char * argv[]) {
int a = 4,b = 5;
printf("a = %d,b = %d\n",a,b);
swap3(&a, &b);
printf("a = %d,b = %d\n",a,b);
return 0;
}
指针的应用场景:
#include <stdio.h>
void changValue(int *p){
*p = 100;
}
void test(){
int a = 1;
int *p1 = &a;
printf("%p\n",&a);
printf("%p\n",p1);
changValue(p1);
printf("%d\n",*p1);
printf("%d\n",a);
}
void caculator(int x,int y,int *add,int *jian,int *cheng,float *chu){
*add = x+y;
*jian = x-y;
*cheng = x*y;
*chu = x/(float)y;
}
int main(int argc, const char * argv[]) {
int add=0;
int jian=0;
int cheng=0;
float chu=0.0f;
caculator(12, 4, &add, &jian, &cheng, &chu);
printf("add = %d\n",add);
printf("jian = %d\n",jian);
printf("cheng = %d\n",cheng);
printf("chu = %.2f\n",chu);
return 0;
}
五、二级指针介绍
1、二级指针介绍
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。也称为“二级指针”通过指针访问变量称为间接访问。由于指针变量直接指向变量,所以称为“一级指针”。而 如果通过指向指针的指针变量来访问变量则构成“二级指针”。
2、多级指针介绍
注意:
int ***m1; 取值***m1
int *****m2 取值*****m2
二级指针
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a = 5;
int *p = &a;
printf(" &a = %p\n",&a);
printf(" p = %p\n",p);
printf("---------------\n");
int **p1 = &p;
printf(" &p =%p\n",&p);
printf(" p1 =%p\n",p1);
printf("---------------\n");
printf("*p =%d\n",*p);
printf("*p1 =%p\n",*p1);
printf("**p1 =%d\n",**p1);
int ***p2 = &p1;
printf("%d\n",***p2);
int ****p3 = &p2;
printf("%d\n",****p3);
return 0;
}
六、指针为什么要区分类型
1、指针为什么要区分类型?
在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。比如,在16位编译器环境下,任何一个指针变量都只占用2个字节,并不会随所指向变量的类型而改变。
虽然所有的指针都只占8个字节,但不同类型的变量却占不同的字节数。 C语言中,int占4个字节,char占1个字节.
如果定义指针变量不定义类型,那么它在取*也就是取其中的值的时候,就不知道应该读取几个字节。 而定义了类型之后。 如果是int型的就读四个字节,char型的就读一个字节。
结论:定义什么类型的指针就应该指向什么类型的变量。
指针类型
#include <stdio.h>
int main(int argc, const char * argv[]) {
int *p1;
char *p2;
double *p3;
float *p4;
void *p5; printf("sizeof(p1) int -->%ld\n",sizeof(p1));
printf("sizeof(p2) char -->%ld\n",sizeof(p2));
printf("sizeof(p3) double -->%ld\n",sizeof(p3));
printf("sizeof(p4) float -->%ld\n",sizeof(p4));
printf("sizeof(p5) void -->%ld\n",sizeof(p5));
int num = 10;
p1 = #
p2 = #
printf("%d\n",*p2);
return 0;
}
七、数组指针
1、数组指针
数组元素指针
一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址
指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中)
所谓数组元素的指针就是数组元素的地址
可以用一个指针变量指向一个数组元素
注意:
数组名a不代表整个数组,只代表数组首元素的地址。 “p=a;”的作用是“把a数组的首元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋给 p”
八、使用指针引用数组元素
1、使用指针引用数组元素
在指针指向数组元素时,允许以下运算:
加一个整数(用+或+=),如p+1
减一个整数(用-或-=),如p-1
自加运算,如p++,++p
自减运算,如p–,–p
两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)
float a[10],float *p=a;
假设a[0]的地址为2000,则
p的值为2000
p+1的值为2004
p-1的值为1996
如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同 一数组中的上一个元素。
注意:
1)如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组 序号为i的元素
2)(p+i)或p+i或a(a+i)是+i所指向的数组元素,即a[i]。
3)如果指针p1和p2都指向同一数组
结论: 引用一个数组元素,可用下面两种方法:
(1)下标法,如a[i]形式
(2)指针法,如(a+i)或(p+i)收地
(3)a是常量(a++错误),p是变量(p++正 确)
使用指针引用数组元素
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[4]={1,2,3,4};
printf("&a[0] = %p\n",&a[0]);
printf("&a[1] = %p\n",&a[1]);
printf("&a[2] = %p\n",&a[2]);
int *p = a; printf(" p = %p\n",p);
printf(" p+1 = %p\n",p+1);
printf(" a+1 = %p\n",a+1);
printf(" *(p+1) = %d\n",*(p+1));
printf(" *(a+1) = %d\n",*(a+1));
for (int i=0; i<4; i++) {
printf("%d\t",*(a+i));
}
printf(" *a = %d\n",*a);
return 0;
}
应用:逆序数组
#include <stdio.h>
void nixuArray(int a[],int len){
int *p = a;
int i = 0,j = len-1;
int temp;
while (i<j) {
temp = *(p+i);
*(p+i) = *(p+j);
*(p+j) = temp;
i++,j--;
}
}
int main(int argc, const char * argv[]) {
int arr[10]={1,2,3,4,5,6,7,8,9,10};
nixuArray(arr, 10);
for (int i=0; i<10; i++) {
printf("%d\t",arr[i]);
}
return 0;
}
九、一维指针数组概念
1、一维指针数组
一个数组的元素值为指针则是指针数组。 指针数组是一组有序的指针的集合。 指针数组的所有元素都必须是具有相同存储类型和 指向相同数据类型的指针变量。指针数组说明的一般形式为:
类型说明符 *数组名[数组长度]
其中类型说明符为指针值所指向的变量的类型。
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a=3,b=4,c=5;
int *pa[3]={&a,&b,&c};
printf(" &a = %p\n",&a);
printf(" pa[0] = %p\n",pa[0]);
printf(" pa = %p\n",pa);
printf(" &pa[0]= %p\n",&pa[0]);
printf(" *(&a) = %d\n",*(&a));
printf(" *(pa[0]) = %d\n",*(pa[0]));
printf("%d\n",**pa);
int a1[2][2]={1,2,3,4};
int *pa1[2]={a1[0],a1[1]};
printf("**pa1 = %d\n",**pa1);
printf("**(pa1+1) = %d\n",**(pa1+1));
return 0;
}
十、指针变量之间的运算
1、两指针变量减法运算
两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。
实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。
1)指针之间的算术运算
(pointer2地址值 -pointer地址值) / sizeof(所指向数据类型)
2、两指针变量进行关系运算
指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。
指针变量还可以与0比较。设p为指针变量,则p==0表明p是空指针,它不 指向任何变量;p!=0表示p不是空指针。
注意:
指针之间可以相减,但不可以相加(相加无意义)
空指针是由对指针变量赋予0值而得到的。
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[5]={1,3,5,7,9};
int *p = a; int *p1 = &a[1];
printf("p1 - p = %ld\n",p1 - p);
printf("p - p1 = %ld\n",p - p1);
printf("p1 > p = %d\n",p1 > p);
return 0;
}
十一、用数组名访问二维数组
1、用数组名访问二维数组
inta[3][4]={{1,3,5,7}, {9,11,13,15},{17,19,21,23}};
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf("&a[0][1] = %p\n",&a[0][1]);
printf("a[0]+1 = %p\n",a[0]+1);
printf("*(a[0]+1) = %d\n",*(a[0]+1));
printf("&a[0][2] = %p\n",&a[0][2]);
printf("a[0]+2 = %p\n",a[0]+2);
printf("a[1] = %p\n",a[1]);
printf("a+1 = %p\n",a+1);
printf("a[2] = %p\n",a[2]);
printf("a+2 = %p\n",a+2);
printf("*(a+1) = %p\n",*(a+1));
printf("&a[1][0] = %p\n",&a[1][0]);
for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) {
printf("%d\t",*(*(a+i)+j));
}
printf("\n");
}
return 0;
}
十二、用指针访问二维数组
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) {
printf("*(a[%d]+%d)=%d\t\t",i,j,*(*(a+i)+j));
}
printf("\n");
}
int *p = a;
for(int i =0;i<12;i++){
printf("%d\t",*(p+i));
}
return 0;
}
十三、二维数组指针定义、初始化
1、二维数组指针的定义、初始化
在上面的说明中我们已经知道,二维数组名是指向行的,它不能对如下说明的指针变量p直接 赋值:
inta[3][4]={{10,11,12,13},{20,21,22,23},{30,31,32,33}},*p;
其原因就是p与a的对象性质不同,或者说二者不是同一级指针。C语言可以通过定义行数组指针的 方法,使得一个指针变量与二维数组名具有相同的性质。
二维数组指针变量说明的 一般形式为:
数据类型 (*指针变量名)[二维数组列数];
其中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类型。
如要将二维数组赋给一指针,应这样赋值:
inta[3][4];
int(*p)[4];
p=a; p++;
所以数组指针也称指向一维数组的指针,亦称行指针
例子:
#include <stdio.h>
intmain()
{
inta[3][4] = {1, 3, 4, 7, 9, 11, 13, 15, 17, 19, 21, 23};
int(*p)[4] = a; // 定义指向二维数组a的指针变量p
for(inti = 0; i < 3; i++)
{
for(intj = 0; j < 4; j++)
{
// 遍历数组
printf("%d\t", *(*(p + i) + j));
}
printf("\n");
}
}
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4] = a;
for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) {
printf("%d\t",*(*(p+i)+j));
}
printf("\n");
}
return 0;
}
十四、指针数组和二维数组指针的区别
1、指针数组和二维数组指针变量的区别
int*pa[3]={&a,&b,&c}; // pa是一个指针数组
int*pa1[2]={a[0],a[1]}
int(*pa)[3]; //二维数组指针
应该注意指针数组和二维数组指针变量的区别。这两者虽然都可用来表示二维数组,但是其表示方法和意义是不同的。
二维数组指针变量是单个的变量,其一般形式中”(指针变量名)”两边的括号不可少。而指针数组 类型表示的是多个指针(一组有序指针)在一般形式中”指针数组名”两边不能有括号。例如:
int(*p)[3];
表示一个指向二维数组的指针变量。该二维数组的列数为3或分解为一维数组的长度为3。
int*p[3]
表示p是一个指针数组,有三个下标变量p[0],p[1],p[2]均为指针变量。
应用:《推箱子》游戏思路和代码实现
#include <stdio.h>
#define kRows 10
#define kCols 11
void printMap(char map[kRows][kCols]){
for (int i=0; i<kRows; i++) {
printf("%s\n",map[i]);
}
}
void move1(char map[kRows][kCols],int oldX,int oldY,int newX,int newY){
char temp;
temp = map[oldX][oldY];
map[oldX][oldY] = map[newX][newY];
map[newX][newY] = temp;
}
int main(int argc, const char * argv[]) {
char map[kRows][kCols]={
"##########",
"#O #### #",
"# X#### #",
"# #",
"###### #",
"# #### #",
"# #",
"# ######",
"# ",
"##########"};
int personX =1;
int personY =1;
int personNextX=personX;
int personNextY=personY;
int boxX = 2;
int boxY = 2;
char direction;
char street = ' ';
char box = 'X';
printMap(map);
printf("请控制小人移动w.上 s.下 a.左 d.右 q.退出\n");
while (1) {
scanf("%c",&direction);
getchar();
personNextX = personX;
personNextY = personY;
switch (direction) {
case 'w':
case 'W':
personNextX--;
break;
case 's':
case 'S':
personNextX++;
break;
case 'a':
case 'A':
personNextY--;
break;
case 'd':
case 'D':
personNextY++;
break;
case 'q':
case 'Q':
printf("程序正在退出...\n");
printf("程序已经退出!\n");
return 0;
default:
break;
}
if (map[personNextX][personNextY] == street) {
move1(map,personX,personY,personNextX,personNextY);
personX = personNextX;
personY = personNextY;
}else if (map[personNextX][personNextY] == box){
int boxNextX = boxX+(boxX-personX);
int boxNextY = boxY+(boxY-personY);
if (map[boxNextX][boxNextY] == street) {
move1(map, boxX, boxY, boxNextX, boxNextY);
move1(map, personX, personY, boxX, boxY);
personX = personNextX;
personY = personNextY;
boxX = boxNextX;
boxY = boxNextY;
}
}
if (boxY==kCols-2) {
printf("哇哦,你出来了哦!\n");
break;
}
}
return 0;
}
学习心得:
通过本章知识点的学习,了解了指针的定义,初始化,和数组,函数的关系以及应用,为函数提供了修改调用变量的灵活手段,使得函数有多个返回值,与数组,函数的结合应用广泛,实现各种复杂的算法功能。