了解指针(1)

1.内存的使用和管理

内存被划分为一个一个的内存单元,每个内存单元的大小是1个字节,一个字节又是8个bit位

每个内存单元都有一个编号,虽然没有明确写出编号,但我们给地址一个编号就能自动找到那个内存单元

内存单元的编号==地址==指针     

2.指针变量和地址

2.1取地址操作符---&

C语言中创建变量其实就是向内存中申请空间

创建了整型变量a,内存中申请4个字节,用于存放10,每个字节都有地址

&------取地址操作符,拿到变量a的地址。取出的是a所占4个字节中地址较小的字节的地址

变量的名字仅仅是给程序员看的,编译器不看名字,编译器是通过地址找内存单元的

2.2指针变量和解引用操作符(*)

2.2.1指针变量

指针变量也是一种变量,这种变量就是用来存放地址(指针)的,存放在指针变量中的值理解为地址

在指针变量眼里什么都是地址

关于指针pa有三个相关的值

1.pa,pa里面放着一个地址

2.*pa,pa指向的那个对象

3.&pa,表示的是pa变量的地址

2.2.2解引用操作符(间接访问操作符)

pa中存放的是a的地址,对指针变量解引用-----*pa,得到的就是a变量

我们可以通过指针变量改变a的值

2.2.3指针变量的大小

指针变量是用来存放地址的,一个地址的内存需要多大空间,那么指针变量的大小就是多大

指针变量的大小和类型是没有关系的,只要是指针类型的变量在相同环境下,其大小是相同的

32位环境(x86),地址是32个bit位(四个字节)

64位环境(x64),地址是64个bit位(八个字节)

3.指针变量的意义

3.1指针的解引用

int类型会将n的四个字节全部改为0,但是char类型只是将第一个字节改为0

总结:指针的类型决定了对指针解引用的时候有多大的权限(一次能操作几个字节)

3.2指针+-整数

char*类型的指针变量+1跳过1个字节;

int*类型的指针变量+1跳过了4个字节

int*p;

p+i 是跳过i*sizeof(int)个字节

总结:指针的类型决定了指针向前或者向后走一步有多大(距离)

3.3 void*类型的指针

可以接受不同类型的地址,但是void*类型的指针不能直接进行指针的+-整数和解引用的运算

4.const修饰指针

4.1 修饰变量-------变量是可以修改的,如果把变量的一个地址交给一个指针变量,通过指针变量可以修改这个变量,但是我们可以加上一些限制

const修饰变量的时候,叫常变量

这个被修饰的变量本质上还是变量,只是不能被修改

4.2const修饰指针变量

1)const在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变

     但是指针变量本身的内容可变。

2)const在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容可以通过指针改变。

5.指针运算

指针+-整数

指针-指针(指针+指针没有意义)

指针的相关运算

5.1指针+-整数

指针类型决定了指针+1的步长,决定了指针解引用的权限

5.2指针-指针

两个指针相减的绝对值是指针和指针之间的元素个数

前提:两个指针指向的是同一个空间

5.3指针的关系运算

6.野指针

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

6.1野指针的成因

1)指针未初始化

int main()

{

   int * p;//局部变量指针未初始化,默认为随机值

  *p =20;

   return 0;

}

2)指针越界访问

int main()

{

    int arr[10]={0};

    int * p=&arr[0];

    for(int i=0;i<=11;i++)

       {

              *(p++)=i;//当指针指向的范围超出数组arr的范围时,p就是野指针

        }

        return 0;

}

3)指针指向的空间释放

int * text()

{

       int n=100;

       return &n;

}

int mian()

{

     int * p=text();//野指针

     printf("%d\n",*p)

     return 0;

}

6.2怎么规避野指针

1.指针初始化

如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL(C语言中定义的一个标识符常量,其值为0,0也是地址,只是这个地址是无法使用的,读写该地址会报错)

int * p=NULL;

空指针是无法直接访问的

2.小心指针越界

3.指针变量不再使用时,及时置为NULL,指针使用前检查有效性

4.避免返回局部变量的地址

7.assert断言

assert.h头文件定义了宏asser(),用于在运行时确保程序符合指定条件,如果不符合就报错终止运行,这个宏常被称为“断言”。

如果表达式为真,就正常运行什么都不发生。如果表达式为假就会显示没有通过的表达式,以及包含这个表达式的文件名和行号(好处之一)

另外一个好处就是不需要更改代码就可以开启或关闭assert()的机制

确认程序没有问题不需要再做断言,定义一个宏NDEBUG

一样的代码,这时就不会发生错误

我们一般在Debug中使用,在vs中,release中就直接优化掉了

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

8.1模拟实现strlen

求字符串\0之前的字符个数

size_t strlen (const char * str)

size_t是一种无符号整型

指向的字符串不期望被修改

8.2传值调用和传址调用

传址调用,可以让函数和主函数之间建立真正的联系,在函数内部可以修改主函数中的变量;所以如果函数中只是需要主调函数中的变量值来实现,就可以采用传值调用。

如果函数内部要修改主调函数中的值,就需要传址调用

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值