4-指针和数组

Posted guard9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4-指针和数组相关的知识,希望对你有一定的参考价值。

1- 指针和一维数组

  • 一维数组名:一维数组名是个指针常量,他存放的是一维数组第一个元素的地址。
#include <stdio.h>
/*

总结:
    一维数组名
    一维数组名是个指针常量
    它存放的是一维数组第一个元素的地址
*/
int main(void)

    int a[5]; //a是数组名  5是数组元素的个数 元素就是变量  a[0]  -- a[4]
//  int a[3][4]; //3行4列 a[0][0]是第一个元素 a[i][j]第i+1行j+1列
    int b[5];
    
    //a = b;//error a是常量
    printf("%#X\\n", &a[0]);
    printf("%#X\\n", a);

    return 0;

常量是不能被改变的,也就是说,一维数组名是不能被改变的。
数组名a存放的是一维数组第一个元素的地址,也就是a = &a。

2-下标和指针的关系

**如果p是个指针变量,则p[i]永远等价于*(p+i)**

# include <stdio.h>

int main(void)

    int a[5] = 1,2,3,4,5;
    int i;

    for (i=0; i<5; ++i)
        printf("%d\\n", a[i]);  //a[i] == *(a+i) 

    return 0;

3- 确定一个一维数组需要几个参数

# include <stdio.h>
/*
 a是个指针变量,所以上面局部函数f的pArr则要定义成指针函数才可以,而len则是int类型。代表接收的是整型的数字。
*/
//f函数可以输出任何一个一维数组的内容
void f(int * pArr, int len)

    int i;

    for (i=0; i<len; ++i)
        printf("%d  ", *(pArr+i) );  //*pArr *(pArr+1) *(pArr+2) 
    printf("\\n");


int main(void)

    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;

/*
1、一定要明白 10行的pArr[3] 和17行 19行的a[3] 是同一个变量因为数组a的名称代表的是a的第一个元素的地址,所以在函数f中所定义的指针变量pArr和a是相同的,因为a也是指针类型。

2、也就是说pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2).所以在f函数中pArr[3]=a[3],所以第二个printf输出的结果是88.总结:pArr[i] = a[i] = *(pArr+i) = *(a+i)

3、在没有学习指针时,可将a[3]认为是数组中第4个元素,这里下标也当成指针了,从首元素开始向后移动3个,即指向第4个元素。
*/

# include <stdio.h>

void f(int * pArr, int len)

    pArr[3] = 88;  //10行


int main(void)

    int a[6] = 1,2,3,4,5,6;
    
    printf("%d\\n", a[3]);  //17行
    f(a, 6);
    printf("%d\\n", a[3]);  //  19行

    return 0;
/*
因为数组a的名称代表的是a的第一个元素的地址,所以在函数f中所定义的指针变量pArr和a是相同的,因为a也是指针变量类型。也就是说pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2).我们在f函数中修改数组的值,相当于修改主函数中相对应的值。
*/
# include <stdio.h>

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");


int main(void)

    int b[6] = -1,-2,-3,4,5,-6;

    f(b, 6);

    b[i]

    return 0;

4- 何谓变量地址 / 一个指针变量占几个字节

  1. Sizeof(变量名/数据类型) 其返回值就是该变量或数据类型所占字节数
  2. ==一个指针变量无论其指向变量占几个字节,其本身所占大小都是4字节。==
  3. *p具体指向几个字节,要靠前面类型确定,如果为int则为4字节,如果double则占8字节。
  4. CPU 与 内存 交互时 有32根线,每根线只能是1或0两个状态,所有总共有232个状态。1 个状态 对应 一个单元。如全为0 全为1 等。内存中第一个单元,即32根线状态全为0。0000 0000 0000 0000 0000 0000 0000 0000 其大小为4字节
  5. 所有每个地址(硬件所能访问)的用4个字节保存(而不是一 位bit)
    一个变量的地址—用该变量首字节的地址表示。这也就是为什么指针变量始终只占4字节的原因。
# include <stdio.h>

int main(void)

    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;

5-动态内存分配

  1. 传统数组的缺点
  • 数组长度必须事先指定,而且只能是常整数,不能是变量

  • 例子 int a[5]; //必须事先指定,而且只能是常整数int len = 5; int a[len];//error

2.传统形式定义的数组,该数组的内存程序员无法手动释放数组一旦定义.

系统为数组分配的内存空间就会一直存在,除非数组所在的函数运行终止。在一个函数运行期间,系统为该函数中的数组分配的空间会一直存在。直到该函数运行完毕时,数组的空间才会被系统自动释放(不是清零)。

例子:void f(void) int a[5]=1,2,3,4,5;....

//数组a 占20个字节的内存空间,程序员无法手动编程释放它,数组a只能在f()函数结束被系统释放

3、数组的长度一旦定义,数组长度就不能再更改。数组的长度不能在函数运行的过程中动态的扩充或缩小.

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

A函数定义的数组,只有在A函数运行期间才可以被其他函数使用,

但A函数运行完毕后,A函数中的数组将无法在被其他函数使用。

# include <stdio.h>
/*
代码实现(传统数组的缺陷)
*/
void g(int * pArr, int len)

    pArr[2] = 88;  //pArr[2] == a[2]


void f(void)

    int a[5] = 1,2,3,4,5;  //20个字节的存储空间程序员无法手动编程释放它,
                             //它只能在本函数运行完毕时由系统自动释放
    g(a, 5);            
    printf("%d\\n", a[2]);


int main(void)
   
    f();

    return 0;

为什么需要动态分配内存

很好的解决的了传统数组的4个缺陷

6-动态内存分配举例_动态数组的构造

/*
    2009年11月17日10:21:31
    malloc 是 memory(内存) allocate(分配)的缩写

*/
# include <stdio.h>
# include <malloc.h>  //不能省

int main(void)

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

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

    return 0;

7-malloc 实现图解

技术图片

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

void f(int * q)

    //*p = 200; //error
    //q = 200;  //200不是整型变量的地址,q只能存放整型变量的地址
    //**q = 200;  //error
    *q = 200;
    //free(q);  //把q所指向的内存释放掉  本语句必须的注释掉,否则会导致第20行的代码出错


int main(void)

    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    第20行

    return 0;

8-动态一维数组示例

图解
技术图片

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

int main(void)

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

    //动态的构造一维数组
    printf("请输入你要存放的元素的个数: ");
    scanf("%d", &len);
    pArr = (int *)malloc(4 * len);  //第12行  本行动态的构造了一个一维数组, 该一维数组的产度是len, 该数组的数组名是pArr, 该数组的每个元素是int类型  类似于 int pArr[len];
    
    //对一维数组进行操作,  如:对动态一维数组进行赋值
    for (i=0; i<len; ++i)
        scanf("%d", &pArr[i]);

    //对位一维数组进行输出
    printf("一维数组的内容是:\\n");
    for (i=0; i<len; ++i)
        printf("%d\\n", pArr[i]);
    
    free(pArr); //释放掉动态分配的数组
    

    return 0;

9-使用动态数组的优点:

1.动态数组长度不需要事先给定;

2.内存空间可以手动释放。

3.在程序运行中,动态内存空间大小可以通过realloc函数手动扩充或缩小

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

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

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

11-多级指针

技术图片

#### 多级指针代码实现
# include <stdio.h>

int main(void)

    int i = 10;
    int * p = &i;  //p只能存放int类型变量的地址
    int ** q = &p;  //q是int **类型, 所谓int **类型就是指q只能存放int *类型变量的地址,   
    int *** r = &q;  //r是int ***类型, 所谓int ***类型就是指r只能存放int ** 类型变量的地址, 

    //r = &p;  //error 因为r是int *** 类型,r只能存放int **类型变量的地址
    printf("i = %d\\n", ***r); //输出结果是10  只有 ***r才表示的是i, *r或 **r或 ****r代表的都不是i

    return 0;

多级指针代码实现2

#include <stdio.h>

//多级指针在函数中的应用
void f(int ** q)

    **q = 100;  //*q就是p


void g()

    int i = 10;
    int * p = &i;
    printf("i = %d  *p = %d\\n", i, *p);

    f(&p); //p是int *类型  &p就是int ** 类型
    
    printf("i = %d  *p = %d\\n", i, *p);


       int main(void) 

    g();
    return 0;

12-静态变量不能跨函数使用

/*
内存越界:程序访问了一个不该被访问的内存,函数内的静态空间,不能被其他函数调用访问。函数中的内存空间,随函数终止而被释放。内存空间释放后的内容不属于其他函数,其他函数无权限访问。但释放后的内存空间的地址是可以被其他函数读取的。但指针变量可以存贮任何函数中静态内存空间的地址,p都能存垃圾,p想存谁存谁。只是它此时已经没有权限读取(访问) i这个地址的数据了,出错。。
*/
# include <stdio.h>

void f(int ** q) //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节

    int i = 5;
    //*q等价于p  q和**q都不等价于p
    //*q = i; //error 因为*q = i; 等价于 p = i; 这样写是错误的
    *q = &i;  // p = &i;


int main(void)

    int *p;  //13行
    
    f(&p);
    printf("%d\\n", *p);  //16行  本语句语法没有问题,但逻辑上有问题

    return 0;

13- 动态内存可以跨函数使用案例

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

void f(int ** q) //*q等价p 已经声明了q的类型为int **

    *q = (int *)malloc(sizeof(int)); //sizeof(数据类型) 返回值是该数据类型所占的字节数
            //等价于 p = (int *)malloc(sizeof(int));
    //q = 5; //error
    //*q = 5; //p = 5;
    **q = 5; //*p = 5;


int main(void)

    int * p;

    f(&p);    //只有调用变量的地址,才能改变变量的值
    printf("%d\\n", *p);  
    
    //f函数中,没有free(q);所以动态空间仍然保留,动态空间中的内容可以被访问
    return 0;

?

以上是关于4-指针和数组的主要内容,如果未能解决你的问题,请参考以下文章

指针数组和数组指针

指针,数组指针,指针数组,函数指针解析

指针,数组指针,指针数组,函数指针解析

详解C语言指针

02指针进阶

数组指针和指针数组