深入理解c指针(三)

本文详细介绍了C语言中指针的运算,包括指针加整数、指针减指针以及关系运算,同时着重讨论了野指针的概念、成因及其规避方法,提供示例代码帮助理解。
摘要由CSDN通过智能技术生成

目录

四、指针运算

1、指针 ± 整数(同理)

2、指针 - 指针

3、指针的关系运算

五、野指针

1、野指针成因

2、如何规避野指针


四、指针运算

1、指针 ± 整数(同理)

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,就能找到后面的所有元素。

//使用指针打印数组内容

#include <stdio.h>
//指针+- 整数
int main()
{
  int arr[10] = {1,2,3,4,5,6,7,8,9,10};
  int *p = &arr[0];
  int i = 0;
  int sz = sizeof(arr)/sizeof(arr[0]);   //计算数组大小
for(i=0; i<sz; i++)
{
  printf("%d ", *p):
  p++;    //这⾥就是指针+整数
}
  return 0;
}

或者

#include <stdio.h>
//指针+- 整数
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
}
return 0;
}

//p = 0x009efc1c
//p+0 = 0x009efc1c
//p+1 = 0x009efc20
//p+2 = 0x009efc24
//p+3 = 0x009efc28
//
//
2、指针 - 指针

两指针相减的注意事项:

  1. 指针类型必须相同: 两个指针相减时,它们必须指向同一类型的数据。否则,结果是未定义的。例如,int* 类型的指针和 char* 类型的指针不能相减,因为它们指向的数据类型不同。

  2. 指针必须指向同一块分配的内存: 两个指针相减的结果表示它们之间的偏移量(以数据类型的大小为单位)。因此,两个指针必须指向同一块分配的内存区域。对于不同分配的内存块的指针,相减的结果是未定义的。

  3. 递增或递减的顺序: 如果指针 p1 指向内存中的位置比指针 p2 更高(地址更大),那么 p1 - p2 的结果将是正数。反之,如果 p1 指向内存中的位置比 p2 更低(地址更小),那么结果将是负数。

  4. 指针必须指向同一数组的元素: 如果两个指针指向同一数组的不同元素,则相减的结果将表示两个元素之间的距离(以数组元素大小为单位)。相减的结果不是两个指针之间的偏移量,而是两个元素之间的偏移量。

#include <stdio.h>

int main()
{
	//指针 - 指针 = 地址 - 地址
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	printf("%d\n", &arr[9] - &arr[0]);

	return 0;
}

        指针 - 指针实际上就是地址 - 地址,且指针 - 指针的绝对值是指针和指针之间的元素个数。(若小地址 - 大地址,&arr[0] - &arr[10] ,结果为-9)  

       将上述代码改写,进行调试可以发现,两个地址分别为16进制的 008ffc58 和 008ffc34,二者相减为24,对应10进制的36。同时以数据类型的大小为单位,int类型大小4字节,故最后结果为9。

理解:指针 ± 整数 = 指针,指针 ± 指针 = 整数。

例:通过指针求字符串长度 。

       在库函数中的 strlen 函数就是用来求字符串长度,其统计的是 \0 前面出现的字符的个数。通过指针求取字符串长度同理,但是注意:在使用函数对字符串传参时传的是首字符的地址。

#include <stdio.h>
#include<string.h>

int new_strlen(char* s)
{
	int count = 0;
	while (*s != '\0')
	{
		count++;
		s++;
	}
	return count;

}
int main()
{
	int len = new_strlen("abcde!");
	printf("%d\n", len);

	return 0;
}

方法二:
#include <stdio.h>
#include<string.h>

int new_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;

}
int main()
{
	int len = new_strlen("abcde!");
	printf("%d\n", len);

	return 0;
}

方法三:也可以去掉while中的判断直接写成while(*p),因为\0的ASCII码值为0
#include <stdio.h>
#include<string.h>

int new_strlen(char* s)
{
	char* p = s;
	while (*p)
		p++;
	return p - s;

}
int main()
{
	int len = new_strlen("abcde!");
	printf("%d\n", len);

	return 0;
}

3、指针的关系运算

注:在深入学习c指针(二)的指针 ± 整数中提到,不同类型指针变量 ± 1 跳过的字节数不同,比如int* 类型则跳过4个字节。

//指针的关系运算
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);  //10
	while (p < arr + sz) //指针的⼤⼩⽐较   arr是数组名,而数组名是数组首元素的地址,故arr 等价于 &arr[0]
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}

五、野指针

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

1、野指针成因

a、指针未初始化

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

b、指针越界访问

#include <stdio.h>
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;
}

c、指针指向的空间释放

#include <stdio.h>
int* test()
{
  int n = 100;
  return &n;
}
int main()
{
  int*p = test();
  printf("%d\n", *p);
  return 0;
}
2、如何规避野指针

a、指针初始化

 b、避免指针越界:一个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

c、指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性

       当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL。

int main()
{
int arr[10] = {1,2,3,4,5,67,7,8,9,10};
int *p = &arr[0];
for(i=0; i<10; i++)
{
*(p++) = i;
}
//此时p已经越界了,可以把p置为NULL
p = NULL;
//下次使⽤的时候,判断p不为NULL的时候再使⽤
//...
p = &arr[0];//重新让p获得地址
if(p != NULL) //判断
{
//...
}
return 0;
}

实例:

方法一:选择拼接法

void leftRound(char * src, int time)
{
	int len = strlen(src);
	int pos = time % len; //断开位置的下标
	char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp
	
	strcpy(tmp, src + pos); //先将后面的全部拷过来
	strncat(tmp, src, pos); //然后将前面几个接上
	strcpy(src, tmp); //最后拷回去
}

方法二:先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)

void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序
{
	int i, j;
	char tmp;

	for (i = start, j = end; i < j; i++, j--)
	{
		tmp = str[i];
		str[i] = str[j];
		str[j] = tmp;
	}
}

void leftRound(char * src, int time)
{
	int len = strlen(src);
	int pos = time % len;
	reverse_part(src, 0, pos - 1); //逆序前段
	reverse_part(src, pos, len - 1); //逆序后段
	reverse_part(src, 0, len - 1); //整体逆序
}


方法三:与方法一相似,先打印后半段,再打印前半段

#include <stdio.h>
#include<string.h>

//计算字符串中的字符数
int new_strlen(char* s)    
{
	int count = 0;
	while (*s != '\0')
	{
		count++;
		s++;
	}
	return count;

}

//字符串左旋
void leftround(char* p, int* n)
{
	int i = 0,j=0;
	int m = new_strlen(p);
	char* p0 = p;
	for (i = 0; i < m-*n; i++)
	{
		printf("%c", *(p + i+*n));
	}
	for (j = 0; j < *n; j++)
	{
		printf("%c", *(p + j));
	}
}
int main()
{
	char ch[50];
	gets(ch);

	//左旋k个字符
	int k = 0;
	printf("请输入左旋字符个数:\n");
	scanf_s("%d", &k);
	leftround(ch, &k);
	return 0;
}

 

跳转其他页面:

深入理解c指针(一)

深入理解c指针(二)

深入理解c指针(三)

深入理解c指针(四)

深入理解c指针(五)

深入理解c指针(六)

深入理解c指针(七)

 

  • 39
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

做完作业了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值