深入理解指针(一)

目录

深入了解指针(一)

1.指针变量和地址

2.指针变量

3.指针的解引用

4.指针+整数的运用

指针+整数的用法实操

5.void类型指针

6.const修饰指针

6.1const作用

6.2const在*左边

6.3const在*右边

6.4双指针玩法

6.5双const

6.6总结 

7.指针的运算

7.1指针+ -整数

7.2指针-指针

指针-指针的意义作用

7.3指针关系运算

8.野指针

8.1指针未初始化

8.2指针越界访问

8.3指针指向的空间已被释放

8.4 避免野指针

9.assert 断言

10.指针的使用和传址调用

10.1传值调用

10.2传址调用




深入了解指针(一)

1.指针变量和地址

在C语⾔中创建变量其实就是向内存申请空间,⽐如:

深入理解指针(一)_第1张图片

2.指针变量

个数值有时候也是需要 存储起来,⽅便后期再使⽤的,这个时候就要用到指针变量。

int a = 100;
int* p = &a;  //取a的地址存放到p指针这里,也可以说是p指针a的地址,所以在这里p指针的值就是a
printf("*p = %d a=%d \n", *p, a);

指针的变量的引用

int a = 100;
int* p = &a;  //取a的地址存放到p指针这里,也可以说是p指针a的地址,所以在这里p指针的值就是a
printf("*p = %d a=%d \n", *p, a);
*p = 0;   //*p代表一个值,但p不是
printf("*p = %d a=%d \n", *p, a);

3.指针的解引用

对⽐,下⾯2段代码,主要在调试时观察内存的变化

代码1

int a = 100;
int* p = &a;  //取a的地址存放到p指针这里,也可以说是p指针a的地址,所以在这里p指针的值就是a
printf("*p = %d a=%d \n", *p, a);
*p = 0;   //*p代表一个值,但p不是
printf("*p = %d a=%d \n", *p, a);

a的内存变化

没变之前                                                   变化之后

   -----> 

代码2

int b = 2546878;
//这个写法有些编译器或者头文件为.cpp报错,.c不会报错
char* q = &b;  //这里用char类型的指针来存放b地址
*q = 0;  //这里改只能改b内容两位
printf("%d %d", *q,b);

b的内存变化

没变之前                                                   变化之后

    ->  

运行效果

深入理解指针(一)_第2张图片

相对比就可以看出来,int类型的指针存放int类型的变量a,是可以直接全部改变的

但如果是char类型的指针q,只能访问一个个字节

结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。

4.指针+整数的运用

int x = 10;
//char* e = &x;;//两个写法
char* e = (char *) &x;
int *pi = &x;
printf("x   = %p\n", &x);  //%p打印地址

printf("e   = %p\n", e);
printf("e+1 = %p\n", e+1); //因为pi为char类型指针,所以 + 1地址会增加1个字节

printf("pi  = %p\n", pi);
printf("pi  = %p\n", pi+1);  //区分pi+1,因为pi为int类型指针,所以+1地址会增加4个字节。

printf("*pi = %d\n", *pi);//*pi代表值,而p则为地址。
printf("*pi+1= %d\n", *pi + 1);

效果如下图

深入理解指针(一)_第3张图片

指针+整数的用法实操

深入理解指针(一)_第4张图片

比如给数组初始化
为什么要指向a[0]?
因为一个数组的地址他是连续的
当你指向第一个地址那么后面的地址肯定是连在一起的,
相当于找到了火车头,那么后面的车厢就可以直接找到了

//比如给数组初始化
//为什么要指向a[0]
//因为一个数组的地址他是连续的
//当你指向第一个地址那么后面的地址肯定是连在一起的,
// 相当于找到了火车头,那么后面的车厢就可以直接找到了
int arr[10] = { 0 };
int* pa = &arr[0];
for (int i = 0; i < 10; i++)
{
	*pa = 1;
	pa = pa + 1; //这样利用int类型的指针就可以指向下一个了。
	//pa++;一样的写法
}
pa = &arr[0]; //这里重新指向数组的头,因为经过上面的初始化,指针已经指向最后了。
for (int i = 0; i < 10; i++)
{
	printf(" %d ", *pa);
	pa = pa + 1; 
}

5.void类型指针

//void*类型指针
int a = 6;
void* pa = &a;
printf("%p\n", pa);
printf("%p\n\n", a);

char ch = 'a';
void* pb = &ch;
printf("%p\n", pb);
printf("%p\n", ch);

        void指针可以接收不同类型的变量的地址而不会报错,其主要用法就是到后面函数的传参和接受参数的时候用void类型的指针来接收参数。

6.const修饰指针

下面分几种情况讨论

6.1const作用

//const修饰指针
//const作用就是给指定的变量加上保护,让其不能被修改,跟常量相似,但其本身还是变量。
const int c = 20;
//c = 20;//这样写会报错
//但是可以通过下面进行间接修改,相当于走后门。
int* p = &c;
*p = 5;
printf("%d \n", c);

6.2const在*左边

//讨论const所在的位置,不同的效果。
const int  d = 50;
int e = 20;
const int* pm = &d; //此时const限定了*p让其不能修改,不能通过pm来修改pm指向的空间内容
//*pm = 30;//报错
const int h = 50;
int * pm1 = &h;
//虽然不能修改pm1指向的内容,但是不会限制pm1指向其他
*pm1 = &e;

6.3const在*右边

const int  q = 50;
int* const pm2 = &q; //在这里const限制的是p,而不是*p
*pm2 = 20;//这个时候就可以修改了

//要理解1.pm2里面存放的是地址(q的地址)
//2.pm2也是一个变量,他也有自己的地址
//3.而*pm2是pm2指针指向的空间内容。

6.4双指针玩法

const int  q = 50;
int* const pm2 = &q; //在这里const限制的是p,而不是*p
*pm2 = 20;//这个时候就可以修改了
int* Pa = pm2;
*Pa = 10;
printf("%d  %d %d", *pm2, *Pa,q);

6.5双const

const int  w = 50;
const int* const pm3 = &w;

6.6总结 

const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
const如果放在* 的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变

7.指针的运算

7.1指针+ -整数

//逆序打印
int arr[10] = { 0 };
int* p = &arr[0];
for (int i = 0; i <10; i++)
{
	scanf("%d", p+i);//输入来实现

}
p = &arr[9];
for (int i = 9; i >= 0; i--)
{
	printf("%d  ", *p);
	p--;
}

7.2指针-指针

//指针-指针
//指针-指针 =整数
//类似于,日期-日期 =天数
//日期-天数 = 日期
int ax[10] = { 0 };
printf("%d", &ax[9] - &ax[0]); //=9  指针-指针的绝对值只会得到相隔的元素个数。
指针-指针的意义作用

模拟strlen的实现,strlen功能是求出字符串的长度,不包括\0.

#include
#include

int ax_strlen(char *s )//指针s指向第一个字符的地址
{
	/*int count = 0;
	while (*s !='\0')
	{
		count++;
		s++;
	}
	return count;*/

	//第二个写法
	//思路是要找到第一个字符的地址以及\0的地址,然后两个指针相减即可。
	char* weikun = s;
	while (*s != '\0')
	{
		s++;
	}
	return s - weikun;

}
int main()
{
	//指针的运用
	//求字符串的长度
	int len = strlen("sdasd");
	printf("%d \n", len);

	int len1 = ax_strlen("sdasdsjhfhjdhff"); //这里传过去的是首个字符的首地址,然后用指针来指向第一个的地址。
	printf("%d \n", len1);
	return 0;
}

7.3指针关系运算

//指针的比较
int ax[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = ax;//这个等价于&ax[0]
//int *p = &ax[0];
int m = sizeof(ax) / sizeof(ax[0]);
printf("%d\n", m);
//ax是数组名,数组名其实就是数组首元素的地址。
while (p < ax+m )//指针+整数=指针
{
	printf("%d  ", * p);
	p++;
}

8.野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

野指针的形成原因有

8.1指针未初始化

#include 
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
 *p = 20;
 return 0;
}

8.2指针越界访问

#include 
int main()
{
 int arr[10] = {0};
 int *p = &arr[0];
 int i = 0;
 for(i=0; i<=11; i++)
 {
 //当指针指向的范围超出数组arr的范围时,p就是野指针
 *(p++) = i;
 }
 return 0;
}

8.3指针指向的空间已被释放

#include 
int* test()
{
 int n = 100;
 return &n;
}
int main()
{
 int*p = test();
 printf("555"); //在这里加一个printf容易看出来*p得不到10,这是关于栈销毁方面知识。
 printf("%d\n", *p);
return 0;
}

8.4 避免野指针

不使用指针时候要置为null

如 int *p=null;

9.assert 断言

assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。

使用方法,用来检指针,或者用于调试都可以,下面举例检验指针的使用。

int a = 10;
int* p = &a;
p = NULL;
//使用记得加上头文件#include
assert(p != NULL);//如果符合条件,那么无影响,如果不符合就会报错,程序停止运行,并报出错误位置。

如要不使用assert加上这个即可,#define NDEBUG

使⽤ assert() 有⼏个好处:它不仅能⾃动标识⽂件和 出问题的⾏号,还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。

10.指针的使用和传址调用

函数又可以分为,传值调用和传参调用
区分何时使用这两种调用
 函数内部要改变函数外部的内容时候,那么就要用到传址调用
 函数内部只需要接受外部的内容是,这个时候用传值调用。
 

10.1传值调用

#include
int swap(int x, int y)
{
	return x + y;
}
int main()
{
int a = 10;
int b = 20;
int len = swap(a, b);//传值调用
printf("len = %d\n", len);
}

10.2传址调用

非指针不可例子,写⼀个函数,交换两个整型变量的值


#include
void swap1(int x, int y)
{
	int z = 0;
	z = x;
	x = y;
	y = z;
}
int main()
{
int a = 10;
int b = 20;
Swap(a, b);
printf("a=%d b=%d", a, b);
}

 

 很显然,并得不到我们想要的结果,为什么呢?

深入理解指针(一)_第5张图片

经过调试可以知道,a、b和x、y的地址都不一样,改变的x,y,但是a和b并没有改变,所以传值是无法实现的,只有通过传a和b的地址,然后再修改才可以。下面传址调用来实现。

#include
void Swap(int* x, int* y)
{
	int z = 0;
	z = *x;
	*x = *y;
	*y = z;
}
int main()
{
int a = 10;
int b = 20;
Swap(&a, &b);
printf("a=%d b=%d", a, b);
}

你可能感兴趣的:(c语言,visual,studio,算法)