C语言二维数组与指针

弄清楚有关数组和指针相关的概念

C99 可变长数组VLA(variable length array)

可以使用int arr[n]; 的形式来做定义

在编译期可变长, 一旦定义就不能改变, 不是运行期可变 (动态数组 )

可变改类型VM(variably modified type), VM包含了VLA和指向VLA的指针

一个VM实体的声明或定义必须符合以下三个条件:

  • 代表该对象的标识符属于普通标识符(ordinary identifier);

  • 具有代码块作用域或函数原型作用域;

  • 无链接性。

一个Ordinary identifier指的是除下列三种情况之外的标识符:

  • 标签

  • 结构、联合和枚举标记

  • 结构、联合成员标识符

不能具有文件作用域,不能位于结构体中

不能具有连接性(extern) VLA不能具有静态存储周期(static), 可以使用静态的VM指针

在函数原型声明中,VLA形参可使用*标记, 表明此处声明的是一个VLA对象(有的编译器并不支持,暂时不要用)

定义在函数内的局部可变长数组,通过栈指针的移动很方便地分配内存,并且相较于malloc更高效,不会造成内存泄露或者产生内存碎片

int n=2, m=3;
// VAL
int arr[n*m] = {1, 1, 2, 2, 3, 4};
f(2, 3, arr);

int **a; 二重指针

在函数传参时使用最方便,但是定义时需要使用malloc创建并且为a[i] 做定义

malloc 在堆中创建内存映像, 作为数组的替代

int** a = (int **)malloc(n*sizeof(int *));
    // 一次性分配全部内存,之后定义数组的每一个首地址。最后只需一次释放
    a[0] = (int *)malloc(m*n*sizeof(int));
    for (int i=1; i<n; i++)
        a[i] = a[0] + m*i;
    int* a[n] = {};
    // 分配不连续的内存,最后先分配后释放
    for (int i=0; i<n; i++){
        a[i] = (int *)malloc(sizeof(int)*m);
        if (a[i]==NULL)
            perror("malloc");
    } 

int (*p)[3]; 二维数组指针, 指向二维数组

需要指明第二维大小,在函数传参时也需要,否则编译器无法计算得到正确地址

// malloc 数组映像
int (*p3)[3] = (int(*)[3])malloc(2*3*sizeof(int));

int a[2][3]; 二维数组

int arr2[2][3] = {{1, 1, 2}, {2, 3, 4}};
cout << " length of arr2[0]: "<< sizeof(arr2[0])/sizeof(arr2[0][0])
     << ", size of arr: " << sizeof(arr2)/sizeof(arr2[0]) << endl;

其他

当定义一个数组时,编译器根据指定的元素个数和元素的类型分配固定大小(元素类型大小×元素个数)的块内存,并命名为a,指向该内存,通过[ ]运算符从0开始来访问数组元素。数组元素从小地址,顺序排列。

二维数组在内存中同样线性顺序排列,a[i][j]的地址为*(a+i)+j,或者(a[i]+j),可以抽象为二维块状排列,在C语言中按行排列。使用sizeof(a)/sizeof(a[0],sizeof(a[0])/sizeof(a[0][0]) 求解行、列数(多此一举)。

使用malloc(calloc realloc)分配的内存在堆中,通过free手动释放内存,

内存清理函数:void bzero(void *s, size_t n); // 把从s地址开始的n字节的一块内存清理为0

void *memset(void *s, int c, size_t n); // 把从地址s开始的n字节的一块内存设置为c(ASCII码值),返回设置成功后内存的首地址

注意realloc函数不要用原指针接收新内存地址,如果分配失败返回NULL无法找回原空间,在C99中旧空间不会被释放

#include <iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;

int f(int n, int m, int *arr){
    printf("\nIn f: int *arr: \n");
    for (int i=0; i<n; i++){
        for (int j=0; j<m; j++){
            printf("%ld: %d ", (arr+i*m+j), *(arr+i*m+j));
        }
        printf("\n");
    }
    printf("\n");
    return 0;
}

int f2(int m, int n, int **arr){
    cout << "In f2: int **a: " << endl;
    for (int i=0; i<m; i++){
        for (int j=0; j<n; j++)
            cout <<  arr[i][j] << " ";
        cout << endl;
    }
    cout << endl;
    return 1;
}

int f3(int n){
    cout << "In f3" << endl;
    int a[n];
    for (int i=0; i<n; i++){
        a[i] = i;
    }
    for (int i=0; i<n; i++)
        cout << a[i] << " ";
    return 0;
}

int main(){
    int n=2, m=3;
    
    // VAL
    int arr[n*m] = {1, 1, 2, 2, 3, 4};
    f(2, 3, arr);

    // 二维数组
    int arr2[2][3] = {{1, 1, 2}, {2, 3, 4}};
    cout << " length of arr2[0]: "<< sizeof(arr2[0])/sizeof(arr2[0][0])
         << ", size of arr: " << sizeof(arr2)/sizeof(arr2[0]) << endl;

    // 二维数组指针, 数组名代表数组第一个元素的地址, arr2[1]代表 {2, 3, 4}的首地址
    int (*p2)[3] = arr2;
    printf("通过二维数组指针,访问二维数组:\np2[1][1]: %d  *(*(p2+1)+1): %d  *(p2[1]+1): %d\n", p2[1][1], *(*(p2+1)+1), *(p2[1]+1));

    // malloc 数组映像
    int (*p3)[3] = (int(*)[3])malloc(2*3*sizeof(int));

    // 定义a为指向指针(一个一维数组(也就是一维数组首地址组成的数组))的指针
    int** a = (int **)malloc(n*sizeof(int *));
    // 一次性分配全部内存,之后定义数组的每一个首地址。最后只需一次释放
    a[0] = (int *)malloc(m*n*sizeof(int));
    for (int i=1; i<n; i++)
        a[i] = a[0] + m*i;
    a[0][1] = 1;
    a[1][0] = 10;
    long long int addr = (long long)a[5];
    cout << "ADDR: " << addr << endl;
    // 已经越界了,这块数据很可能被其他变量占用(?)
    a[5] = a[1] + 4*m;
    addr = (long long)a[5];
    cout << "new ADDR: " << addr << endl;
    a[5][5] = 55;
    f2(2, 3, a);
    free(a);


    // int* a[n] = {};
    // // 分配不连续的内存
    // for (int i=0; i<n; i++){
    //     a[i] = (int *)malloc(sizeof(int)*m);
    //     if (a[i]==NULL)
    //         perror("malloc");
    // } 
    // a[0][1] = 1;
    // a[1][0] = 10;
    // f2(2, 3, a);
    // for (int i=0; i<n; i++)
    //     free(a[i]);
    // free(a);

    f3(5);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值