06--指针(1)

内存地址

  • 地址的单位字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte = 8bits
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。

基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。
  • 多字节数据:对于多字节数据而言,其地址是其所有字节中编号最小的那个,称为基地址(入口地址)。

取址符

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址
  • 例如:
    int a = 100;
    printf("整型变量 a 的地址是: %p\n", &a);
    
    char c = 'x';
    printf("字符变量 c 的地址是: %p\n", &c);
    
    double f = 3.14;
    printf("浮点变量 f 的地址是: %p\n", &f);

  • 注意:
    • 虽然不同的变量的尺寸是不同的,但是他们的地址的尺寸确是一样的。
      • 不管是任何类型的数据,他们的内存地址大小都是一样的,取决于当前系统的位数(32位系统下所有的内存地址都是只有 4个字节, 64位系统所有的内存地址都是 8个字节)
    • 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。

指针基础

  • 指针的概念:
    • 地址。比如 &a 是一个地址,也是一个指针,&a 指向变量 a。
    • 专门用于存储地址的变量,又称指针变量。
  • 指针的定义:
    int    *p1; // 用于存储 int  型数据的地址,p1 被称为 int  型指针,或称整型指针
    char   *p2; // 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
    double *p3; // 用于存储double型数据的地址,p3 被称为 double 型指针

  • 如何初始化 及赋值
    int a = 123 ;
    double d = 234523.342563456 ;
    
    int *p_a = &a ; // 定义了一个指针变量 p_a 并把变量 a 的地址存入到 该变量中
    double * p_d ; // 定义了一个指针当没有赋值(野指针)
    p_d = &d ;  // 给指针变量赋值

如何通过指针来访问内存中的数据:


// * 解引用  作用是取得指针所指向内存中的值 【去地址】
*p_a = 321 ; // 把p_a 所指向的内存的数据修改位 321 

printf("a:%d\n" , a);

printf("*p_a:%d\n" , *p_a);
printf("*p_d:%lf\n" , *p_d);

 野指针

  • 概念:指向一块未知区域的指针,被称为野指针。野指针是危险的。
  • 危害:
    1. 引用野指针,相当于访问了非法(未知)的内存,常常会导致段错误(segmentation fault)
    2. 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
  • 产生原因:
    1. 指针定义之后,未初始化(随机值)
    2. 指针所指向的内存,被系统回收
    3. 指针越界(指针 + - 运算后)
  • 如何防止:
    1. 指针定义时,及时初始化
    2. 绝不引用已被系统回收的内存
    3. 确认所申请的内存边界,谨防越界

空指针

很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针无法立即为其分配一块恰当的内存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存。

  • 概念:空指针即保存了零地址的指针,亦即指向零地址的指针。
  • 示例:
    // 1,刚定义的指针,让其指向零地址以确保安全:
    char *p1 = NULL;
    int  *p2 = NULL;
    
    // 2,被释放了内存的指针,让其指向零地址以确保安全:
    char *p3 = malloc(100); // a. 让 p3 指向一块大小为100个字节的内存
    ....
    free(p3);               // b. 释放这块内存,把内存归还给系统,此时 p3 相当于指向了一块非法内存
    p3 = NULL;              // c. 让 p3 指向零地址

    指针运算

  • 指针加法意味着地址向上移动若干个目标(指针的类型)
  • 指针减法意味着地址向下移动若干个目标(指针的类型)
  • 示例:
    int  a = 100;
    int *p = &a; // 指针 p 指向整型变量 a
    
    int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
    int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)

 

指针与数组的第一个关系:

使用一个整形指针 来指向一个整形数组中的某一个元素;

int arr [5] = { 1, 2, 3, 4, 5} ;
int * p = &arr[2];

如何通过指针p 来访问整个数组?
p-2  得到数据1 的地址
p-1  得到数据2 的地址
p+1  得到数据4 的地址

*p-2  先*p 得到数据 3 , 然后再 3-2 = 1  


*(p-1)  得到数据2 的值
*(p+1)  得到数据4 的值

 阅读下面两段代码,分析程序的输出内容。

  • 代码片段一:
    int *p ;  // 野指针
    int a[2][2] = {1, 2, 3, 0}; // a[0][0] = 1  a[0][1] = 2   a[1][0] = 3  a[1][1] = 0
    p = a[0];
    printf("%d, %d", *p, *(p+1)); // 输出什么?
    输出:1 2 
    
    
    代码片段二:
    int *p; //      a[0]     a[1]
    int a[2][2] = {{1, 0}, {2, 3}};
    p= a[0];
    printf("%d, %d", *p, *(p+1)); // 输出什么?
    输出: 1 0 

  • 假设有如下声明:
  • 则下列语句中那些是正确的,哪些是错误的?原因是什么?

    float a[3];
    float b[2][3];
    float c = 2.2, *p; 
    
    a[3] = c;  //  a是一个数组总共有3个元素   a[3]则访问的是第4个元素 , 因此该操作有越界的问题
    a = c;     //  a 是一个数组名, 不能直接对数组名进行赋值
    scanf("%f", &a);  // a是一个数组 , &a 则是获得一个数组的地址,因此该地址的类型为数组的类型 , 类型不匹配
    printf("%f", a[3]); // a是一个数组总共有3个元素   a[3]则访问的是第4个元素 , 因此该操作有越界的问题
    b[1][2] = a[2]; // 正确 
    b[1] = a; // b 是一个二维数组, 因此b[1]是该数组中的第二个元素 ,而该数组b 中每一个元素都是数组, 因此b[1] 也是一个数组, 不允许直接对数组进行赋值
    p = c;  // 存放地址的变量 = 浮点数  类型完全不匹配
    p = a;  // 正确   a是一个数组, 而变量名 a 在当前的语境下 代表的是该数组的首元素的首地址

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值