C语言指针相关笔记

指针

#include <stdio.h>
int main()
{
    int * p;
    /*
    p是变量的名字,int *表示变量存放的是int类型变量的地址
    int * p:不表示定义了一个名字叫做*p的变量
         应该这样理解:p是变量名,p变量的数据类型是int *类型
         所谓int *类型 实际就是存放int变量地址的类型
   */
    int i = 3;

    p = & i;
    /*
        p保存了i的地址,因此p指向i
        p不是i,i也不是p,更准确说:修改p的值不影响i的值,修改i的值也不会影响p的值

        如果一个指针变量指向某个普通变量, 则
            *指针变量  就完全等同于 普通变量

        exp:
                如果p是个指针变量,并且p存放了普通变量i的地址,
                则指向了普通变量i
                *p  完全等同于 i
                或者说:在所有出现*p的地方都可以替换成i
                        在所有出现i的地方都可以替换成*p

                        *p 就是以p的内容为地址的变量
    */
    int j = *p; //等价于 j = i
    printf("%d %d\n", i, j);

    return 0;
}

指针就是地址,地址就是指针 //指针只是一个编号

地址就是内存单元的编号

指针变量是存放地址的变量

指针和指针变量是2个不同的概念

但是要注意:通常我们叙述是会把指针变量简称为指针,实际他们的含义并不一样

附注:

* 的含义

​ 1.乘法

​ 2.定义指针变量

​ int * p; // 定义了一个名字叫p的变量, int * 表示p只能存放int变量的地址

​ 3.指针运算符

​ 该运算符放在已经定义好的指针变量的前面,

​ 如果p是一个已经定义好的指针变量,

​ 则*p表示 以p的内容为地址的变量

如何通过被调函数修改主调函数普通变量的地址

​ 1.实参必须为该普通变量的地址

​ 2.形参必须为指针变量

​ 3.在被调函数中通过 *形参名 = …… 的方式就可以修改主调函数相关变量的值

指针和数组

1.指针和一维数组

​ 一维数组名

一维数组名是个指针常量

​ 他存放的是一维数组的第一个元素的地址

#include <stdio.h>
int main()
{
    int a[5];
    int b[5];

    printf("%#X\n", &a[0]);
    printf("%#X\n",a);

    return 0;
}

/*

0X60FEEC
0X60FEEC

Process returned 0 (0x0)   execution time : 0.017 s
Press any key to continue.
*/

​ 下标和指针的关系

​ 如果p是个指针变量,则

​ p[i] 永远等价于 *(p+i)

​ 确定一个一维数组需要几个参数:

​ 【如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息】

​ 需要两个参数:**数值的第一个元素地址;数组的长度 **

#include <stdio.h>
void f();//输出任何一个一维数组的内容

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int b[6] = {-1, -2, -3, 4, 5, 6};
    int c[100]  = {1, 99, 22, 33};

    f(a, 5); //a 是int *
    f(b, 6);
    f(c, 100);

    return 0;
}

void f(int * parr, int len)
{
    int i;

    for(i=0; i<len; ++i)
    {
        printf("%d  ", *(parr+i));
    }
    printf("\n");

    //* parr //因为 a 是 a[0] 的地址,所以* parr等价于a[0]
    //* (parr + 1)
    //数组的长度无法通过数组名获取
}

#include <stdio.h>

void f();

int main()
{
    int a[6] = {1,2,3,4,5,6};
    printf("%d\n", a[3]);//4
    f(a,6);
    printf("%d\n", a[3]);//88

    return 0;
}

void f(int * parr, int len)
{
    parr[3] = 88; //parr[3]等价于 *(parr+3) 等价于 a[3]
}

/*
4
88

Process returned 0 (0x0)   execution time : 1.232 s
Press any key to continue.
*/
#include <stdio.h>
void f();//输出任何一个一维数组的内容

int main()
{
   int a[5] = {1, 2, 3, 4, 5};
   int b[6] = {-1, -2, -3, 4, 5, 6};
   int c[100]  = {1, 99, 22, 33};

   f(b, 6);

   return 0;
}


void f(int * parr, int len)
{
   int i;

   for(i=0; i<len; ++i)
   {
       printf("%d  ", parr[i]); //*(parr+i) 等价于 parr[i] 也等价于 b[i] 也等价于 *(b+i)
   }
   printf("\n");

   //* parr //因为 a 是 a[0] 的地址,所以* parr等价于a[0]
   //* (parr + 1)
   //数组的长度无法通过数组名获取

}

//parr[2] == *(parr+2) == *(a+2) == a[2]
#include <stdio.h>
void f();


int main()
{
    int i = 5;

    f(&i); //要想改变一个变量的值,只能发送这个地址
    printf("i = %d\n", i);


    return 0;
}

void f(int * p)
{
    *p = 10; // i = 10
}

​ 指针变量的运算

​ 指针变量不能相加/相乘/相除,只能减

​ 如果两个指针变量指向的是同一连续空间中的不同存储单元,则这两个指针变量才可以相减

#include <stdio.h>

int main()
{
    int i = 5;
    int j = 10;
    int * p = &i;
    int * q = &j;

    int a[5];
    p = &a[1];
    q = &a[4];a
    printf("p和q所指向单元相隔%d个单元\n", q-p);

    //p-q无实际意义
    
    return 0;
}

一个指针变量到底占几个字节?

​ 预备知识:

​ sizeof(数据类型 ) 功能:返回值就是该数据类型所占的字节数

​ exp:sizeof(char) = 1 sizeof(int) = 4 sizeof(double) = 8

​ sizeof(变量名)

​ 假设P指向char类型变量(1个字节)

​ 假设q指向int类型变量(4个字节)

​ 假设r指向double类型变量(8个字节)

​ p q r 本身所占的字节数是否一样?

总结:一个指针变量,无论它指向的变量占几个字节,该指针本身只占4个字节

一个变量的地址 使用该字节变量首字节的地址来表示

#include <stdio.h>

int main()
{
    char ch = 'A';
    int i = 99;
    double x = 66.6;
    char * p = &ch;
    int * q = &i;
    double * r =&x;

    printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r) );

    return 0;
}

/*
4 4 4

Process returned 0 (0x0)   execution time : 0.049 s
Press any key to continue.
*/

2.指针和二维数组

指针和函数

指针和结构体

多级指针

专题:

动态内存分配

传统数组(静态数组)的缺点:

1.数组长度必须事先制定,且只能是常数,不能是变量

​ exp:int a[5]; //ok

​ int len = 5; int a[len]; //error 长度不能为一个变量

2.传统形式定义的数组,该数组的内存程序员无法手动释放,数组一旦定义,系统为该数组分配的存储空间就会一直存在,除非数组所在的函数运行结束

【在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在直到该函数运行完毕时,数组的空间才会释放】

3.数组的长度不能再函数运行的过程中动态的扩充或缩小

【数组的长度一旦定义,其长度就不能再更改】

4.A函数定义数组,在A 函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法在被其它函数使用

​ 传统方式定义的数组不能跨函数使用

为什么要动态分配内存

动态数组横好的解决了传统数组的4个缺陷

动态内存分配举例——动态数组的构造

1.malloc用法 1

/*
malloc 是momory(内存) allocate(分配)的缩写


*/
#include <stdio.h>
#include <malloc.h>

int main()
{
   int i = 5;//分配了4个字节 静态分配
   int * p = (int *)malloc(4);
   /*
       1.要是用malloc函数,必须添加malloc.h这个头文件
       2.malloc函数只有一个形参,并且形参是整型
       3.4表示请求为本程序分配4个字节
       4.malloc函数只能返回第一个字节的地址
       5. 分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
       6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的

   */

   *p = 5; //*p代表的就是一个int变量 ,只不过*p这个整型变量的内存分配方式和(int i = 5)的分配方式不同
   free(p); //free(p)表示把p所指向的内存给释放掉,p本身的内存是静态的,
            //不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时,由系统自动释放
   printf("111\n");

   return 0;
}

2.malloc用法 2

#include <stdio.h>
#include <malloc.h>

void f();

int main()
{
    int * p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
    *p = 10;

    printf("%d\n", *p); //10
    f(p); //p是int *类型
    printf("%d\n", *p); //200

    return 0;
}

void f(int * q) //p的内容发送给q,p和q的内容是一样的;q是p内容的一份拷贝/副本;
//p中存放的是4个字节的地址,那么q中也存放了这4个字节的地址,q也就指向了这4个字节的变量
//那么*q就代表了这4个字节,那么就可以把200赋给这4个字节
{
    //*p = 200; //error
    //q = 200; //200z只是一个整数,不是整型变量的地址
    //**q = 200; // *q代表的是这个整型变量,整型变量前不能加*(若p不是指针变量,是一定不能加*)
    *q = 200;
    //free(q); //把q所指向的内存释放
    /* free(q)后的输出结果
    10
    13309280
    */
}

3.动态一维数组

#include <stdio.h>
#include <malloc.h>

int main()
{
    int a[5]; //如果int占4个字节的话,则本数组总共包含有20个字节,每4个字节被当做一个int变量来使用
    int len, i;
    int * parr;

    printf("请输入你要存放的元素个数:");
    scanf("%d", &len);
    parr = (int *)malloc(4 * len); //动态的构造了一个一维数组,类似于 int parr[len];  该一维数组的数组名是parr,长度是len,每个元素是int类型
    //假设len = 5,则会产生20个字节的地址(空闲空间),由于parr为 int *类型,(parr是第一个地址)所以parr指向前4个字节
    printf("\n");

    //对一维数组进行操作
    //对动态一维数组赋值
    printf("输入一维数组的内容:");
    for (i=0; i<len; ++i)
        scanf("%d", &parr[i]);
    printf("\n");

    //对动态一维数组赋值
    printf("一维数组的内容是:");
    for (i=0; i<len; ++i)
        printf("%d ", parr[i]);
    printf("\n");

    free(parr); //释放掉动态分配的数组

    return 0;
}

/*
    realloc(parr,100); //扩充函数 将parr的内存扩充到100
    若parr = 50时,realloc(parr,100)会将parr扩充至100,且保留前50个字节
    若parr = 150时,realloc(parr,100)会将parr缩小至100,会丢失后50个字节数据,前100个保留
*/

静态内存和动态内存的比较

静态内存是由系统自动分配,系统自动释放,是在栈分配的,

动态内存是由程序员手动分配,手动释放,是在堆分配的

跨函数使用内存问题

1.多级指针

#include <stdio.h>

int main()
{
    int i = 10;
    int * p = &i;
    int ** q = &p;
    int *** r = &q;  //***r 等价于i

    //r = &p //error 因为r是int *** 类型,r只能存放int **类型变量地址
    printf("i = %d\n", ***r);

    return 0;
/*
    printf("%#X\n",i);
    printf("%#X\n",*p);
    printf("%#X\n",**q);
    printf("%#X\n",***r);

    0XA
    0XA
    0XA
    0XA
    Process returned 0 (0x0)   execution time : 0.015 s
    Press any key to continue.
   

    int *p :一级指针,表示p所指向的地址里面存放的是一个int类型的值
    int **p :二级指针,表示p所指向的地址里面存放的是一个指向int类型的指针(即p指向的地址里面存放的是一个指向int的一级指针)
    */
}

2.静态变量不能跨函数使用

#include <stdio.h>
void f(int ** q);


int main()
{
    int *p;

    f(&p);
    printf("%d\n", *p);//本语句语法没问题,但逻辑有问题
        //*p所指向的i变量已经释放了
        //静态变量不能够跨函数使用 或者说 静态变量在程序终止时不能够被其它函数使用
    return 0;
}

void f(int ** q) //q是个指针变量,无论q是什么类型的变量,都只占4个字节
//q存放的是p的地址,q指向p,*q就是p
{
    int i = 5;
    //*q等价于p q和**q都不等价于p
    //*q = i; //error 因为*q=i;等价于 p = i; 这样写是错误的
    *q = &i; //p = &i;
}

3.动态内存可以跨函数使用

#include <stdio.h>
#include <malloc.h>

void f(int ** q);

int main()
{
   int * p;

   f(&p);
   printf("%d\n", *p);

   return 0;
}


void f(int ** q)
{
   *q = (int *)malloc(sizeof(int));
   //sizeof(数据类型) 返回值是该数据类型所占字节数
   //等价于p = (int *)malloc(sizeof(int));  p指向的4个字节是动态分配的
   //动态分配是在堆中分配的,在堆内分配的,函数终止时不会释放
   //q = 5; //q存放的是p的地址,error
   //*q = 5;//*q就是p, p = 5,error
   **q = 5;//**q = *p, *p = 5, ok
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值