测试题:
0. 你猜下边代码会打印多少?
#include <stdio.h>
int main()
{
void a;
printf("%d\n", sizeof(a));
return 0;
}
答:4(错误)
答案:会报错!那 void 既然是无类型,我们就不应该用它来定义一个变量,如果你一定要这么做,那么程序就会给你报错(我只是换个提问方式,看看多少童鞋会上当)。
不信你看:
1. 那 sizeof(void *) 呢?
答:4
答案:如果你回答是 4 个字节或 8 个字节,那么本题不能算你答对。因为指针的尺寸是与编译器的目标平台相关的。比如目标平台是 32 位的,那么 sizeof(void*) 就是 4,如果是 64 位的,那么 sizeof(void *) 就是 8,如果是 16 位的,那么就是 2 啦。
2. 如何有效地避免出现悬空指针?
注:悬空指针就是指向了不确定的内存区域的指针,通常对这种指针进行操作会使程序发生不可预知的错误。
答:指针在定义的时候,赋值地址或者NULL
答案:当你的指针不知道指向哪儿的时候,那么将它指向 NULL,以后就不会有太多的麻烦。比如定义一个指针变量的时候,你可以把它初始化为 NULL,这样至少可以确保它不是一个垂悬指针。
3. 对 NULL 指针进行解引用,结果是什么?
答:‘\0’(错误)
答案:报错。无论什么操作系统,对空指针进行解引用都是非法的。
4. 请问下边定义有没有问题?
……
int *p = void *0;
……
答:有问题,int *类型的赋值的得是int *的
答案:报错。
NULL 的宏定义是
#define NULL ((void *)0)
这里是将 0 强制转换成 void 指针,所以要这么写才能通过编译:
int *p = (void *)0;
5. 请问下边代码会打印什么?
#include <stdio.h>
int main()
{
int array[5] = {1, 2, 3, 4, 5};
int *pi = &array[2];
void *pv;
pv = pi;
pv++;
pi = pv;
printf("%d\n", *pi);
return 0;
}
答:4(错误)
答案:建议回答“4”的童鞋自己打一次代码……,
先晒下结果:
为何如此邪乎?
我们把代码修改如下,你或许就会知道答案:
#include <stdio.h>
int main()
{
int array[5] = {1, 2, 3, 4, 5};
int *pi = &array[2];
void *pv;
pv = pi;
printf("%p, %p\n", pv, pi);
pv++;
pi = pv;
printf("%p, %p\n", pv, pi);
printf("%d\n", *pi);
return 0;
}
程序实现如下:
猜到了吧?
由于 pv 是 void 类型指针,所以编译器并不知道其“跨度”是多少,因此 pv++ 只是简单的将地址加 1。
那么地址不正确,打印出来的值肯定就是错误的!
那打印出来的是“乱码”吗?
不是!如果没有猜错,你在电脑上得到的结果应该跟小甲鱼是一样的。
下边解释需要你懂得小端和大端的原理
pi 指向的是数组的第三个元素(即 array[2],其值为 3)
那么 array[2] 和 array[3] 在内存中的存放形式(小端)应该如下:
pv = pi 使得 pv 也是指向 0xbf93b10c 地址。
pv++ 刚才我们解释了,编译器只知道机械将其地址加 1,即 pv 现在指向的是 0xbf93b10d 地址。
pi = pv 使得 pi 被 pv 带上了歪路……
以整型的跨度打印 pi,那么编译器会从 pi 指向的地址开始(0xbf93b10d),找到四个字节的数据(0x00000004),并将它们打印出来。
由于是小端,所以 0x00000004 对应的人类可读十六进制就是 0x04000000,转换为十进制就是大家所看到的 67108864。
动动手:
0. 这道题是第 20 讲课后作业 动动手第 1 题的升级版,这一次我们允许用于自定义矩阵的尺寸和数据。
要求一:允许用户自定义输入两个矩阵的尺寸和数据
要求二:输出格式如下
要求三:由于我们还没有教动态内存分配,所以不允许使用 malloc 函数。但你可以使用 VLA 变长数组
答:忘记矩阵知识了(错误)
答案:
#include <stdio.h>
int main()
{
int m, p, n;
int i, j, k, row;
// 定义第一个矩阵
printf("请输入第一个矩阵的尺寸(M * P):");
scanf("%d * %d", &m, &p);
int matrix_in_1[m][p];
// 定义第二个矩阵
printf("请输入第一个矩阵的尺寸(P * N):");
scanf("%d * %d", &p, &n);
int matrix_in_2[p][n];
// 初始化存放乘积的二维数组
// VAL数组不支持直接初始化操作
int matrix_out[m][n];
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
matrix_out[i][j] = 0;
}
}
// 让用户输入第一个矩阵
printf("请输入第一个矩阵的值:\n");
for (i = 0; i < m; i++)
{
for (j = 0; j < p; j++)
{
scanf("%d", &matrix_in_1[i][j]);
}
}
// 让用户输入第二个矩阵
printf("请输入第二个矩阵的值:\n");
for (i = 0; i < p; i++)
{
for (j = 0; j < n; j++)
{
scanf("%d", &matrix_in_2[i][j]);
}
}
// 计算乘积并保存
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
for (k = 0; k < p; k++)
{
matrix_out[i][j] += matrix_in_1[i][k] * matrix_in_2[k][j];
}
}
}
printf("计算结果如下:\n");
// row 取行数最大值
row = m > p ? m : p;
for (i = 0; i < row; i++)
{
printf("| ");
// 打印 matrix_in_1
for (j = 0; j < p; j++)
{
if (i < m)
{
printf("\b%d ", matrix_in_1[i][j]);
printf("|");
}
else
{
printf("\b\b\b ");
}
}
// 打印 * 号
if (i == row / 2)
{
printf(" * ");
}
else
{
printf(" ");
}
printf("| ");
// 打印 matrix_in_2
for (j = 0; j < n; j++)
{
if (i < p)
{
printf("\b%d ", matrix_in_2[i][j]);
printf("|");
}
else
{
printf("\b\b\b ");
}
}
// 打印 = 号
if (i == row / 2)
{
printf(" = ");
}
else
{
printf(" ");
}
// 打印 matrix_out
printf("| ");
for (j = 0; j < n; j++)
{
if (i < m)
{
printf("\b%d ", matrix_out[i][j]);
printf("|");
}
else
{
printf("\b\b\b ");
}
}
printf("\n");
}
return 0;
}