指针的本质与使用场景

目录

1、指针的本质

1.1 指针的定义

1.2 取地址操作符与取值操作符

2、指针的使用场景

2.1 指针的传递

2.2 指针的偏移

2.3 指针与自增、自减运算符

2.4 指针与一维数组

2.5 指针与动态内存申请

2.6 字符指针与字符数组的初始化

3、二级指针

3.1 二级指针的传递

1、指针的本质

1.1 指针的定义

一般在内存地址中存储的是数据,如果我们需要将某个变量的地址保存下来,就需要用到指针。

1.2 取地址操作符与取值操作符

取地址操作符为&,也称引用,通过该操作符我们可以获取一个变量的地址值;取值操作符为*,也称为解引用,通过该操作符我们可以得到一个地址对应的数据。

注意

(1)指针的变量名是p,而不是*p

(2)在定义指针变量时必须指定其类型。需要注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中,依此类推。

(3)连续定义三个指针变量的正确写法

int* a, * b, * c;//定义三个整型的指针变量
int* a, b, c;//一个整型指针变量,两个整型变量

2、指针的使用场景

2.1 指针的传递

普通值传递

#include <stdio.h>

void change(int j);

int main()
{
	int i = 10;
	printf("before change i=%d\n", i);
	change(i);
	printf("after change i=%d\n", i);
	return 0;
}

void change(int j)
{
	j = 5;
}

 经过change函数,i的值仍然为10

 指针传递

#include <stdio.h>

void change(int* j);

int main()
{
	int i = 10;
	printf("before change i=%d\n", i);
	change(&i);
	printf("after change i=%d\n", i);
	return 0;
}

void change(int* j)//j=&i
{
	*j = 5;
}

经过change函数,i的值改变

  

2.2 指针的偏移

指针即地址,就像我们找到了一栋楼,这栋楼的楼号是B,那么住前就是A,往后就是C,所以应用指针的另一个场景就是对其进行加减,但对指针进行乘除是没有意义的,就像家庭住址乘以5没有意义一样。

数组是特殊的,不能和整型变量、浮点型、字符型变量类比。

数组名a,类型是数组,a里边存了一个值,是地址值,是数组的起始地址

#include <stdio.h>

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* p;
	p = a;
}

 

#include <stdio.h>

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* p;
	p = a;
	printf("*p=%d\n", *p);
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", *(p + i));
	}
	return 0;
}

  

2.3 指针与自增、自减运算符

#include <stdio.h>

int main()
{
	int a[3] = { 2,7,8 };
	int* p;
	int j;
	p = a;//让指针变量p,指向数组的开头
	j = *p++;//j=*p;p++,任何时候都是把后加加去掉,第二步,另一个运算符看优先级是否高于++
	printf("a[0]=%d,j=%d,*p=%d\n", a[0], j, *p);
	return 0;
}

 

#include <stdio.h>

int main()
{
	int a[3] = { 2,7,8 };
	int* p;
	int j;
	p = a;//让指针变量p,指向数组的开头
	j = (*p)++;//j=*p;p++,任何时候都是把后加加去掉,第二步,另一个运算符看优先级是否高于++
	printf("a[0]=%d,j=%d,*p=%d\n", a[0], j, *p);
	return 0;
}

 

#include <stdio.h>

int main()
{
	int a[3] = { 2,7,8 };
	int* p;
	int j;
	p = a;//让指针变量p,指向数组的开头
	j = *p++;//j=*p;p++,任何时候都是把后加加去掉,第二步,另一个运算符看优先级是否高于++
	printf("a[0]=%d,j=%d,*p=%d\n", a[0], j, *p);//227
	j = p[0]++;//j=p[0],p[0]++
	printf("a[0]=%d,j=%d,*p=%d\n", a[0], j, *p);//2,7,8
	return 0;
}

 

2.4 指针与一维数组

#include <stdio.h>

void change(char* d);

int main()
{
	char c[10] = "hello";
	change(c);
	puts(c);
	return 0;
}

void change(char* d)
{
	*d = 'H';
}

  

#include <stdio.h>

void change(char* d);

int main()
{
	char c[10] = "hello";
	change(c);
	puts(c);
	return 0;
}

void change(char* d)
{
	*d = 'H';
	d[1] = 'E';
	*(d + 2) = 'L';
}

 

  

2.5 指针与动态内存申请

在学习完数组之后会觉得数组长度固定很不方便,其实C语言的数组长度固定是因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中,而栈空间的大小在编译时是确定的。如果使用的空间大小不确定,那么就要使用堆空间。(最新的C11数组变量可变,但是因为其放在堆空间了)

栈是计算机系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈操作、出栈操作都有专门的指令执行,这就决定了栈的效率比较高,堆则是C/C++函数库提供的数据结构,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能由于内存碎片太多),那么就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后返回。栈空间由系统自动管理,而堆空间的申请和释放需要自行管理。

程序是放在磁盘上的有序的指令集合,程序启动起来时才叫进程。

malloc函数可以实现动态申请空间

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
	int place_apply;//申请多大的空间
	scanf("%d", &place_apply);
	char* p;
	p = (char*)malloc(place_apply);//malloc申请空间的单位是字节
	//malloc返回的是void*无类型指针,需要强转成char*类型,使与p的类型一致
	strcpy(p, "malloc success");
	puts(p);
	return 0;
}

 

 申请多大空间用完之后要使用free函数用于释放申请的空间,free之后要将p赋值为NULL,防止野指针

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
	int place_apply;//申请多大的空间
	scanf("%d", &place_apply);
	char* p;
	p = (char*)malloc(place_apply);//malloc申请空间的单位是字节
	//malloc返回的是void*无类型指针,需要强转成char*类型,使与p的类型一致
	strcpy(p, "malloc success");
	puts(p);
	free(p);//释放空间,free时必须使用malloc申请时返回的指针值,不能进行任何偏移
	p = NULL;
	return 0;
}

 栈空间与堆空间的区别

#include <stdio.h>
char* print_stack();

int main()
{
	char* p;
	p = print_stack();
	puts(p);//不能打印,栈空间会随着函数的执行结束而释放
	return 0;
}

char* print_stack()
{
	char c[17] = "I am print_stack";
	puts(c);//能正常打印
	return c;
}

 

#include <stdio.h>
char* print_stack();
char* print_malloc();

int main()
{
	char* p;
	p = print_stack();
	//puts(p);//不能打印,栈空间会随着函数的执行结束而释放
	p = print_malloc();
	puts(p);//可以正常打印,堆空间不会随子函数的结束而释放,必须自己free
	return 0;
}

char* print_malloc()
{
	char* p = (char*)malloc(30);
	strcpy(p, "I am print_malloc");
	puts(p);//可以正常打印
	return p;
}
char* print_stack()
{
	char c[17] = "I am print_stack";
	puts(c);//能正常打印
	return c;
}

 

2.6 字符指针与字符数组的初始化

#include <stdio.h>

int main()
{
	char* p = "hello";//把字符串型常量"hello"的首地址赋给p
	char c[10] = "hello";//等价于strcpy(c,"hello");
	c[0] = 'H';
	p[0] = 'H';//不可以对常量区数据进行修改
	printf("c[0]=%c\n", c[0]);
	printf("p[0]=%c\n", p[0]);
	p = "world";//将字符串world的地址赋给p
	c = "world";//非法
	return 0; 
}

 

 

3、二级指针

一级指针的使用场景是传递和偏移,服务的对象是整型变量、浮点型变量、字符型变量等。二级指针也是一种指针,其作用自然也是传递与偏移,其服务对象更加简单,即只服务于一级指针的传递与偏移。

3.1 二级指针的传递

如果p是一个指针变量,那么要存储p的地址的话,&p就是一个二级指针,要存储二级指针就要定义一个二级指针变量,所以二级指针的初始化一定是某一个一级指针取地址

#include <stdio.h>
void change(int** p, int* pj);

int main()
{
	int i = 10;
	int j = 5;
	int* pi;
	int* pj;
	pi = &i;
	pj = &j;
	printf("i=%d,*pi=%d,*pj=%d\n", i, *pi, *pj);//i和*pi等于10,*pj=5
	change(&pi, pj);
	printf("after change i=%d,*pi=%d,*pj=%d\n", i, *pi, *pj);//目标是让*pi的值为5
	return 0;
}

void change(int** p, int* pj)
{
	int i = 5;
	*p = pj;
}

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值