C语言全方位讲解指针与地址和数组函数堆空间的关系

目录
  • 一、一种特殊的变量-指针
  • 二、深入理解指针与地址
  • 三、指针与数组(上)
  • 四、指针与数组(下)
  • 五、指针与函数
  • 六、指针与堆空间
  • 七、指针专题经典问题剖析

一、一种特殊的变量-指针

指针是C语言中的变量

  1. 因为是变量,所以用于保存具体值
  2. 特殊之处,指针保存的值是内存中的地址
  3. 内存地址是什么?
  • 内存是计算机中的存储部件,每个存储单元有固定唯一的编号
  • 内存中存储单元的编号即内存地址

需要弄清楚的事实

  • 程序中的一切元素都存在于内存中,因此,可通过内存地址访问程序元素。

内存示例

获取地址

  • C语言中通过 & 操作符获取程序元素的地址
  • & 可获取变量,数组,函数的起始地址
  • 内存地址的本质是一个无符号整数(4字节或8字节)

下面看一个简单的例子:

#include<stdio.h>
int main()
{
    int var = 0;
    printf("var value = %d\n", var);
    printf("var address = %p\n", &var);
    return 0;
}

下面为输出结果:

注意事项

  • 只有通过 内存地址+长度 才能确定一个变量中保存的值。

指针定义语法:

type *point;

  • type - 数据类型,决定访问内存时的长度范围
  • * 标志,意味着定义一个指针变量
  • pointer 变量名,遵循C语言命名规则

例如:

int main()
{
    char* pChar;
    short* pShort;
    int* pInt;
    float* pFloat;
    double* pDouble;
    return 0;
}

指针内存访问:

* pointer

  • 指针访问操作符(*)作用于指针变量即可访问内存数据
  • 指针的类型决定通过地址访问内存时的长度范围
  • 指针的类型统一占用 4 字节或 8 字节

即:sizeof(type*) == 4或 sizeof(type*) == 8

下面看一段代码,感受一下:

#include <stdio.h>
int main()
{
    int var = 0;
    int another = 0;
    int* pVar = NULL;
    printf("1. var = %d\n", var);
    printf("1. pVar = %p\n", pVar);
    pVar = &var;  // 使用指针保存变量的地址
    *pVar = 100;  // *pVar 等价于 var , var = 100;
    printf("2. var = %d\n", var);
    printf("2. pVar = %p\n", pVar);
    pVar = &another;  // 改变了 pVar 的指向,使得 pVar 保存 another 的地址
    *pVar = 1000;     // another = 1000;
    printf("3. another = %d\n", another);
    printf("3. pVar = %p\n", pVar);
    printf("4. add ==> %d\n", var + another + *pVar);   // 100 + 1000 + 1000  ==> 2100
    return 0;
}

下面为输出结果:

注意 NULL 地址为 00000000

小结

  • 指针是C语言中的变量(本质为容器)
  • 指针专用于保存程序元素的内存地址
  • 可使用 * 操作符通过指针访问程序元素本身
  • 指针也有类型,指针类型由 数据类型+* 构成

二、深入理解指针与地址

灵魂三问

  • 指针类型和普通类型之间的关系是什么?
  • 何看待“内存地址+长度才能访问内存中的数据”?
  • 不同类型的指针可以相互赋值吗?

初学指针的军规

  • Type* 类型的指针只保存 Type 类型变量的地址
  • 禁止不同类型的指针相互赋值
  • 禁止将普通数值当作地址赋值给指针

注意:指针保存的地址必须是有效地址

下面看一段代码:

#include <stdio.h>
int main()
{
    int i = 10;
    float f = 10;
    int* pi = &f;    // WARNING
    float* pf = &f;  // OK
    printf("pi = %p, pf = %p\n", pi, pf);
    printf("*pi = %d, *pf = %f\n", *pi, *pf);
    pi = i;   // WARNING
    *pi = 110;  // OOPS
    printf("pi = %p, *pi = %d\n", pi, *pi);
    return 0;
}

下面为输出结果:

这个程序犯了两个错误:

1、将不同类型的指针相互赋值,虽然 int 类型的指针变量保存的地址是对的,但是其所保存的值是错的。

2、 将普通数值当作地址赋值给指针,这会导致严重的错误,不能正确输出

编写函数交换两个变量的值

  • 想要编写函数交换变量的值,那么,必须有能力在函数内部修改函数外部的变量!!!

看下面的代码:

#include <stdio.h>
void func(int* p)
{
    *p = 100;   // 修改内存中 4 字节的数据,即:修改一个整型变量的值
}
void swap(int* pa, int* pb)
{
    int t = 0;
    t = *pa;
    *pa = *pb;
    *pb = t;
}
int main()
{
    int var = 0;
    int a = 1, b = 2;
    printf("1. var = %d\n", var);
    func( &var );
    printf("2. var = %d\n", var);
    printf("3. a = %d, b = %d\n", a, b);
    swap(&a, &b);
    printf("4. a = %d, b = %d\n", a, b);
    return 0;
}

下面为输出结果:

小结论

可以利用指针从函数中“返回”多个值 (return只能返回一个值)!!

下面看一段代码:

#include <stdio.h>
int calculate(int n, long long* pa, long long* pm)
{
    int ret = 1;
    if( (1 <= n) && (n <= 20) )
    {
        int i = 0;
        *pa = 0;
        *pm = 1;
        for(i=1; i<=n; i++)
        {
            *pa = *pa + i;
            *pm = *pm * i;
        }
    }
    else
    {
        ret = 0;
    }
    return ret;
}
int main()
{
    long long ar = 0;
    long long mr = 0;
    if( calculate(5, &ar, &mr) )
        printf("ar = %lld, mr = %lld\n", ar, mr);
    return 0;
}

下面为输出结果:

这段代码中的子函数通过指针,计算了1加到5以及1乘到5的值,这就间接地通过指针从子函数“返回”多个值

小结

指针是变量,因此赋值时必须保证类型相同

指针变量保存的地址必须是有效地址

通过指针参数

  • 一能够实现函数交换变量的值
  • 一能够从函数中“返回”多个值

三、指针与数组(上)

问题

  • 数组的本质是一片连续的内存,那么,数组的地址是什么?如何获取?

一些事实

  • 使用取地址操作符&获取数组的地址
  • 数组名可看作一个指针,代表数组中 0 元素的地址
  • 当指针指向数组元素时,可进行指针运算(指针移动)

深入理解数组地址( int a[]= {1, 2, 3, 4, 5}; )

  • &a 与 a 在数值上相同,但是意义上不同
  • &a 代表数组地址,类型为:int(*)[5]
  • a 代表数组0号元素地址,类型为: int*
  • 指向数组的指针: int (*pName)[5] = &a;

下面看一段代码:

#include <stdio.h>
int main()
{
    int a[] = {1, 2, 3, 4, 0};
    int* p = a;  // a 的类型为 int*, &a[0] ==> int*
    int (*pa) [5] = &a;
    printf("%p, %p\n", p, a);
    p++;
    *p = 100;  // a[1] = 100;
    printf("%d, %d\n", *p, a[1]);
    printf("%p, %p\n", &a, a);
    p = pa;   // WARNING  !!!!
    p = a;
    while( *p )
    {
        printf("%d\n", *p);
        p++;
    }
    return 0;
}

下面为运行结果:

需要注意的是,p 和 pa不是一个指针类型,所以令 p = pa 这种做法是不正确的。

注意

  • 数组名并不是指针,只是代表了0号元素的地址,因此可以当作指针使用。

四、指针与数组(下)

指针与数组的等价用法

假如:

int a[ ] = {1, 2,3, 4,5}

int* p = a;

则以下等价:

a[i] <--> *(a + i) <--> *(p + i) <--> p[i]

下面看一段代码,加深理解:

#include <stdio.h>
int main()
{
    int a[] = {1, 2, 3, 4, 5};
    int* p = a;
    int i = 0;
    // a[i] <==> *(a+i) <==> *(p+i) <==> p[i]
    for(i=0; i<5; i++)
    {
        printf("%d, %d\n", a[i], *(a + i));
    }
    printf("\n");
    for(i=0; i<5; i++)
    {
        printf("%d, %d\n", a[i], p[i]);
    }
    printf("\n");
    for(i=0; i<5; i++)
    {
        printf("%d, %d\n", p[i], *(p + i));
    }
    printf("\n");
    printf("a = %p, p = %p\n", a, p);
    printf("&a = %p, &p = %p\n", &a, &p);
    return 0;
}

下面为输出结果:

这里可以看到 a和 p的地址不同,因为它们是两个不同的指针变量。

字符串拾遗

  • 字符串常量是 char* 类型,一种指针类型

指针移动组合拳:

int v = *p++;

解读:

指针访问操作符(*)和自增运算操作符(++) 优先级相同

所以,先从p指向的内存中取值,然后p进行移动

等价于:

int v = *p;

p++;

下面看一段代码,体会一下:

#include <stdio.h>
int main()
{
    int a[] = {1, 2, 3};
    int* p = a;
    int v = *p++;
    char* s = NULL;
    printf("%p\n", "D.T.Software");
    printf("%p\n", "D.T.Software");
    printf("v = %d, *p = %d\n", v, *p);
    printf("First = %c\n", *"D.T.Software");
    s = "D.T.Software";
    while( *s ) printf("%c", *s++);
    printf("\n");
    return 0;
}

下面为输出结果:

因为D.T.Software 在全局数据区的起始地址一样,所以两次打印出来的地址一样。

小结

  • 数组名可看作一个指针,代表数组中0元素的地址
  • &a与a在数值上相同,但是意义上不同
  • C语言中的字符串常量的类型是 char *
  • 当指针指向数组元素时,才能进行指针运算

五、指针与函数

问题

  • 函数调用时会跳转到函数体对应的代码处执行,那么,如何知道函数体代码的具体位置?

深入函数之旅

  • 函数的本质是一段内存中的代码(占用一片连续内存)
  • 函数拥有类型,函数类型由返回类型和参数类型列表组成
  • 例:
函数申明 类型
int sum(int n); int (int)
void swap(int* pa, int* pb) void (int*, int*)
void g(void); void (void)

函数的一些事实

  • 函数名就是函数体代码的起始地址(函数入口地址)
  • 通过函数名调用函数,本质为指定具体地址的跳转执行
  • 因此,可定义指针,保存函数入口地址

函数指针( Type func (Type1 a,Type2 b))

  • 函数名即函数入口地址,类型为 Type(*)(Type1,Type2)
  • 对于 func 的函数,&func 与 func 数值相同,意义相同
  • 指向函数的指针:Type (*pFunc) (Type1, Type2) = func;

函数指针参数

  • 函数指针的本质还是指针(变量,保存内存地址)
  • 可定义函数指针参数,使用相同代码实现不同功能

注意

函数指针只是单纯的保存函数的入口地址

因此

  • 只能通过函数指针调用目标函数
  • 不能进行指针移动(指针运算)

下面看一段代码,理解一下:

#include <stdio.h>
int add(int a, int b)
{
    return a + b;
}
int mul(int a, int b)
{
    return a * b;
}
int calculate(int a[], int len, int(*cal)(int, int))
{
    int ret = a[0];
    int i = 0;
    for(i=1; i<len; i++)
    {
        ret = cal(ret, a[i]);
    }
    return ret;
}
int main()
{
    int a[] = {1, 2, 3, 4, 5};
    int (*pFunc) (int, int) = NULL;
    pFunc = add;
    printf("%d\n", pFunc(1, 2));
    printf("%d\n", (*pFunc)(3, 4));
    pFunc = &mul;
    printf("%d\n", pFunc(5, 6));
    printf("%d\n", (*pFunc)(7, 8));
    printf("1 + ... + 5 = %d\n", calculate(a, 5, add));
    printf("1 * ... * 5 = %d\n", calculate(a, 5, mul));
    return 0;
}

下面为输出结果:

这里注意,只有调用的时候,才能确定 calculate() 子函数中的 cal 是什么函数。

再论数组参数

函数的数组形参退化为指针!因此,不包含数组实参的长度信息。使用数组名调用时,传递的是0号元素的地址。

void func(int a[ ]) <--> void func(int* a)

<--> void func (int a[1])

<--> void func (int a[10)

<--> void func(int a[100)

下面看一段代码:

#include <stdio.h>
int demo(int arr[], int len)  // int demo(int* arr, int len)
{
    int ret = 0;
    int i = 0;
    printf("demo: sizeof(arr) = %d\n", sizeof(arr));
    while( i < len )
    {
        ret += *arr++;
        i++;
    }
    return ret;
}
int main()
{
    int a[] = {1, 2, 3, 4, 5};
    // int v = *a++;
    printf("return value: %d\n", demo(a, 5));
    return 0;
}

下面为输出结果:

定义的形参arr[]可以进行 *arr++ 的操作,这就说明函数的数组形参退化为指针,因为数组不可以进行 ++ 的运算。

小结

  • 函数名的本质是函数体的入口地址
  • 函数类型由返回类型和参数类型列表组成
  • 可定义指向函数的指针:Type (*pFunc) (Type1,Type2);
  • 函数指针只是单纯的保存函数的入口地址(不能进行指针运算)

六、指针与堆空间

再论内存空间

内存区域不同,用途不同

  • 全局数据区:存放全局变量,静态变量
  • 栈空间:存放函数参数,局部变量
  • 堆空间:用于动态创建变量(数组)

堆空间的本质

  • 备用的“内存仓库”,以字节为单位预留的可用内存
  • 程序可在需要时从“仓库”中申请使用内存(动态借)
  • 当不需要再使用申请的内存时,需要及时归还(动态还)

问题

  • 如何从堆空间申请内存?如何归还?

预备知识-- void*

  • void 类型是基础类型,对应的指针类型为 void*
  • void* 是指针类型,其指针变量能够保存地址
  • 通过 void* 指针无法获取内存中的数据(无长度信息)

void* 总结

  • 不可使用void*指针直接获取内存数据。
  • void*指针可与其它数据指针相互赋值。

下面看一段代码:

#include <stdio.h>
int main()
{
    char c = 0;
    int i = 0;
    float f = 2.0f;
    double d = 3.0;
    void* p = NULL;
    double* pd = NULL;
    int* pi = NULL;
    /* void* 指针可以保存任意类型的地址 */
    p = &c;
    p = &i;
    p = &f;
    p = &d;
    printf("%p\n", p);
    // void* 类型的指针无法访问内存中的数据
    // printf("%f\n", *p);
    /* void* 类型的变量可以直接合法的赋值给其他具体数据类型的指针变量 */
    pd = p;
    pi = p;
    // void* 是例外,其他指针类型的变量不能相互赋值
    // pd = pi;
    return 0;
}

下面为输出结果:

注意几个问题:

1.void* 指针可以保存任意类型的地址

2.void* 类型的指针无法访问内存中的数据

3.void* 类型的变量可以直接合法的赋值给其他具体数据类型的指针变量

4.void* 是例外,其他指针类型的变量不能相互赋值

堆空间的使用

  • 工具箱:stdlib.h
  • 申请:void* malloc ( unsigned bytes )
  • 归还:void free( void* p)

堆空间的使用原则

  • 有借有还,再借不难(杜绝只申请,不归还)
  • malloc申请内存后,应该判断是否申请成功
  • free只能释放申请到的内存,且不可多次释放(free 释放的是堆空间的地址)

下面看一段代码感受一下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int* p = malloc(4); // 从堆空间申请 4 个字节当作 int 类型的变量使用
    if( p != NULL )  // 如果申请失败 p 为 0 ,即:空值
    {
        *p = 100;
        printf("%d\n", *p);
        free(p);
    }
    p = malloc(4 * sizeof(int));
    if( p != NULL )
    {
        int i = 0;
        for(i=0; i<4; i++)
        {
            p[i] = i * 10;
        }
        for(i=0; i<4; i++)
        {
            printf("%d\n", p[i]);
        }
        free(p);
    }
    return 0;
}

下面为输出结果:

小结

  • 堆空间是程序中预留且可用的内存区域
  • void*指针只能能够保存地址,但无法获取内存数据
  • void*指针可与其它数据指针相互赋值
  • malloc申请内存后,应该判断是否申请成功
  • free只能释放申请到的内存,且不可多次释放

七、指针专题经典问题剖析

多级指针

  • 可以定义指针的指针保存其它指针变量的地址

如:

Type v;
Type *pv = &v;
Type** ppv = &pv;
type*** pppv = &ppv;

下面看一段代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a = 0;
    int b = 1;
    int* p = &a;
    int** pp = &p;
    **pp = 2;   // a = 2;
    *pp = &b;   // p = &b;
    *p = 3;     // b = 3;
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

下面为输出结果:

*pp 就是取 pp 里面的内容,而 pp 里面存的内容是 p 的地址,所以 *pp 就相当于p 的内容,而 p 的内容就是 a 的地址,所以说 **p 就相当于 a,**p = 2 也就是把 2 赋值给 a,*pp = &b 即为 p = &b,所以 *p = 3,就是把 3 赋值给 b。

下面再看一段代码:

#include <stdio.h>
#include <stdlib.h>
int getDouble(double** pp, unsigned n)
{
    int ret = 0;
    double* pd = malloc(sizeof(double) * n);
    if( pd != NULL )
    {
        printf("pd = %p\n", pd);
        *pp = pd;
        ret = 1;
    }
    return ret;
}
int main()
{
    double* p = NULL;
    if( getDouble(&p, 5) )
    {
        printf("p = %p\n", p);
        free(p);
    }
    return 0;
}

下面为输出结果:

这里特别注意:函数外的一个一级指针指向了这里申请的堆空间

再论二维数组

二维数组的本质是一维数组 ,即:数组中的元素是一维数组!!

因此:

int a[2][2];

a 就是 &a[0]

a[0] 的类型是 int[2]

可知 a 的类型是 int (*)[2]

下面看一段代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int b[2][2] = {{1, 2}, {3, 4}};
    int (*pnb) [2] = b;  // b 的类型是 int(*)[2]
    *pnb[1] = 30;
    printf("b[0][0] = %d\n", b[0][0]);
    printf("b[0][1] = %d\n", b[0][1]);
    printf("b[1][0] = %d\n", b[1][0]);
    printf("b[1][1] = %d\n", b[1][1]);
    return 0;
}

下面为输出结果:

pnb[0]是[1,2],pnb[1]经过赋值后是[30,4],所以*(pnb[1])就是取该数组所代表的第0个元素,也就是30。

下面再看一个代码:

#include <stdio.h>
#include <stdlib.h>
int* func()
{
    int var = 100;
    return &var;
}
int main()
{
    int* p = func();  // OOPS!!!!
                      // p 指向了不合法的地址,这个地址处没有变量存在
                      // p 是一个野指针,保存不合法地址的指针都是野指针
    printf("*p = %d\n", *p);
    *p = 200;   // 改变 func 函数中局部变量 var 的值,是不是非常奇怪???
    printf("*p = %d\n", *p);
    return 0;
}

这段代码是有问题的, func() 函数执行后, var 这个变量就会被销毁,所以 p 指向了一个不合法的地址。

小结

  • 可以定义指向指针的指针(保存指针变量的地址)
  • 一维数组名的类型为Type* (变量地址类型)
  • 二维数组名的类型为Type (*)[N](数组地址类型)
  • 不要从函数中返回局部变量/函数参数的地址

到此这篇关于C语言全方位讲解指针与地址和数组函数堆空间的关系的文章就介绍到这了,更多相关C语言指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言详细讲解多维数组与多维指针

    目录 一.指向指针的指针 二.二维数组与二维指针 三.数组名 四.小结 一.指向指针的指针 指针的本质是变量 指针会占用一定的内存空间 可以定义指针的指针来保存指针变量的地址值 为什么需要指向指针的指针? 指针在本质上也是变量 对于指针也同样存在传值调用与传址调用 下面看一个重置动态空间大小(从 size 到 new_size)的代码: #include <stdio.h> #include <malloc.h> int reset(char** p, int size, int

  • C语言函数的参数使用指针

    在c语言中实参和形参之间的数据传输是单向的"值传递"方式,也就是实参可以影响形参,而形参不能影响实参.指针变量作为参数也不例外,但是可以改变实参指针变量所指向的变量的值. #include <stdio.h> void swap1(int x,int y),swap2(int *px,int *py),swap3(int *px,int *py); int main(void) { int a=1,b=2; int *pa=&a,*pb=&b; swap1(

  • C语言零基础讲解指针和数组

    目录 一.指针和数组分析-上 1.数组的本质 2.指针的运算 3.指针的比较 4.小结 二.指针与数组分析-下 1.数组的访问方式 2.下标形式 VS 指针形式 3.a 和 &a 的区别 4.数组参数 5.小结 一.指针和数组分析-上 1.数组的本质 数组是一段连续的内存空间 数组的空间大小为 sizeof(array_type) * array_size 数组名可看做指向数组第一个元素的常量指针 下面看一段代码: #include <stdio.h> int main() { int

  • C语言函数指针详解

    目录 Introduction 函数指针 Function Pointers Exercise 1:qsort中的函数指针 Exercise 2: 总结 Introduction 上一个lab的主要内容为__data pointer__(指向数据的指针)可能在Linux系统中造成的__segmentation fault__.本次lab将考虑__function pointer__(指向函数/代码的指针)可能造成的错误:segfault或其他exceptions. 函数指针 Function P

  • C语言深入分析数组指针和指针数组的应用

    目录 一.数组类型 二.定义数据类型 三.数组指针 四.指针数组 五.小结 一.数组类型 C语言中的数组有自己特定的类型 数组的类型由元素类型和数组大小共同决定 例:int array[5] 的类型为 int[5] 二.定义数据类型 C语言中通过 typedef 为数组类型重命名:typedef type(name)[size]; 数组类型: typedef int(AINT5)[5]; typedef float(AFLOAT10)[10]; 数组定义: AINT5 iArray; AFLOA

  • C语言全方位讲解指针与地址和数组函数堆空间的关系

    目录 一.一种特殊的变量-指针 二.深入理解指针与地址 三.指针与数组(上) 四.指针与数组(下) 五.指针与函数 六.指针与堆空间 七.指针专题经典问题剖析 一.一种特殊的变量-指针 指针是C语言中的变量 因为是变量,所以用于保存具体值 特殊之处,指针保存的值是内存中的地址 内存地址是什么? 内存是计算机中的存储部件,每个存储单元有固定唯一的编号 内存中存储单元的编号即内存地址 需要弄清楚的事实 程序中的一切元素都存在于内存中,因此,可通过内存地址访问程序元素. 内存示例 获取地址 C语言中通

  • C语言全方位讲解指针的使用

    目录 一.指针的概念 1.1.变量和地址 1.2.指针变量和指针的类型 二.指针变量 2.1.指针变量的定义及使用 2.2.指针运算 三.野指针 3.1.概念: 3.2.野指针的成因 3.3.如何规避野指针 四.字符指针 4.1.字符指针类型 4.2.例题 五.指针与数组 5.1.指针与二维数组 5.2.多级指针 六.指针数组 七.数组指针 7.1.数组指针的定义 7.2.&数组名与数组名 7.3.数组指针的使用 八.指针与函数 8.1.函数指针的定义 8.2.指向函数的指针 总结 接着上次的函

  • C语言全方位讲解数组的使用

    目录 一维数组的创建和初始化 1.数组的创建 2.数组创建方式 3.数组的初始化 一维数组的使用 一维数组的存储 二维数组的创建与初始化 1.二维数组的创建 2.二维数组的初始化 二维数组的存储 数组的越界 总结 接着上次的操作符的详解,让我们来简单了解C语言里的数组. 一维数组的创建和初始化 1.数组的创建 数组是一组相同类型的元素的集合. 2.数组创建方式 type_t(数组类型) arr_name(数组名) [const_n](用来指定数组大小) 3.数组的初始化 数组的初始化是在其定义的

  • C语言 深入浅出讲解指针的使用

    目录 一.利用指针倒序字符串 二.题目实例 三.总结 一.利用指针倒序字符串 void _reversal(char* left, char* right) { while (left < right) { char tmp = *left; *left = *right; *right = tmp; left++; right--; } } 通过上述代码不难看出,left与right分别代表一个字符数组的首端和尾端,通过中间变量 tmp进行首尾交换,left++中的left是char*类型,同

  • C语言详细讲解指针数组的用法

    目录 1. 指针数组定义方法 2. 指针的指针(二级指针) 3. 字符串和指针 4. 数组指针 定义方法 数组指针的用法 1. 指针数组定义方法 格式: 类型说明符 *数组名[ 元素个数 ] int *p[10]; // 定义了一个整型指针数组p,有10个元素,都是int *类型的变量 指针数组的分类: 同指针类型的分类,见上一篇 大多数情况下,指针数组都用来保存多个字符串. #include <stdio.h> int main() { char *name[5] = {"Hell

  • C语言深入讲解指针与结构体的使用

    目录 1 啥是指针 1.1指针与指针变量 1.2总结 2 指针和指针类型 2.1指针+-整数 3 野指针 3.1 野指针的成因 1指针未初始化 2指针越界访问 3指针指向的空间释放 3.2 如何避免野指针的出现 4 二级指针 5 指针数组 6 结构体 6.1 结构的声明 6.2 结构体变量的定义和初始化 6.3 结构体的访问 6.4 结构体传参 1 啥是指针 刚刚接触指针的同学肯定会很懵逼,指针是啥啊?指南针哈哈,不和大家开玩笑,我们进行正题吧,指针是本质是就是地址,但我们要注意我们口头上常说的

  • C语言例题讲解指针与数组

    目录 1.概要复习 2.指针与数组笔试题 2.1一维数组 2.2字符数组 2.3字符串数组 2.4字符串指针 2.5二维数组 1.概要复习 本篇的内容主要围绕指针与数组.指针与字符串等之间的关系,以及进一步理解sizeof .strlen 的使用与意义. 数组是指具有相同类型元素的集合,字符串常量是一个指向在连续空间里存放的字符的首字符的地址的指针.我们会在下面理解数组与字符串数组的不同. sizeof 是一个操作符,是计算类型空间大小的.strlen 是针对字符串的库函数,用来求字符串的长度.

  • C语言 详细讲解数组参数与指针参数

    目录 一.C语言中的数组参数退化为指针的意义 二.二维数组参数 三.等价关系 四.被忽视的知识点 五.小结 一.C语言中的数组参数退化为指针的意义 C 语言中只会以值拷贝的方式传递参数 当向函数传递数组时: 将整个数组拷贝一份传入函数        × 将数组名看做常量指针传数组首元素地址    √ C 语言以高效作为最初设计目标: a) 参数传递的时候如果拷贝整个数组执行效率将大大下降. b) 参数位于栈上,太大的数组拷贝将导致栈溢出. 二.二维数组参数 二维数组参数同样存在退化的问题 二维数

随机推荐