指针的作用:
1、表示一些复杂的数据结构
2、快速传递数据(传参结构体中和函数处理数组的时候)
3、使函数返回一个以上的值
4、能直接访问硬件(因为它里面存的是硬件上的一个地址)
5、能够方便的处理字符串
6、是理解面向对象语言中引用的基础
总结;指针是c语言的灵魂
指针的定义:
地址:内存单元的编号(从零开始的非负整数)范围:4G【0--4G-1】
指针:
指针与指针变量:
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是一个变量,什么样的变量呢?一个存放地址的变量
指针与指针变量是两个不同的概念可以换一句话说(地址和地址变量)
但是注意:通常我们叙述时会把指针变量简称为指针,实际上含义是不同的
指针的本质是一个操作受限的非负整数(指针不能加、乘、除只能相减(即可以相减得出两个房间号中间隔了多少内存))
指针的分类:
1、基本类型指针
include<stdio.h>
//指针变量就是地址变量即能存放其他变量的地址
int main(void)
{
int* p;//p是变量的名字(p是指针变量即p能存放整形变量的地址) int*表示变量存放的是int类型变量的地址
/*
1、int* p 不表示定义了一个名字叫做*p的变量
2、int* p 应该这样理解:p是变量名,p变量的类型为 int*类型(既存放int变量地址的类型)
3、所谓int*类型 实际就是存放int变量地址的类型
*/
int i = 10;
p = &i;
/*
1、p保存了i的地址,因此p指向i。
2、p不是i,i也不是p,更准确的说修改p的值不会影响i的值,修改i的值也不会影响p的值
3、如果一个指针变量指向了某个普通变量,则
*指针变量 就完全等同于 普通变量
例如:
如果p是个指针变量,并且p存放了普通变量i的地址
则p指向了普通变量i
*p 就完全等同于 i
或者说:在所有出现*p的地方都可以替换成i
在所有出现i的地方都可以替换成*p
*p就是以p的内容位地址的变量的值
*/
// p = i;// error,因为类型不一致,p只能存放int类型变量的地址,不能存放int类型变量的值
// p=55;//error,类型不一致
}
附录:*的含义
1、乘法
2、定义指针变量
int* p;
//定义了一个名字叫p的指针变量,int*表示 变量p的类型(即只能存整形变量的地址)
3、指针的运算(又叫*运算)
该运算法放在定义好的指针变量的前面
如果p是一个已经定义好的指针变量
则*p表示 以p的内容为地址的变量的值
如何通过被调函数修改主调函数中普通变量的值?
1、实参必须为该普通变量的地址
2、形参必须为指针变量
3、在被调函数中通过
*形参名 = ....
的方式可以修改主调函数相关变量的值
例如:
#include<stdio.h>
void fun(int* p,int*q)
{
*p = 30;
*q = 40;
}
int main(void)
{
int a = 10;
int b = 20;
fun(&a,&b);
printf("a = %d b = %d\n",a,b);
}
2、指针和数组的关系
指针和一维数组的关系
一维数组名
一维数组名是一个指针常量
它存放的是一维数组第一个元素的第一个字节的地址编号
下标和指针的关系
如果p是一个指针变量,则p[i] 永远等价于 *(p + i)
界定一个一维数组需要几个参数【如果一个函数处理一个一维数组,则需要接受数组的哪些信息】
1、数组名即数组元素的首地址(数组第一个元素的地址)
2、数组的长度
指针变量的运算
指针变量不能相加、不能相乘、不能相除、只能相减
如果两个指针变量指向的是同一块连续的空间的不同的存储单元,则这两个指针变量才可以相减
一个指针变量占几个字节【一个变量的地址是用该变量的首字节的地址来表示】
预备知识:
sizeof(数据类型) 功能:返回值就是该数据类型所占的字节数
例子: sizeof(int) = 4 sizeof(char) = 1 sizeof(double) = 8
sizeof(变量名) 功能:该变量所占的字节数
总结:指针变量无论它指向谁,它本身只占四个字节。
一个变量的地址是用该变量首字节的地址来表示。
指针和二维数组的关系
3、指针和函数的关系
4、指针和结构体的关系
5、多级指针
1、指针使用的常见错误:
#include<stdio.h>
int main(void)
{
int i = 5;
int* p;
int*q;
p = &i;
*q = p;//语法错误也会报错因为*q是整形变量而P是一个地址变量
*q = *p;//语法没错,但是q没有指向里面是个垃圾地址
p=q;
/*
q的空间是属于本程序的,所以本程序可以读写q中的内容,
但是如果q内部是垃圾值,则本程序不能读写*q中的内容,
以为此时*q所代表的内存单元的控制权限并没有(不一定)分配给本程序。
*/
printf("%d",*p);
return 0;
}
指针常见错误2因为指针变量没有指向,他有可能存的地址是系统分配给本程序以外的内存。那么本程序对本程序以外的内存没有读写的权限。
#include<stdio.h>
int main(void)
{
int *p;
int i=5 ;
*p = i;//会报错指针变量没有指向或里面是一个垃圾值会报错(因为p中存放的地址可能不是系统分配给本程序的内存的地址)
printf(" %d ",*p);
return 0;
}
这样是对的:先给指针一个指向。
#include<stdio.h>
int main(void)
{
int *p;
int i ;
p = &i;
*p = 3;
printf(" 整形变量i的地址为: %d\n指针变量p中存放的地址为: %d\n变量i中的数值为: %d\n",&i,p,i);
return 0;
}
2、指针的两个经典交换题
#include<stdio.h>
void Exchange(int* x,int* y);
int main(void)
{
int a= 10;
int b = 20;
int *pointer1,*pointer2;
pointer1 = &a;
pointer2 = &b;
Exchange(pointer1,pointer2);
printf("a=%d b=%d\n",a,b);
}
void Exchange(int* p1,int* p2)
{
int z;
z = *p1;
*p1=*p2;
*p2= z;
}
运行结果:
分析:
另一种:
#include<stdio.h>
void Exchange(int* x,int* y);
int main(void)
{
int a= 10;
int b = 20;
Exchange(&a,&b);
printf("a=%d b=%d\n",a,b);
return 0;
}
void Exchange(int* x,int* y)
{
int*z;
z = x;
x=y;
y= z;
printf("*X = %d *y = %d\n",*x,*y);
}
运行结果:
内存分析图:
3、数组名的含义:数组名是数组元素第一个元素第一个字节的地址
#include<stdio.h>
int main(void)
{
int a[5];
int b[5];
//a = b;//error,a是常量
//a = &b[3];//error a 是常量
printf("数组名a =%#X\n第一个元素的首地址&a[0] = %#X\n",a,&a[0]);
return 0;
}
运行结果;
4、数组和下标的关系: 如果p是一个指针变量,则p[i] 永远等价于 *(p + i)
#include<stdio.h>
int main(void)
{
int a[5] ={1,2,3,4,5};
int b =10;
int *p ;
p = &b;
printf("======输出数组名就相当于输出一个指针变量里面存的内容即一个地址(数组名里面存的是数组数组中第一个元素的地址)======\n");
printf("数组名 a = %#X\n数组中&a[0] = %#X\n",a,&a[0]);
printf("====数组名就是一个指针变量里面存的是数组第一个元素的地址====\n");
printf(" a = %#X\n &a[0] = %#X\n",a,&a[0]);
printf(" (a+0) = %#X\n &a[0] = %#X\n",(a+0),&a[0]);
printf(" (a+1) = %#X\n &a[1] = %#X\n",(a+1),&a[1]);
printf(" (a+2) = %#X\n &a[2] = %#X\n",(a+2),&a[2]);
printf(" (a+3) = %#X\n &a[3] = %#X\n",(a+3),&a[3]);
printf(" (a+4) = %#X\n &a[4] = %#X\n",(a+4),&a[4]);
printf("====利用指针输出数组中的元素=====\n");
printf(" *a = %d\n a[0] = %d\n",*a,a[0]);
printf("*(a+0) = %d\n a[0] = %d\n",*(a + 0),a[0]);
printf("*(a+1) = %d\n a[1] = %d\n",*(a + 1),a[1]);
printf("*(a+2) = %d\n a[2] = %d\n",*(a + 2),a[2]);
printf("*(a+3) = %d\n a[3] = %d\n",*(a + 3),a[3]);
printf("*(a+4) = %d\n a[4] = %d\n",*(a + 4),a[4]);
return 0;
}
运行结果:
5、确定一个数组最少需要数组名和数组的长度。则p[i] 永远等价于 *(p + i)
#include<stdio.h>
void fun(int* pAry,int len)//输出数组
{
/*
*(pAry + i)或pAry[i]输出数组的值
*/
int i;
for( i = 0;i<len;i++)
{
printf("%d\n",pAry[i]);
}
printf("======华丽的分割线========\n");
for( i = 0;i<len;i++)
{
printf("%d\n",*(pAry+i));//好好理解
}
printf("=====华丽的分割线======\n");
}
int main(void)
{
int a[5] ={1,2,3,4,5};
int b[9] = {9,8,7,6,5,4,3,2,1};
fun(&a,5);
fun(&b,9);
return 0;
}
运行结果:
6、函数处理数组:最少需要数组名和数组的长度。则p[i] 永远等价于 *(p + i)
#include<stdio.h>
/*
因为a[3]等价于*(a+3)而*(a+3)是数组的第四个元素,所以a[3]为数组的第四个元素
*/
void fun(int* pAry,int len)
{
pAry[5] = 88;
//*(pAry + 5) = 88;
}
int main(void)
{
int a[6] = {1,2,3,4,5,6};
printf("%d\n",a[5]);
fun(a,6);
printf("%d\n",a[5]);
return 0;
}
运行结果:
7、指针变量的运算,只能相减,不能相加,也不能相除,
#include<stdio.h>
int main(void)
{
int i = 5;
int j = 10;
float x = 10;
float *y;
int *p1,*q1,*p2,*q2;
int a[5];
p2 = &a[1];
q2 = &a[4];
p1 = &i;
q1 = &j;
y = &x;
printf("p和q指向的单元相隔 %d 个单元没有实际意义因为不连续\n",p1-y);
printf("p2和q2指向的单元相隔 %d 个单元\n",q2-p2);
return 0;
}
运行结果:
8、指针变量接收地址详解
#include<stdio.h>
int main(void)
{
char ch = 'a';
int a = 10;
double b = 6.66;
char*p = &ch;
int*q = &a;
double*r = &b;
printf("char*类型的指针变量所占的字节数 = %d\nint*类型的指针变量所占的字节数 = %d\ndouble*类型的指针变量所占的字节数 = %d\n",sizeof(p),sizeof(q),sizeof(r));
printf("======华丽的分割线=======\n\n");
printf("char类型变量所占的字节数= %d\nint类型变量所占的字节数 = %d\ndouble类型变量所占的字节数 = %d\n",sizeof(ch),sizeof(a),sizeof(b));
return 0;
}
运行结果:
内存分析: