指针详解2

指针详解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 = &num;

        *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 = &num;

        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 = &num;

        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 = &num;

        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 = &num;

        *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;

}

程序不能运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值