目录
四、指针运算
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、指针 - 指针
两指针相减的注意事项:
-
指针类型必须相同: 两个指针相减时,它们必须指向同一类型的数据。否则,结果是未定义的。例如,
int*
类型的指针和char*
类型的指针不能相减,因为它们指向的数据类型不同。 -
指针必须指向同一块分配的内存: 两个指针相减的结果表示它们之间的偏移量(以数据类型的大小为单位)。因此,两个指针必须指向同一块分配的内存区域。对于不同分配的内存块的指针,相减的结果是未定义的。
-
递增或递减的顺序: 如果指针
p1
指向内存中的位置比指针p2
更高(地址更大),那么p1 - p2
的结果将是正数。反之,如果p1
指向内存中的位置比p2
更低(地址更小),那么结果将是负数。 -
指针必须指向同一数组的元素: 如果两个指针指向同一数组的不同元素,则相减的结果将表示两个元素之间的距离(以数组元素大小为单位)。相减的结果不是两个指针之间的偏移量,而是两个元素之间的偏移量。
#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;
}
跳转其他页面: