05--数组(2)

本文详细解释了C语言中数组的不同含义、数组名作为指针的情况、数组下标与指针运算的关系,以及字符串常量、零长数组、变长数组和指针数组的概念及其操作。特别关注了数组大小的固定性和动态内存分配在数组中的应用。
摘要由CSDN通过智能技术生成
  • 数组名有两个含义:
    • 第一含义是:整个数组
    • 第二含义是:首元素地址
  • 当出现以下情形时,那么数组名就代表整个数组:
    • 在数组定义中 int arr [3] ; arr 表示整个数组
    • 在 sizeof 运算表达式中 sizeof(arr) ; 得到的结果是整个数组的占用内存 type * 元素个数
    • 在取址符&中 p = & arr ; 取得数组 arr 的地址, 因此 表示整个数组
  • 其他任何情形下,那么数组名就代表首元素地址。即:此时数组名就是一个指向首元素的指针。
    int arr [5] = {3,4,5,1,2};
    int * p = arr ;  // 当前的arr不属于以上三种情况, 因此它代表首元素的首地址  数据  3 的地址
    int (*p1) [5] = &arr ; // p1 是一个指针  它指向(存放的地址)的数据类型,是 int [5]的类型
                    // &arr 表示整个数组的地址 
                    
    int arr[5] = {1,3,5,7,9} ;
    int * p = arr ;  // arr 是数组首元素的首地址, 因此指针 p 指向了数据1 的地址
    int * p1 = arr+1 ; // arr 是数组首元素的首地址 +1 则 + 一个元素的类型
        // 因此 arr+1 则+了一个整形大小,也就是 数据 3 的地址
        
    printf("p:%d p1:%d\n" , *p , *p1 );
    int * p3 = &arr + 1 ;  // &arr 表示整个数组 arr 的地址
        // 整个数组的地址  + 1  则 + 一个数组的大小

    示例:

    int a[3];                  // 此处,a 代表整个数组
    printf("%d\n", sizeof(a)); // 此处,a 代表整个数组
    printf("%p\n", &a);        // 此处,a 代表整个数组,此处为整个数组的地址
    
    int *p = a;       // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
    p = a + 1;        // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
    function(a);      // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
    scanf("%d\n", a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]

    C语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。

 

 

 数组下标:

  • 数组下标实际上是编译系统的一种简写,其等价形式是:
    a[i] = 100;  等价于  *(a+i) = 100;

    根据加法交换律,以下的所有的语句均是等价的:

  •   a[i] = 100;
    *(a+i) = 100;
    *(i+a) = 100;
      i[a] = 100; // 3[arr] = 123 ;
                  // arr[3] = 123 ;
    
    // 以下四个语句都能正确访问数据
    printf("arr[2]:%d\n" , arr[2]) ;
    printf("*(arr+2):%d\n" , *(arr+2)) ;
    printf("*(2+arr):%d\n" , *(2+arr)) ;
    printf("2[arr]:%d\n" , 2[arr]) ;

  • 数组运算,等价于指针运算。

字符串常量

  • 字符串常量在内存中的存储(存储在常量区-该区域不允许被修改(只读ReadOnly)),实质是一个匿名数组
  • 匿名数组,同样满足数组两种涵义的规定
  • 示例:
    printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
    printf("%p\n", &"abcd");        // 此处 "abcd" 代表整个数组
    
    printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址
    char *p1 = "abcd";         // 此处 "abcd" 代表匿名数组的首元素地址
    char *p2 = "abcd" + 1;     // 此处 "abcd" 代表匿名数组的首元素地址
    

    字符串常量是以字符串数组的形式存储在数据段(常量区)。

零长数组

  • 概念:长度为0的数组,比如 int data[0];
  • 用途:放在结构体的末尾,作为可变长度数据的入口
  • 示例:
    struct node
    {
        /* 结构体的其他成员 */
        // 成员1
        // 成员2
        // ... ...
        
        int   len;
        char *data[0];
    };
    
    // 给结构体额外分配 10 个字节的内存。
    struct node *p = malloc(sizeof(struct node) + 10);
    p->len = 10;
    
    // 额外分配的内存可以通过 data 来使用
    p->data[0] ~ p->data[9]

    变长数组

  • 概念:定义时,使用变量作为元素个数的数组(使用变量作为数组定义时的大小)。
  • 要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。
  • 示例:
    int arr[60];
    
    int len = 5; // 可能通过某些运算,或用户的选择得到了数组的大小
    int a[len];  // 数组元素个数 len 是变量,因此数组 a 是变长数组
    
    len = 30 ; // 再次修改变量的值并不会影响数组的大小
    
    int x = 2;
    int y = 3;
    int b[x][y]; // 数组元素个数 x、y 是变量,因此数组 b 是变长数组
    int b[2][y]; // 数组元素个数 y 是变量,因此数组 b 是变长数组
    int b[x][3]; // 数组元素个数 x 是变量,因此数组 b 是变长数组

  • 语法:变长数组不可初始化,即以下代码是错误的:
    int len = 5;
    
    
    int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化 , 应在再定义数组之前该数组的大小都是未知的,因此无法进行初始化
    // 编译器报错:
    variable-sized object may not be initialized

    总结:

    数组在定义之处就已经确定了大小,在未定义的时候可以通过计算得到恰当的大小,当数组被定义出来后他的大小就无法再被改变。

指针数组:

概念:一个用来存放指针的数组。

语法:int * arr[5] ; int * (arr[5]) ;

如何初始化以及赋值:

int a = 123 ;
int b = 456 ;
int c = 789 ;

// 定义并初始化
int * arr[5] = { &a ,&b , &c } ;

int d = 111 ;
int k = 555 ;
// 给数组赋值
arr[3] = &b ;
arr[4] = &k ;

如何通过指针数组来访问各个指针的内容:

int a = 123 ;
int b = 456 ;
int c = 789 ;

// 定义并初始化
int * arr[5] = { &a ,&b , &c } ;

int d = 111 ;
int k = 555 ;
arr[3] = &b ;
arr[4] = &k ;

printf( "*(arr[0]):%d\n" , *(arr[0]) ); // arr[0] --> *(arr+0)
printf( "*(arr+1):%d\n" , **(arr+1) ); // arr+1 --> **(&b 的地址)  -- >  *(&b) --> b
printf( "*(2[arr]):%d\n" , *(2[arr]) );
printf( "a--> **arr:%d\n" , **arr );

二维数组的加减法问题

int arr[3][5] =  {  {1,2,3,4,5} , {11,22,33,44,55} , {111,222,333,444,555} };
printf("&arr:%p\n" , &arr);

// arr 是一个数组的名字 arr[3] --> 因此该数组名arr 表示的是首元素的地址
// 首元素  {1,2,3,4,5}    (int  [5])
// arr + 1 加的是一个元素的类型的大小  
int * p = arr + 1 ; // 因此p 应该距离入口地址 5个元素*int大小 = 20 个字节
printf("p:%p\n" , p );

// & arr 表示整个数组的地址,因此他的类型 int [3][5]
int * p2 = &arr + 1 ; // 因此 p2 距离数组的入口地址相差 60 字节
printf("p2:%p\n" , p2 );

// arr 表示首元素的地址  {1,2,3,4,5}
// *arr 的到 {1,2,3,4,5} 的首元素的首地址
// {1,2,3,4,5} 的首元素 就是 数据1 ,因此他的地址就 int 类型的
// *arr + 1 则加一个 int 类型的大小 4字节
int *p3 = *arr + 1 ; // p3距离 数组的入口地址相差 4 字节
printf("p3:%p\n" , p3 );

假设有int arr [100][450]数组, 请写出访问该数组中第 1430 个元素的语句:

arr[ m ] [ n ] :

m = 1430 / 450

n = 1430 % 450 - 1

arr[3][4] - 写出 第 8 个元素:

arr [ 1 ] [ 3 ]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值