视频链接:十年程序员整理的指针教程 | 没有废话 直奔主题 彻底搞懂指针 | 可以白嫖_哔哩哔哩_bilibilii
一. 指针的基本概念
int a=1;
int* p=&a;
p的类型是int* 类型,叫整型指针类型 p是一个指针变量
&表示取地址,&a则表示a这个变量的地址
int* p=&a; 表示把a的地址保存在了变量p中
注:1个字节占8个二进制位
64位操作系统中指针变量通常是占8个字节(64/8=8)
32位操作系统中指针变量通常是占4个字节(32/8=4)
测试:
#include <stdio.h>
#include<stdlib.h>
int main()
{
//sizeof 用来计算给定类型或变量的大小 以字节为单位
printf("int* 类型指针变量大小为:%d\n", sizeof(int*));
printf("int 类型变量大小为:%d\n", sizeof(int));
printf("short* 类型指针变量大小为:%d\n", sizeof(short*));
printf("short 类型指针变量大小为:%d\n", sizeof(short));
printf("long* 类型指针变量大小为:%d\n", sizeof(long*));
printf("long 类型指针变量大小为:%d\n", sizeof(long));
printf("char* 类型指针变量大小为:%d\n", sizeof(char*));
printf("char 类型指针变量大小为:%d\n", sizeof(char));
printf("float* 类型指针变量大小为:%d\n", sizeof(float*));
printf("float 类型指针变量大小为:%d\n", sizeof(float));
printf("double* 类型指针变量大小为:%d\n", sizeof(double*));
printf("double 类型指针变量大小为:%d\n", sizeof(double));
system("pause");
return 0;
}
输出:
我这上面显示的都是4个字节
占多少字节看系统是多少位的
#include <stdio.h>
#include<stdlib.h>
int main()
{
int a = 1;
int* p = &a;
printf("a的地址为:\n");
printf("%p", &a);
printf("\n");
printf("指针变量p变量所存储的地址 或是p所指向的地址:\n");
printf("%p", p);//%p输出地址 打印指针变量的内存地址
printf("\n");
printf("指针的大小为%d个字节", sizeof(p));
system("pause");
return 0;
}
输出:
006FF784 这8个数字每个数字是十六进制数 每个用4位二进制表示
8位二进制是一个字节 所以是4个字节表示一个地址
指针示意图:
注意:* 的两种含义
1.定义的时候 (前面有类型):
比如 int* ,int和*可以理解为构成了一个整体
表示后面的变量是指针 如int* p=&a;
2. 使用的时候(前面没有类型):
表示取值(取指针指向的内存空间的值)
int* p=&a;
*p=100;//这里的*p可以理解为就是变量a,即把a改为100
区别:int* 和char*的区别
char ch=‘a’;
char* q=&ch;
int* 和char* 都作为指针变量(但一个是整型指针变量,一个是字符型指针变量),他们的长度是一样的,但是它们指向的整型变量和字符型变量长度是不一样的
而且int* 和 char*的步长是不一样的
#include <stdio.h>
#include<stdlib.h>
int main()
{
int num = 1;
int* p = #
*p = 100;
printf("整数的值为%d\n", num);
printf("整型指针变量的地址为:%p\n", p);
printf("整型指针变量+1后的地址为:%p\n", p + 1);
char ch = 'a';
//int* q = &ch;//会报错 类型对不上 什么变量类型就对什么变量类型指针
char* q = &ch;
*q = 'x';
printf("字符的值为%c\n", ch);
printf("字符型指针变量的地址为:%p\n", q);
printf("字符型指针变量+1后的地址为:%p\n", q + 1);
system("pause");
return 0;
}
结果:
整型指针变量+1 , 整数占4个字节,地址实际上+4
字符型指针变量+1 ,字符占1个字节,地址实际上+1
指针变量的步长取决于其所指向的变量类型
但实际上可以这么理解:
整型指针+1,它指向了下一个整型
字符型同理 指针+1,指向了下一个字符
二. 指针作为函数参数
最典型的例子 两个数互换值
#include <stdio.h>
#include<stdlib.h>
void swap(int x,int y)
{
int t = x;
x = y;
y = t;
}
int main()
{
int a = 1, b = 2;
swap(a, b);
printf("互换后 a的值为%d\n", a);
printf("互换后 b的值为%d\n", b);
system("pause");
return 0;
}
结果:
a和b的值并没有得到交换
实参a,b分别传值给了函数中的形参 x,y
而在函数中,x,y执行了相关的互换操作之后,函数调用结束后形参被释放了
而a,b仍是保留了原值
如果要交换实参的值,必须要传地址
#include <stdio.h>
#include<stdlib.h>
void swap(int* x,int* y)
{
int t = *x;
*x = *y;
*y = t;
}
int main()
{
int a = 1, b = 2;
swap(&a, &b);
printf("互换后 a的值为%d\n", a);
printf("互换后 b的值为%d\n", b);
system("pause");
return 0;
}
结果:
这次互换成功了
swap(&a, &b); 将a的地址和b的地址传入函数
void swap(int* x,int* y) 交换函数用x,y两个整型指针变量接收传入的地址
而在函数内 *x ,*y就是将x,y指向的数据进行操作,也即a,b
函数执行结束后形指针类型变量x,y虽然释放,但a,b的值也进行了实际上的交换
再次注意:如果要交换实参的值,必须要传地址
同时,传参用指针效率也往往更高,比如结构体,传入整个结构体数据很大,但传入它的地址,用指针接收,也有着相同的效果,而且指针的字节数往往是固定的
三. 指针的运算
int a,*pa=&a,*pb;pb=pa;
这句代码意思是定义一个整型变量a,又有int* pb=&a (*pa=&a要与前面的int连起来看)
即a的地址赋给了pb,又创建了一个整型指针pb,第二个语句中,指针pa直接赋值给了pb,这两个指针此时指向同一个地方 即a
下面来看几个运算:
#include <stdio.h>
#include<stdlib.h>
int main()
{
int x = 3, y = 0, * px = &x;
y = *px + 5;
printf("y的值为:%d\n", y);
system("pause");
}
y的值为-------8
很明白,实际上就是px所指向的x加上3然后赋值给了8
#include <stdio.h>
#include<stdlib.h>
int main()
{
int x = 3, y = 0, * px = &x;
y = ++ * px;
printf("y的值为:%d\n", y);
system("pause");
}
y的值为------4
这也没有非议 px所指向的x先进行自增,3+1=4然后赋值给了y
y = ++ (* px); 在*px外面加个括号是一个意思
下面这两个例子注意区别:*px++ 和(*px)++的区别
第一个
#include <stdio.h>
#include<stdlib.h>
int main()
{
int x = 3, y = 0, * px = &x;
y = *px++;//不加括号
printf("y的值为:%d\n", y);
system("pause");
}
先把px指向的x的值赋值给了y
y的值为:3
下一步是谁++?是px+1 ,px的地址增加了4个字节,可以理解为px指向下一个整型变量
y = (*px)++;//加括号
而这个表示的是*px赋值给y 后,*px加一 即x的值加1
附加:const相关事宜
const讲究一个就近原则,表示指向的东西不能改
如过作形参希望某个参数不能被修改可以在前面加上const
比如 void mystrcpy(char *dest,const char* scr)
const的不可被修改指的到底是谁?
const int *p1=&num;
就近 离他最近的数据是* 他指的是*p1不可被修改
(*p1)++ 会报错 即num不可被修改、
而p1++ 没问题,p1本身可以被修改
int* const p2=&num;
离他最近的数据是p2
p2++报错 p2不可以被修改
而(*p2)++可以
const int * const p3=#
(*p3)++不行
p3++也不行
四. 空指针和野指针
#include <stdio.h>
#include<stdlib.h>
int main()
{
int* p;
*p = 100;
system("pause");
}
这个代码跑不过,p是野指针,只定义了p是一个整型指针 ,然而它指向哪里呢
对*p强行改值有很大风险
#include <stdio.h>
#include<stdlib.h>
int main()
{
int* p=NULL;
*p = 100;
system("pause");
}
初始赋值为null使其为空指针也没用,跑了会报错,难道它改的是空地址地方的值吗
但相较于野指针,空指针还是要好一点,他不会破坏不知道什么地方的数据
如何合法地使用内存呢?
1.系统分配的内存
int a;
int *p1=&a;
这种是操作系统自己分配的
2.用户申请内存(堆内存) 注意规范
char *str=(char*)malloc(32);//动态开辟一个32字节大小的内存,并把起始地址赋值给指针str
free(str);//释放str指向的堆内存,如果不释放,内存越用越少,会造成内存泄露
str=NULL;//将str赋值为空指针,不能让其成为野指针