指针详解2
整型指针加1则加4个字节,字符型指针加1则加一个字节
const修饰指针
const关键字
1.const修饰普通变量 ---- 被const修饰后就具有常属性,不能被修改
#include<stdio.h>
int main()
{
const int num = 0;
//在c语言中,这里的num是常变量,
//num的本质还是变量,因为有const修饰,
//编译器在语法上不允许修改这个变量
/在c++中,这里的num是常量
num = 20;//num就不能被修改,只能读取它的值(报错)
printf("%d\n",num);
return 0;
}
强行修改卡bug
#include<stdio.h>
int main()
{
const int num = 0;
int*p = #
*p = 20;
printf("%d\n",num);
return 0;
}
通过指针改值,但是,这种写法破坏规则,本来用const不修改num,与修改num矛盾。
2.const修饰指针变量
有两种情况
1-----(const放在*的左边)int const * p,const int * p
//意思:表示指针指向的内容,不能通过指针来改变了(*p),但是指针变量本身的值是可以改的(p)
#include<stdio.h>
int main()
{
const int num = 10;
int n = 100;
const int*p = #
p = &n;//即num的地址可以改变(可以执行)
*p = 20;//即num的值不能改变(报错)
printf("%d\n",num);
return 0;
}
结果为10
2-----(const放在*的右边)int * const p
//意思:表示指针指向的内容,能通过指针来改变了(*p),但是指针变量本身的值是不能改的(p)
#include<stdio.h>
int main()
{
const int num = 10;
int n = 100;
int * const p = #
p = &n;//即num地址不能改变(报错)
*p = 20;//即num的值可以改变(可以执行)
printf("%d\n",num);
return 0;
}
结果为:20
3-----const (*)两边都有 const int * const p
意思是:指针变量p不能被修改,指针变量p指向的内容也不能被修改。
#include<stdio.h>
int main()
{
const int num = 10;
int n = 100;
const int * const p = #
p = &n;//err
*p = 20;//err
printf("%d\n",num);
return 0;
}
指针运算
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+1));
}
return 0;
}
结果为:2222222222
倒着访问打印,i不变
#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 = sz-1;i>=0;i--)
{
printf("%d",*(p+1));//
}
return 0;
}
改成p变
#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("%4d",*p);
p++;
}
return 0;
}
结果为:1 2 3 4 5 6 7 8 9 10
倒着访问打印
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int * p = &arr[sz-1];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i = 0;i<sz;i++)
{
printf("%d",*p);
p--;
}
return 0;
}
结果为:10 9 8 7 6 5 4 3 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",arr[i]);
}
return 0;
}
2.指针减指针(地址减地址)的绝对值得到的指针和指针之间的元素个数
这种运算的前提条件是:两个指针指向同一块空间
#include<stdio.h>
int main()
{
int arr[10] = {0};
printf("%d\n",&arr[9] - &arr[0]);
return 0;
}
-----> 9
#include<stdio.h>
int main()
{
int arr[10] = {0};
printf("%d\n",&arr[0] - &arr[9]);
return 0;
}
-------> -9
//求字符串长度
#include<stdio.h>
int main()
{
char arr[] = "abcdef";
size_t len = strlen(arr);
printf("%zd\n",len);
return 0;
}
----->6
strlen 统计的是字符串中\0之前的字符个数
#include<stdio.h>
size_t my_strlen(char*s)
{
size_t count = 0;
while(*s !='\0')
{
count++;
s++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
//数组名其实是数组首元素的地址
size_t len = my_strlen(arr);//arr == &arr[0]
printf("%zd\n",len);
return 0;
}
----->6
另一写法(指针减指针)
#include<stdio.h>
size_t my_strlen(char*s)
{
char* start = s;
while(*s !='\0')
s++;//算字符串最后一个字符的地址
return s - start;
}
int main()
{
char arr[] = "abcdef";
//数组名其实是数组首元素的地址
size_t len = my_strlen(arr);//arr == &arr[0]
printf("%zd\n",len);
return 0;
}
----->6
3.指针的关系运算
数组随着下标的增长,地址是由低到高变化的
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
//使用指针的关系运算来打印数组的内容
int* p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);//20
while(p<&arr[sz])
{
printf("%d\n",*p);
p++;
}
return 0;
}
结果为:1 2 3 4 5 6 7 8 9 10
倒着打印
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
//使用指针的关系运算来打印数组的内容
int* p = &arr[sz-1];
int sz = sizeof(arr)/sizeof(arr[0]);//20
while(p>=&arr[0]) //p>=arr
{
printf("%d\n",*p);
p--;
}
return 0;
}
1.野指针:指针指向的位置是不可知的(随机的,不正确,没有明确限制的)
#include<stdio.h>
int main()
{
int* p;//p是没有初始化的,p里边存放的地址
//就是随机的
*p = 20;//非法访问,p就是野指针
return 0;
}
程序无法运行
正确写法
#include<stdio.h>
int main()
{
int num = 100;
int*p = #
*p = 20;
printf("%d\n",num);
return 0;
}
------> 20
2.指针的越界访问
#include<stdio.h>
int main()
{
int arr[10] = {0};
int*p = &arr[0];
int i = 0;
for(i = 0;i<=11;i++)//循环12次
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;-----> *p = i;
p++;
}
return 0;
}
程序无法执行
3.指针指向的空间释放
#include<stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n",*p);
return 0;
}
认识该代码
n为int* test()局部变量,&n出来后空间被释放
当p得到了这个地址时,p就是野指针了
4.如何规避野指针
1.指针初始化
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;//明确的指向
return0;
}
或不明确
#include<stdio.h>
int main()
{
int* p2 = NULL; //NULL - 空指针
return 0;
}
0--------数字0
'\0'------转义字符
‘0’-------字符0----ASCLL值是48
NULL - 空指针----0,0也是地址,这个地址是无法使用的
2.小心指针越界
#include<stdio.h>
int main()
{
int arr[10] = {0};
int*p = &arr[0];
int i = 0;
for(i = 0;i<=11;i++)//循环12次
{
//当指针指向的范围超出数组arr的范围时,
//p就是野指针
*(p++) = i;-----> *p = i;
p++;
}
return 0;
}
3.指针变量不再使用时,及时置NULL,指针使用之前检查有效性----->只要是NULL指针就不去访问,同时使用指针之前可以判断是否为NULL。
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
int* p2 = NULL; //NULL - 空指针
if(p2 !=NULL)
{
*p2 = 200;
}
return 0;
}
4.避免返回局部变量的地址
#include<stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n",*p);
return 0;
}
n为int* test()局部变量,&n出来后空间被释放
当p得到了这个地址时,p就是野指针了
assert断言
好处:它不仅能自动标识文件和出问题的行号,还有一种无需要更改代码就能开启或关闭assert()的机制
assert.h头文件定于了宏assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这常称为“断言”
assert(p != NULL);
会报错
#include<assert.h>
int main()
{
int* p = NULL;
assert(p != NULL);//假
return 0;
}
不报错
#include<assert.h>
int main()
{
int a = 10;
int* p = &a;
assert(p != NULL);
printf("%d\n",*p);
return 0;
}
---------> 10
还有一种无需要更改代码就能开启或关闭assert()的机制(一般在Debug中使用)
#define NDEBUG
加上后程序就不会崩溃
#include<assert.h>
int main()
{
int a = 10;
int* p = &n;
assert(p != NULL);
printf("%d\n",*p);
return 0;
}
结果为:10
指针的使用和传址调用
1.strlen的模拟实现
size_t strlen(count char * str);
头文件 #include<string.h>
//my_strlen 求字符串长度的,它是不期望s指向的字符串被修改的!用const来绑定*s=字母,使之不被改变
size_t my_strlen(const char* s)
{
int count = 0;
assert(s !=NULL);
while(*s != '\0') //while(*s)
{
count++;
s++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
size_t len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
2.传值调用和传址调用
练习:写一个函数,交换两个整型变量的值(传值调用)
#include<stdio.h>
void swap1(int x, int y)
{
int z = 0;
z = x;
x = y;
y = z;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
printf("交换前:a=%d b=%d",a,b);
//写一个函数,交换两个整型变量的值
swap1(a,b);
printf("交换后:a=%d b=%d",a,b);
}
该代码是无法完成交换,这是因为当实参传递给形参的时候,形参是有独立的空间的,对形参的修改不影响实参
传址调用
交换成功
void swap2(int *pa, int *pb)
{
int z = 0;
z = *pa; //z = a
*pa = *pb; //a = b
*pb = z; //b = z
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
printf("交换前:a=%d b=%d",a,b);
//写一个函数,交换两个整型变量的值
swap2(&a,&b);
printf("交换后:a=%d b=%d",a,b);
}
写一个函数,将两个数相加
int Add(int x,int y)
{
return x+y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
int c = Add(a,b);//传值调用
printf("%d\n",c);
return 0;
}
程序不能运行