弄清楚有关数组和指针相关的概念
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;
}