文章目录
- 第4天
- 二维数组的步长问题
- int** m的赋值过程,分两步
- int**m 的释放过程,分两步
- char** buf 示意图
- 函数返回分配好的char**变量,函数free char**变量
- 首元素地址和首行地址
- 三种二级指针内存模型图
- 指针指向谁,就把谁的地址赋给指针
- typedef 定义数组类型
- *和[]
第4天
二维数组的步长问题
char *p1[] = {"11", "2222", "3333"}; // 指针数组,字符常量区分配字符串,返回地址
sizeof(p1)/sizeof(p1[0]) = 4*3/4 = 3
char *p2[10] = {"1111", "2222", "3333"}
sizeof(p2)/sizeof(p2[0]) = 40/4
char p3[][30] = {"1111", "2222", "3333"}
sizeof(p3)/sizeof(p3[0]) = 1*3*30/1*30 = 3 // sizeof(p3[0]) 第0行所占空间大小
char p[][30] = {"11", "2222", "3333"}; // 每行30个*字符*,一共3行
printf("%c %c %c %c %c %c", p[0][0], p[0][1], p[0][2], p[1][0], p[1][1], p[3][0]); //1 1 2 2 0
char* p[][30] = {"111", "22", "3"}; // 每行容纳30个*指针*,一共1行(计算得出)
printf("%s %s %s %s %s", p[0][0], p[0][1], p[0][2], p[0][3], p[1][0]);// 111 22 3 (null) ? @
char p[][30] = {'1', '2', '3'}; // 每行30个字符,一共1行
printf("%c %c %c %c %c", p[0][0], p[0][1], p[0][2], p[0][3], p[1][0]);// 1 2 3
char p4[10][30] = {"1111", "2222", "3333"}
sizeof(p4)/sizeof(p4[0]) = 1*10*30/1*30 = 10
char p4[10][30] = {"1111", "2222", "3333"};
printf("%c %c %c %c %c %c", p4[0][0], p4[0][1], p4[0][2], p4[0][3], p4[0][4], p4[0][5]); // 1 1 1 1
printf("%c %c %c %c %c %c", p4[1][0], p4[1][1], p4[1][2], p4[1][3], p4[1][4], p4[1][5]); // 2 2 2 2
void fun(char **buf), void fun(char buf[][30]); // 二者步长不一样,前为4后为30
int** m的赋值过程,分两步
int main()
{
int** m;
m = (int**)malloc(N * sizeof(int*)); // 第一步:分配指针数组
for(int i = 0; i < N; i++)
m[i] = (int*)malloc(N * sizeof(int)); // 第二步:分配每个指针所指向的数组
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
scanf("%d", &m[i][j]);
}
}
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
printf("%d ", m[i][j]);
}
printf("\n");
}
}
int**m 的释放过程,分两步
char **buf = NULL;
// ...
void free(char **buf, int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
free(buf[i]);
buf[i] = NULL;
}
if(buf != NULL)
{
free(buf);
buf = NULL;
}
}
free(buf, n); // 值传递
buf = NULL;
void free(char ***tmp, int n) // ***tmp 接收二维指针的地址
{
char **buf = *tmp; // 局部变量 **buf来获得二维指针内容(*tmp)
int i = 0;
for(i = 0; i < n;i++)
{
free(buf[i]);
buf[i] = NULL;
}
if(buf != NULL)
{
free(buf);
buf = NULL;
}
*tmp = NULL;
}
free(&buf, n); // 地址传递
if(buf != NULL)
{
free(buf);
buf = NULL;
}
char** buf 示意图
// 动态分配一个数组,每个元素都是char *
char **buf = (char **)malloc(n * sizeof(char *)); // char *buf[3]
for(i = 0; i < n; i++)
buf[i] = (char *)malloc(30 * sizeof(char));
for(i = 0; i < n; i++)
{
free(buf[i]);
}
if(buf != NULL)
{
free(buf);
}
函数返回分配好的char变量,函数free char变量
char** getMem(int n)
{
int i = 0;
char** buf = (char**)malloc(n * sizeof(char*)); // char *buf[3]
for(i = 0; i < n; i++)
buf[i] = (char*)malloc(30 * sizeof(char));
return buf;
}
void free_buf(char **buf, int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
free(buf[i]);
}
if(buf != NULL)
{
free(buf);
}
}
int main()
{
char **buf = NULL;
int n = 3;
buf = getMem(n);
if(buf == NULL)
{
printf("getMem err\n");
return -1;
}
print_buf(buf, n);
free_buf(buf, n);
buf = NULL;
}
调用函数时,如果形参比实参多一个 *,则说明是地址传递。
首元素地址和首行地址
二维数组的数组名代表首行地址(第一行一维数组的地址),char a[10][30]
首行地址+1 跳过30字节;
首行地址和首行元素地址的值是一样的,但是它们步长不一样;
char b[30];
&b 代表整个一维数组的地址,相当于二维数组首行地址;
b 代表一维数组首元素地址,相当于二维数组首行首元素地址;
&b 和 b 的值虽然一样,但是,它们的步长不一样
&b + 1: 跳过整个数组,+30;
b+1: 跳过1个字符
sizeof(a):有4个一维数组,每个数组长度为30,4*30=120
sizeof(a[0]):第0个一维数组首元素地址,相当于测第0个一维数组的长度,为30
三种二级指针内存模型图
指针指向谁,就把谁的地址赋给指针
a[10][30] // 二维数组 10 行 30 列,10 个一维数组a[30],总共能容量 10 行字符串
// 二级指针释放 char **buf buf[][n]
free(&buf, n);
if(buf != NULL)
{
free(buf);
buf = NULL;
}
void free(char ***tmp, int n)
{
char **buf = *tmp;
for(int i = 0; i < n; i++)
{
free(buf[i]);
buf[i] = NULL;
}
if(buf != NULL)
{
free(buf);
buf = NULL;
}
*tmp = NULL;
}
typedef 定义数组类型
// 数组类型:由元素个数和元素类型决定
// 通过 typedef 定义一个数组类型
// 有 typedef 是类型,没有是变量
typedef int A[8]; // 代表数组类型,不是变量
A b; // int b[8];
// 数组指针,指向一个数组的指针
// 数组指针,指向一维数组的整个数组,而不是首元素地址
// 1. 先定义数组类型,根据类型定义指针变量
int main()
{
int a[10] = {0};
typedef int A[10];
A *p = NULL; // p 数组指针类型变量 根据数组类型,定义一个指针变量
p = &a;
printf("p:%d, p+1:%d\n", p, p+1); // p:6356692, p+1:6356732
for(int i = 0; i < 10; i++)
{
(*p)[i] = i + 1;
}
for(int i = 0; i < 10; i++)
{
printf("%d ", (*p)[i]);
}
// p:6356684, p+1:6356724
// 1 2 3 4 5 6 7 8 9 10
}
// 2. 先定义数组指针类型,根据类型定义变量
// 和指针数组写法类似,多了()
// () 和 [] 优先级一样,从左往右
int main()
{
int a[10] = {0};
int i = 0;
typedef int(*P)[10]; // int(*P)[10] 数组指针变量,非类型
P q;
q = &a;
for(int i = 0; i < 10; i++)
{
(*q)[i] = i + 1;
}
for(int i = 0; i < 10; i++)
{
printf("%d ", (*q)[i]);
}
// 1 2 3 4 5 6 7 8 9 10
}
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
printf("a:%d, a+1:%d\n", a, a+1);
printf("%d, %d\n", *(a + 0), *(a + 0) + 1);
printf("%d, %d\n", a[0], a[0]+1);
a:6356688, a+1:6356704 // +16,跳过一行
6356688, 6356692 // +4,该行下一个元素的地址
6356688, 6356692
*和[]
// a -> 第0行首地址 二维数组数组名
// (a+i) -> &a[i] 第i行首地址
// *(a+i) -> a[i] 第i行首元素地址 要想把首地址转化为首元素地址,加*
// *(a+i)+j -> &a[i][j] 第i行第j列元素的地址 要想得到某个元素地址,加偏移量
// *((a+i)+j) -> a[i][j] 第i行第j列元素的值