测试题:Powered by
0. 请问 str[3] 和 *(str + 3) 是否完全等价?
答:等价
答案:完全等价。
解析:在 C 语言中,数组名是被作为指针来处理的。更确切的说,数组名就是指向数组第一个元素的指针,而数组索引就是距离第一个元素的偏移量。这也解释了为什么在 C 语言中数组下标是从 0 开始计数的,因为这样它的索引可以对应到偏移量上。因此,str[3] 和 3[str] 是相同的,因为它们在编译器的内部都会被解释为 *(str + 3)。
1. 请问下边代码是否可以正常执行?如果可以,会打印什么值?如果不行,请说明原因?
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *b;
b = &a[3];
printf("%d\n", b[-2]);
return 0;
}
答:不能(错误),答案为2
答案:可以正常执行。
解析:数组的下标法跟指针法访问是等价的,所以 b[-2] 相当于 *(b - 2),这样也就不难理解啦^_^T
2. 为什么不能使用 if (str1 == str2) 这样的形式来比较两个字符串?
答:这是两个地址,不能做比较
答案:因为这样比较的是指向两个字符串的指针,而不是字符串本身。
3. 通常我们交换两个变量的值需要使用到一个临时变量,代码如下:
……
temp = a;
a = b;
b = temp;
……
小明童鞋说其实大可不必使用临时变量,他这么写:
……
a += b;
b = a - b;
a -= b;
……
请问小明的办法可行吗?
答:可以,但我觉得只有大聪明会这么写
答案:在大部分情况下,小明的方案是奏效的。不过有一种情况需要担心,就是在颠倒同一个变量时,这个代码是无法正常运行的。
……
#define SWAP(a, b) (a += b, b = a - b, a -= b)
……
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i, j;
……
SWAP(array[i], array[j]); // 当 i == j 时,触发 Bug
……
4. 如果数组 array 的定义如下:
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
请问 array 和 &array 有区别吗?
答:有区别,array为数组的地址,&array为array的地址(分析错误,详情见答案)
答案:有。
解析:虽然 array 和 &array 的值相同,但含义是不一样的。array 表示数组第一个元素的位置,而 &array 表示的是整个数组的位置(这里将数组看做一个整体)。
5. 如果不上机,你能看出下边代码将打印什么值吗?
#include <stdio.h>
int main()
{
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = (int *)(&array + 1);
printf("%d\n", *(p - 6));
return 0;
}
答:&array的地址不清楚指向,猜想 *(p-6)= 3(错误)
答案:4
解析:首先你要明白虽然 array 和 &array 的值相同,但含义是不一样的。array 表示数组第一个元素的位置,而 &array 表示的是整个数组的位置(这里将数组看做一个整体)。
因此,&array + 1 指向的就是整个数组最后的位置(第二个 array 数组的起始位置),然后 (int *) 将其强制转换为一个整型地址(指针),所以指针变量 p 初始化后,指向的地址应该是 array[10](第 11 个元素),所以 *(p - 6) == array[10 - 6] == array[4] == 4。
重点强调:array 是数组第一个元素的地址,所以 array + 1 指向数组第二个元素;&array 是整个数组的地址,所以 &array + 1 指向整个数组最后的位置。
6. 如果不上机,你能看出下边代码将打印什么值吗?
#include <stdio.h>
int main()
{
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*p)[10] = &array;
printf("%d\n", *(*(p+1)-6));
return 0;
}
答:4
答案:4
解析:基本上一道题成功理解的话,这道题就不会有什么问题。指针 p 指向数组 array 的地址,p + 1 便是指向整个数组最后的位置(第二个 array 数组的起始位置),于是 *(p+1) 即下一个数组 array 数组的起始位置,即 &array[11],所以 *(*(p+1)-6) == array[11 - 6] = array[5]。
PS:如果我把打印的代码写成这样 printf("%d\n", (*(p + 1))[-6]); 现在你应该也可以理解了吧!
7. 你如何理解“指针的类型决定了指针的视野,指针的视野决定了指针的步长”这句话?A
答:我不太清楚(错误)
答案:你必须清楚一个指针变量将告诉我们两个信息:某个数据结构的起始地址,以及该结构的跨度。比如 int p = &a; 说明该指针指向变量 a 的起始地址,以及它的跨度为 sizeof(int)。所以 p + 1 == p + sizeof(int)。
怕大家不够清晰,我接着给大家讲上一题(第 4 题)……同样的道理,int (*p)[10] 虽然是定义一个整型指针,但不要忘了它后边还有一个数组,所以它的跨度应该是 sizeof(int) * 10,而 array 作为数组名,它的含义是“指向数组第一个元素的地址”,所以 array 的跨度是 sizeof(array[0]),因此编译系统遇到 int (*p)[10] = array; 这样的定义就会果断报错:“左右类型不一致”。
追问:那 &array 怎么可以?
追答:天啊,快点忘掉 &array 的值等于 array 这一回事儿吧(你会这么想完全是因为这个)……因为 &array 是整个数组的地址,所以它的跨度自然而然就是整个数组啦。
动动手:
0. 现学现用:本道题要求你快速学会一个新的技巧并独立完成练习。T
教学篇
我们前面使用的 main 函数都是不带参数的,所以 main 后的括号都是空括号:
int main()
{
……
}
实际上,main 函数是可以带参数的,这个参数可以认为是 main 函数的形参(形式参数)。C 语言规定 main 函数的参数只能有两个,习惯上把这两个参数写为 argc 和 argv。
C 语言还规定C语言还规定了argc(第一个形参)必须是整型变量,argv(第二个形参)必须是指向字符指针的指针(注意:它不是指针数组,但你可以理解为指向一个字符指针数组名或字符串数组,在函数章节我们将详细讲解,这里只要求你能够根据案例模仿实现即可)。
因此,main 函数可以写成下边这样:
int main (int argc,char *argv[])
{
……
}
我们知道 main 函数是由系统进行调用的(我们自己无法调用它),那么如何给它传递参数呢?
其实我觉得你们应该猜到了,main 函数的参数是从命令行上接收的。
举个例子大家就明白了,看代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}
编译的时候指定输出的文件名:
gcc test.c -o test
观察下边几种执行程序的方式:
对比一下代码,大家就应该不难发现:argc 参数指定的是程序的参数数量(包括程序名本身),而 argv 这个指针数组指向的则是每个参数的名字(字符串)。"
好了,新技能Get√就到这里结束,大家开始做练习!
写一个叫 sum 的程序,计算后边紧跟着的所有整型参数的和。
提示:如果你不希望自己实现字符串到整数的转换,可以使用 atoi 函数
程序实现如下:P
答:我用的dev c++,实现不了gcc编译器的操作(错误)
#include <stdio.h>
int main(int argc, char *argv[])
{
int i,sum=0;
for(i=0; i<argc; i++)
{
sum = sum + (int)(*wargv[i]);
}
printf("sun = %d\n",sum);
}
答案:(理解不了答案)
#include <stdio.h>
int main(int argc, char *argv[])
{
int result = 0;
while (argc-- != 1)
{
result += atoi(argv[argc]);
}
printf("sum = %d\n", result);
return 0;
}
Ij1. 虽然你们看到这道题可能会很想扁我……但这道题只有真正理解指针数组和数组指针的童鞋才能正确实现,一头雾水的童鞋请回顾下相关概念吧X
填充空白部分的代码:来
Ij@[`8aH+bNJ K#&sOfUk%crLgP,p7
要求实现结果如下:
答:答不出来(错误)
答案:(还能这么操作?直接多加一维度的数组?)
#include <stdio.h>
int main()
{
char *array[5] = {"FishC", "Five", "Star", "Good", "WoW"};
char *(*p)[5] = &array;
int i, j;
for (i = 0; i < 5; i++)
{
for (j = 0; (*p)[i][j] != '\0'; j++)
{
printf("%c ", (*p)[i][j]);
}
printf("\n");
}
return 0;
}
解析:其实这道题是按照课堂上的代码”依葫芦画瓢“,仔细推敲并不难理解。我们只是把整型变成了字符指针类型(即指向字符串第一个字符的指针),所以定义数组指针的时候,类型改为 char *,索引时只需要在原来的基础上使用下标法或指针法,即可打印出字符串的每个字符。
当然,根据下标法和指针法可以互换的原理,代码也可以这么写:
( *(*p+i) 为 (*p)[i] )
#include <stdio.h>
int main()
{
char *array[5] = {"FishC", "Five", "Star", "Good", "WoW"};
char *(*p)[5] = &array;
int i, j;
for (i = 0; i < 5; i++)
{
for (j = 0; *(*(*p + i) + j) != '\0'; j++)
{
printf("%c ", *(*(*p + i) + j));
}
printf("\n");
}
return 0;
}
2. 修改上一题的代码,要求按下边格式输出:
代码清单:
#include <stdio.h>
#include <string.h>
int main()
{
char *array[5] = {"FishC", "Five", "Star", "Good", "Wow"};
char *(*p)[5] = &array;
int i, j;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (i > strlen((*p)[j]) - 1)
{
break;
}
printf("%c ", (*p)[j][i]);
}
printf("\n");
}
return 0;
}
答:答不出来(错误)
答案:
解析:矩阵转置还记得吗?(我们在讲二维数组时候讲过哦~),只需要简单的将 i 和 j 两个循环变量调换一下位置即可实现。
不过这道题还要考察你的细心程度,像下边这么写就会造成字符数组的越界访问:
#include <stdio.h>
int main()
{
char *array[5] = {"FishC", "Five", "Star", "Good", "Wow"};
char *(*p)[5] = &array;
int i, j;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if ((*p)[j][i] == '\0')
{
break;
}
printf("%c ", (*p)[j][i]);
}
printf("\n");
}
return 0;
}