🤾♂️感觉指针难的可以看看这些题,肯定收获很大!
一、概念易错题
1.1🚀交换两值
A 选项: 程序采用值传递,不能实现变量值的交换,所以该选项错误。
B 选项:将swap(&a,&b); 中的参数改为a,b,就变成了值传递,更无法实现交换,该选项错误。
C 选项: 仅将形参定义为指针,而执行语句不变,依然没有通过指针去修改指向的变量的值,不能实现交换,该选项错误。
🎈答案:D
swap(int *p,int *q)//选项C
{
int *t;
t = p;
p = q;
q = t;
}
/*这段代码只是在函数内部对指针变量 p、q 自身的值进行了交换,
也就是改变了它们指向的位置,但并没有通过指针去修改主函数中 a、b
这两个变量的值 。*/
swap(int *p,int *q) //这样才对
{
int t;
t = *p;
*p = *q;
*q = t;
}
1.2🚀野指针
A 选项:p = &n; 让指针 p 指向变量 n,scanf(“%d”, &p); 中 &p 是取指针 p 本身的地址,而 scanf 需要的是存储输入值的变量的地址,应该是 p 而不是 &p,所以该选项错误。
B 选项:p = &n; 让指针 p 指向变量 n,但 scanf(“%d”, *p); 中 *p 表示指针 p 所指向的变量 n,scanf 需要的是地址,这里传递的是变量值,所以该选项错误。
C 选项:scanf(“%d”, &n); 正确读取一个整数存入 n 中,但 *p = n; 时,指针 p 未初始化指向,直接对 *p 赋值会导致访问非法内存,所以该选项错误。
D 选项:p = &n; 让指针 p 指向变量 n,*p = m; 即把 m 的值赋给 p 所指向的变量 n,是合理的操作,该选项正确。
🎈答案:D
重点看C选项: *p = n;:这行代码存在问题。在 C 语言中,指针在使用前需要明确其指向,p虽然被声明为指向整型的指针int p,但在执行p = n;之前,并没有让p指向一个有效的内存地址(比如通过p = &某个已声明的整型变量;的方式)。此时p的值是不确定的,也就是它指向的内存位置是未知的。 对*p进行赋值操作,相当于向一个不确定的内存位置写入数据,这可能会覆盖其他重要的数据,甚至导致程序崩溃,这种行为被称为 “野指针” 操作,是非常危险且错误的。
1.3 🚀字符数组与指针语句判断
A 选项:
char *a = “china”; 是定义一个字符指针 a ,并让它指向字符串常量 “china” 的首地址。
而 char *a; *a = “china”; 中,char *a; 声明了指针 a ,但 *a = “china”; 是错误的。*a 表示指针 a 所指向的字符,不能直接将一个字符串赋值给一个字符,所以这两个语句不等价,A 选项错误。
B 选项:
char str[10] = {“china”}; 是正确地定义并初始化一个字符数组 str,长度为 10,初始化为字符串 “china”。
char str[10]; str[] = {“china”}; 中,str[] 这种写法在初始化时是错误的,数组名 str 本身代表数组首地址,不能这样使用来初始化,B 选项错误。
C 选项:
char *s = “china”; 定义字符指针 s 并指向字符串常量 “china”。
char *s; s = “china”; 先声明字符指针 s ,然后让 s 指向字符串常量 “china” ,这两个语句效果等价,C 选项正确。
D 选项:
char c[4] = “abc”, d[4] = “abc”; 是分别定义并初始化两个字符数组 c 和 d。
char c[4] = d[4] = “abc”; 是错误的,在 C 语言中不能这样连续对数组进行初始化赋值,D 选项错误。
🎈答案:C
✨B正确的方式:
//先声明后逐个字符赋值
#include <stdio.h>
int main() {
char str[10];
str[0] = 'c';
str[1] = 'h';
str[2] = 'i';
str[3] = 'n';
str[4] = 'a';
str[5] = '\0'; // 字符串结束标志不能忘
return 0;
}
//先声明后使用strcpy函数赋值
#include <stdio.h>
#include <string.h>
int main() {
char str[10];
strcpy(str, "china");
return 0;
}
1.5 🚀字符串赋值
A 选项:
在 C 语言中,使用字符串常量初始化字符数组时,系统会自动在字符串末尾添加 ‘\0’ 作为结束标志。这里 char s[5] = {“ABCDE”}; ,“ABCDE” 实际长度为 5,加上系统自动添加的结束符 ‘\0’ ,共需要 6 个字节的空间,而 s 数组大小为 5,空间不足,会导致数组越界,所以该选项错误。
B 选项:
char s[5] = {‘A’, ‘B’, ‘C’, ‘D’, ‘E’}; 这样只是初始化了一个包含 5 个字符的字符数组,它并不是一个字符串,因为缺少字符串结束标志 ‘\0’,不符合字符串赋值操作的要求,所以该选项错误。
C 选项:
char *s; s = “ABCDE”; 定义了一个字符指针 s ,然后让指针 s 指向字符串常量 “ABCDE” ,这是合法的操作,实现了将字符串的首地址赋给指针 s ,所以该选项正确。
D 选项:
char *s; scanf(“%s”, s); 中,指针 s 未初始化,它指向的地址是不确定的。使用 scanf 函数向一个未确定指向的指针所指向的内存区域写入数据,会导致访问非法内存,可能引发程序崩溃,所以该选项错误。
🎈答案C
本题也是野指针问题,s就是个野指针。
1.6 🚀指针的移动
首先,char *s = “abcde”; 定义了一个字符指针 s ,并让它指向字符串常量 “abcde” 的首地址,也就是字符 ‘a’ 的地址。
然后,s += 2; 这一操作使指针 s 向后移动了 2 个字符的位置(因为 s 是字符指针,移动单位是字符大小),此时 s 指向字符串中的字符 ‘c’ 的地址。
最后,printf(“%d”, s); 这里使用 %d 格式控制符输出 s 。在 C 语言中,指针本质上存储的是地址,以整数形式表示。所以这里输出的是字符 ‘c’ 的地址。
🎈答案C
🥇指针的移动:
算术运算:可通过+、-、++、–等运算符实现移动。如p++使指针指向下一个同类型数据的地址;p + n(n为整数)让指针向后移动n个同类型数据的位置;p - n则向前移动n个同类型数据的位置。
数组场景:数组名可视为常量指针,指向数组首元素。对数组名进行指针移动操作,能遍历数组元素,如arr[i]和*(arr + i)等价。
1.7 🚀数组和指针内容
A 选项:
s 是字符数组名,它是一个常量指针,代表数组首地址,其值不能被改变;p 是一个字符指针变量,可以指向不同的地址,两者本质不同,并非完全相同,该选项错误。
B 选项:
数组 s 中存储的是字符串 “china” 的各个字符以及结束符 ‘\0’ ;指针变量 p 中存储的是数组 s 的首地址,内容不相等,该选项错误。
C 选项:
数组 s 的长度为 6(包含字符串结束符 ‘\0’ ),而 p 所指向的字符串 “china” 长度为 5(不包含结束符),两者不相等,该选项错误。
D 选项:
p = s; 使指针 p 指向数组 s 的首地址,*p 表示取指针 p 所指向地址处的值,也就是 s[0] 的值,所以 *p 与 s[0] 相等,都为字符 ‘c’ ,该选项正确。
🎈答案:D
注意B选项!
字符数组s:当定义 char s[] = “china”; 时,系统会开辟一段连续的内存空间,按照顺序依次存储字符’c’、‘h’、‘i’、‘n’、‘a’以及字符串结束标志’\0’ 。虽然数组名s在表达式中会被转换为首元素的地址,但数组本身的内存空间里存的是具体的字符数据。
1.8🚀printf函数的格式控制字符串
在printf(format, “a+=b”, a, b);中:
format的值为"%s,a=%d,b=%d\n",是printf函数的格式控制字符串。
按照printf函数的规则,%s需要一个字符串类型的参数来替换,这里传入的是"a+=b";%d需要整数类型的参数来替换,这里依次传入a和b,但在执行a += b;后,a的值变为11,b的值是10 。
所以最终printf函数会按照格式控制字符串将对应参数输出,即输出a+=b,a=11,b=10。
🎈答案C
//如下也是一样的
#include <stdio.h>
int main() {
int a = 1, b = 10;
a += b;
printf("%s,a=%d,b=%d\n", "a+=b", a, b);
return 0;
}
/*两者区别在于,使用指针变量可以更灵活地在程序中动态地改变
所指向的格式控制字符串内容,而字符串常量是固定不变的。*/
1.9🚀scanf函数将用户输入的字符串
A 选项:
char str[20];声明了一个能存储 20 个字符的数组str。scanf(“%s”, &str);存在错误,str本身就代表数组首地址,相当于&str[0],对数组名取地址&str得到的是指向整个数组的指针,类型和scanf期望接收的字符数组首地址(指向字符的指针)不匹配,所以该选项错误。
B 选项:
char *p;声明了一个字符指针p,但p未初始化,是野指针。scanf(“%s”, p);试图往一个不确定的内存地址写入数据,会导致程序出现未定义行为,所以该选项错误。
C 选项:
char str[20];声明字符数组str。scanf(“%s”, &str[2]);表示从数组的第 3 个元素(数组下标从 0 开始)处开始存储输入的字符串,只要输入的字符串长度合适,不会导致数组越界,这种用法是正确的,所以该选项正确。
D 选项:
char str[20], p = str;声明了字符数组str并让指针p指向str的首地址。scanf(“%s”, p[2]);存在错误,p[2]等价于(p + 2),表示指针p所指向位置偏移 2 个字符后的那个字符,是一个字符变量,不是地址,而scanf需要的是地址参数,所以该选项错误。
🎈答案C
scanf存在的问题及限制:
无法处理包含空格的字符串:scanf函数遇到空格、制表符或换行符时会认为字符串输入结束,因此不能完整接收包含这些字符的字符串。例如输入hello world,scanf只会将hello存入字符数组。
可能导致缓冲区溢出:如果输入的字符串长度超过字符数组的大小,scanf不会自动检查边界,会导致数据写入到数组之外的内存区域,造成缓冲区溢出,引发程序错误甚至安全问题。
1.10🚀strcpy函数
首先,char *p = “abcdefgh”; 定义了字符指针p并使其指向字符串常量 “abcdefgh”。
然后,p += 3; 让指针p向后移动 3 个字符位置,此时p指向字符’d’。
接着,strcpy(p, “ABCD”) 是将字符串 “ABCD” 复制到p所指向的位置。因为p指向原字符串 “abcdefgh” 中的’d’,所以从’d’开始覆盖,原字符串变为 “abcABCD” 。strcpy函数返回目标字符串的首地址,即p。
最后,strlen(strcpy(p, “ABCD”)) 计算复制后的字符串长度。strlen函数计算字符串长度时不包含字符串结束符’\0’,字符串 “ABCD” 的长度为 4。
答案C
注意:strcpy函数会把原来的字符串覆盖,并把’\0’也复制过去。
1.11🚀常量指针
A 选项:
char a[10]=“china”; 定义一个长度为 10 的字符数组a,并使用字符串 “china” 对其初始化,系统会自动在 “china” 后面加上字符串结束符’\0’,这种方式是正确的。
B 选项:
char a[10], *p=a; 先定义字符数组a和字符指针p,并让p指向数组a的首地址。然后p=“china”; 使指针p指向字符串常量 “china”,这是合法的操作,指针p指向的内容发生了改变,该选项正确。
C 选项:
char *a; a=“china”; 定义字符指针a,然后让a指向字符串常量 “china”,即将字符串常量的首地址赋给指针a,这是正确的操作,该选项正确。
D 选项:
char a[10], *p; p=a=“china”; ,在 C 语言中,赋值表达式从右向左计算,a=“china”; 是错误的,因为数组名a是一个常量指针,代表数组的首地址,其值不能被改变,不能直接将字符串常量赋值给数组名,该选项错误。
答案:D
注意:数组名是常量指针,不能再进行赋值操作。
1.12🚀不同类型指针
A 选项:*p = 100; ,指针p在使用前没有指向一个有效的内存地址,属于野指针操作,直接对其解引用赋值是不安全的,会导致未定义行为,该选项错误。
B 选项:p = &ch; s = p; ,p是指向int类型的指针,s也是指向int类型的指针,ch是char类型变量,将char类型变量的地址赋给指向int类型的指针p,虽然不会报错,但会出现类型不匹配的问题;而且后续s = p也延续了这种潜在风险,该选项错误。
C 选项:p = &i; q = &ch; p = q; ,p是指向int类型的指针,q是指向char类型的指针,不同类型的指针之间不能直接赋值,这样会导致类型不匹配,该选项错误。
D 选项:p = &i; q = &ch; *p = 40; *q = *p; ,先让p指向int类型变量i,q指向char类型变量ch,然后给i赋值40,再将p所指向的值(即i的值40)赋给ch。在 C 语言中,int类型的值可以隐式转换为char类型(截取低 8 位),这种操作在类型转换和指针使用上是合理的,该选项正确。
答案D
注意:int类型值和char类型值可以互相赋值,但是int类型指针和char类型指针不可以。
1.13🚀二维指针
首先,int x[5] = {2, 4, 6, 8, 10}, *p, **pp; 定义了一个整型数组 x,一个指向整型的指针 p,以及一个指向指针的指针 pp。
p = x; 让指针 p 指向数组 x 的首地址,即 p 指向 x[0]。
pp = &p; 让二级指针 pp 指向指针 p 的地址。
接着看 printf(“%d”, *(p++));:
p++ 是后自增运算符,先使用 p 的当前值,再对 p 进行自增操作。此时 p 指向 x[0],*p 就是 x[0] 的值,即 2,所以第一个 printf 输出 2。输出后,p 自增,指向 x[1]。
最后看 printf(“%3d\n”, **pp);:
pp 指向 p,*pp 就是 p,此时 p 已经指向 x[1],再进行一次解引用 **pp 就相当于 *p,也就是 x[1] 的值,为 4。所以第二个 printf 输出 4(%3d 表示输出宽度为 3,不足 3 位左边补空格)。
答案B
注意:在代码中,pp = &p; 这一步执行时,pp 就指向了指针 p 的地址 。
之后执行 printf(“%d”, *(p++)); ,虽然 p 发生了自增,但 pp 存储的是 p 的地址这一事实并没有改变,它依然指向 p 对应的内存单元。不管 p 所指向的位置如何变化,pp 始终指向 p 本身所在的内存地址,所以后续通过 **pp 访问时,获取的是自增后 p 所指向的值。
1.14 学习库函数isspace
【1】解析:
for
循环需遍历字符串p
,直到遇到结束符'\0'
。条件应为p[i]
(非'\0'
时继续循环),对应选项A。若选B(! p[i]
),会在p[i]
为'\0'
时才执行循环体,逻辑错误;C、D与循环遍历需求不符。
【2】解析:
isspace
函数需判断当前字符是否为空白符,p[i]
即当前字符,而*(p+i)
等价于p[i]
,因此选C。A(p+i
)是地址,非字符;B、D操作对象是c
数组,与题意不符。
答案:
【1】A
【2】C
1.15 字符串排序 暴力
【1】解析:
内层循环需找出当前范围内最小字符,通过比较*r
(当前记录的最小字符值)与*q
(当前遍历字符值),若*r > *q
,则更新r
为q
(即当前更小字符位置)。因此选 A。
【2】解析:
若最终找到的最小字符位置r
与当前起始位置p
不同,说明需要交换两者字符,确保字符从小到大排序。因此条件为r != p
,对应选项 D。
【3】解析:
删除重复字符时,strcpy
需将从q
开始的非重复字符覆盖到当前p+1位置,以消除重复。因此目标地址应为
p+1,选 B。
答案:
【1】A 【2】D 【3】D
1.16 传的指针也是值传递,形参是个副本🚩
w
在 main
函数中,char *p
未初始化,属于野指针,指向不确定的内存地址。调用 fun(p)
时,尽管 fun
函数内部对局部数组 a
进行了操作(如 strcpy(a, "look")
),但由于 C 语言参数传递是值传递,fun
函数内的 s
是 p
的副本,修改 s
的指向不会影响 main
中 p
的值。最终 puts(p)
操作野指针,访问的内存地址不确定,程序运行结果无法预知。
答案:D
1.17 main函数形参1
注意第二个参数是个二维指针!
在命令行中,argc
表示参数个数,argv[0]
固定为可执行程序名(即 echo
)。题目要求输出 china tianjin
,需确保 argv[1]
为 china
,argv[2]
为 tianjin
。此时命令行应输入可执行程序名 echo
后跟两个参数,即 echo china tianjin
。
- A:缺少可执行程序名,错误。
- B:
echo china tianjin
,argv[1]
是china
,argv[2]
是tianjin
,符合输出要求,正确。 - C:仅输入
echo
,无后续参数,无法输出目标内容,错误。 - D:格式不匹配,无法正确对应
china
和tianjin
作为参数,错误。
答案:B
在C/C++中,argc
(命令行参数数量)和argv
(命令行参数字符串数组)通过main
函数的参数获取,具体使用方式如下:
1. 函数声明
int main(int argc, char *argv[]) {
// 代码逻辑
return 0;
}
argc
:统计命令行输入的参数总个数(包括可执行程序名)。argv
:数组元素为字符指针,argv[0]
存储可执行程序名,argv[1]
、argv[2]
等依次存储后续输入的参数。
2. 获取参数示例
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("参数总个数:%d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
- 编译运行:假设生成可执行文件名为
test.exe
,在命令行输入:test hello world 123
- 输出结果:
参数总个数:4 argv[0] = test argv[1] = hello argv[2] = world argv[3] = 123
1.18 mian函数形参2
分析各选项:
- A:C语言中
main
函数形参固定为两个(argc
和argv
形式),并非用户任意指定个数,错误。 - B:
main
函数形参名并非只能是argc
和argv
,只是习惯使用这两个名称,语法上允许自定义命名,错误。 - C:
main
函数的argc
(参数个数)和argv
(参数数组)专门用于接收命令行输入的参数,传值只能来自命令行,正确。 - D:若命令行仅输入可执行程序名,无其他参数,
argc
值为1
,并非必须大于1
,错误。
答案:C
二、数组指针
2.1🚀二维数组的地址
A 选项:*(a[i]+j) ,a[i] 相当于第 i 行的首地址,a[i]+j 表示第 i 行中第 j 个元素的地址,再对其进行解引用 *(a[i]+j) 得到的是第 i 行第 j 列的元素值,而不是地址,所以该选项错误。
B 选项:(a + i) ,a 是二维数组名,代表数组首地址,也就是第 0 行的首地址,a + i 表示第 i 行的首地址,并非第 i 行第 j 列元素的地址,所以该选项错误。
C 选项:*(a + j) ,a 是二维数组首地址,a + j 由于二维数组在内存中按行存储,这里j超过了行的范围概念(没有正确对应到i行j列),且再解引用*(a + j)得到的也不是第i行第j列元素地址,所以该选项错误。
D 选项:a[i]+j ,a[i] 表示第 i 行的首地址,a[i]+j 就是在第 i 行首地址的基础上向后偏移 j 个元素的位置,即第 i 行第 j 列元素的地址,所以该选项正确。
答案D
2.2🚀指向包含 n个int类型元素的数组的指针
在 C 语言中,int (*p)[4] 是一种特殊的指针声明形式。
A 选项:如果是指向整型变量的指针,应该是 int *p 的形式,而不是 int (*p)[4],所以 A 选项错误。
B 选项:指针数组的声明形式是 int *p[4],这里 [] 的优先级高于 *,表示 p 是一个数组,数组中的元素是指向 int 类型的指针。而 int (*p)[4] 中 () 使得 * 先与 p 结合,说明 p 是一个指针,并非指针数组名,所以 B 选项错误。
C 选项:int (*p)[4] 表示 p 是一个指针,它指向的是一个包含四个整型元素的一维数组。这种指针也被称为行指针,当用于二维数组时,可以方便地操作数组的行,该选项正确。
D 选项:int (*p)[4] 这种声明在 C 语言中是合法的,所以 D 选项错误。
答案C
2.3🚀指向包含 n个int类型元素的数组的指针访问二维数组
已知定义int a[2][3], (p)[3];,p是一个指向包含 3 个int类型元素的数组的指针,且p = a,此时p指向二维数组a的首地址(即第 0 行的地址)。
A 选项:(p + 2) ,p + 2表示指针p向后移动 2 个 “包含 3 个int元素的数组” 的位置,但数组a只有 2 行,p+2超出了数组范围,并且*(p + 2)是对移动后的指针进行解引用,得到的是一个int类型的值,不是地址,该选项错误。
B 选项:p[2] ,等价于*(p + 2),同样超出了数组范围,并且得到的是一个int类型的值,不是地址,该选项错误。
C 选项:p[1]+1 ,p[1]等价于*(p + 1),表示指向数组a的第 1 行,p[1]+1表示在第 1 行首地址的基础上向后移动 1 个int类型元素的位置,即第 1 行第 1 列元素的地址,该选项正确。
D 选项:(p + 1)+2 ,p + 1指向数组a的第 1 行,(p + 1)+2表示在第 1 行的基础上再向后移动 2 个 “包含 3 个int元素的数组” 的位置,超出了数组范围,该选项错误。
答案C
认真理解这句话:p[1]等价于*(p + 1),表示指向数组a的第 1行*斜体样式
2.4🚀指针数组
A 选项:strp 是一个指针数组名,它代表指针数组的首地址,即 strp[0] 的地址,并不是对字符串的引用,所以该选项不正确。
B 选项:str[k] ,str 是一个二维字符数组,str[k] 表示第 k 行的首地址,指向第 k 个字符串,是对字符串的正确引用,该选项正确。
C 选项:strp[k] ,通过 for 循环 strp[n]=str[n]; ,strp[k] 指向了二维数组 str 中的第 k 个字符串,是对字符串的正确引用,该选项正确。
D 选项:*strp 等价于 strp[0] ,strp[0] 指向了二维数组 str 中的第一个字符串,是对字符串的正确引用,该选项正确。
答案A
2.5🚀二维数组名代表的首地址和某行的首地址
A 选项:q[i]=b[i]; ,q 是指针数组,q[i] 是指向 int 类型的指针;b 是二维数组,b[i] 表示第 i 行的首地址,也是指向 int 类型的指针,将 b[i] 赋值给 q[i] 是合法的,该选项正确。
B 选项:p = b; ,p 是指向 int 类型的指针,b 是二维数组名,代表二维数组首地址,其类型是指向包含 6 个 int 元素的一维数组的指针(即行指针),与 p 的类型不匹配,不能直接赋值,该选项错误。
C 选项:p = b[i]; ,b[i] 是二维数组 b 第 i 行的首地址,类型是指向 int 类型的指针,与 p 的类型一致,可以赋值,该选项正确。
D 选项:q[i]=&b[0][0]; ,&b[0][0] 是二维数组 b 首元素的地址,类型是指向 int 类型的指针,q[i] 也是指向 int 类型的指针,可以进行赋值,该选项正确。
答案B
注意:b 是二维数组名,代表二维数组首地址,其类型是指向包含 6 个 int 元素的一维数组的指针(即行指针),b[i] 是二维数组 b 第 i 行的首地址,类型是指向 int 类型的指针
2.6 当时没看懂的一个题
对变量进行 --
运算,要求其为合法左值且指向可操作的内存空间。分析选项:
- A:
int *a = p;
,a
是指针,但初始指向数组首元素,直接--
会越界,逻辑不合理。 - B:
int *a = &k;
,a
是指针,指向单个变量k
,对指针a
执行--
无实际意义(非指向数组元素)。 - C:
char *a[3];
,a
是指针数组名,本质是常量地址,不能作为左值进行--
运算。 - D:
int *a = b + 1;
,a
是指针变量,指向数组b
的元素b[1]
,符合指针运算规则,可通过--
让其指向b[0]
,语法和逻辑均合法。
答案:D
三、函数指针
3.1🚀函数指针的调用
在 C 语言中,当使用函数指针调用函数时,有两种常见的正确形式:
(*函数指针变量)(实参列表):这里p是指向函数max的指针,(*p)(a,b)是通过解引用p,并传入参数a和b来调用max函数。
函数指针变量(实参列表):即可以直接使用函数指针变量p像函数名一样调用,写成p(a,b) 。
A 选项:(*p)max(a,b); ,这种写法是错误的,(*p)是对函数指针解引用,后面应跟的是参数列表,而不是函数名max,该选项错误。
B 选项:*pmax(a,b); ,这里pmax不是有效的函数指针调用形式,并且容易让人误解,该选项错误。
C 选项:(*p)(a,b); ,符合通过函数指针调用函数的正确形式,该选项正确。
D 选项:*p(a,b); ,*p是对指针p解引用,这里的写法相当于先对p解引用再传入参数,不符合函数指针调用规则,该选项错误。
答案C
四、填空题
4.1 递归实现找最值
注意这里没有循环,通过递归实现下标后!!!每递归一次,i就后动一下!
【1】解析:当发现a[i]
大于当前记录的最大值(由*pk
指向的下标对应值),需更新最大值下标,因此填 *pk = i
。
【2】解析:递归调用findmax
时,需传递数组a
、数组长度n
、下一个索引i+1
,以及记录下标的指针pk
,因此填 a, n, i+1, pk
。
答案:
【1】*pk = i
【2】a, n, i+1, pk
4.2 读懂题在问什么
【1】int a, *p;
解析:定义整型变量a
和整型指针p
,为存储结构建立基础。
【2】scanf("%d", &a);
解析:通过scanf
函数给变量a
输入数据,&a
获取a
的地址用于输入。
【3】p = &a;
解析:将变量a
的地址赋值给指针p
,使p
指向a
,完成存储结构的指针指向关系。
4.3 注意指针已经指向的东西
程序运行分析如下:
main
函数:p
指向b
(初始值为2
),调用pp(a+c, &b)
即pp(4, &b)
。pp
函数:*p = *b + c
:因p
是全局指针,指向main
中的b
,执行后main
的b
变为2 + 4 = 6
。a = *p - c
:*p
为6
,计算得a = 6 - 4 = 2
。- 输出
(2) 2 6 6
(a
为2
,*b
为6
,*p
为6
)。
- 返回
main
:输出(1) 1 6 6
(a
仍为1
,b
已改为6
,*p
为6
)。
最终运行结果:
(2) 2 6 6
(1) 1 6 6
4.4 是否是数字
程序中,p
指向字符串 "PDP1-0"
,循环遍历每个字符:
i=3
时,*(p+3)
为'1'
,是数字,输出1*
;i=5
时,*(p+5)
为'0'
,是数字,输出0*
。
最终运行结果为:
1* 0*
4.5 十进制转十六进制
【1】strlen(s) - 1
🎈
解析:转换后的十六进制字符按逆序存储在s
中,需从最后一个有效字符开始倒序输出,strlen(s) - 1
获取最后一个有效字符的下标。
【2】j < 10
解析:当余数j
小于10
时,对应十进制数字字符(如0-9
),通过j + 48
转换为字符'0'-'9'
。
【3】p++
解析:每次存储一个十六进制字符后,指针p
需后移,指向下一个存储位置,以便继续存储后续转换的字符。
答案:
【1】strlen(s) - 1
【2】j < 10
【3】p++
4.6 注意首地址不是0时长度表示
【1】s + n - 1
🎈
解析:n
为字符串长度,s + n - 1
使p2
指向字符串最后一个字符。
【2】p1 < p2
解析:循环需在首尾指针未相遇时持续比较,p1 < p2
确保未遍历完字符串中间位置。
【3】p2--
解析:当字符不相等时,除了p1
后移,p2
需前移,继续比较下一组首尾字符。
答案:
【1】s + n - 1
【2】p1 < p2
【3】p2--
4.7 指针数组
【1】解析:
循环中 i=0
时,p[0] = &a[2*0] = &a[0]
,因此 *p[0]
引用的是 a[0]
。
【2】解析:
i=1
时,p[1] = &a[2*1] = &a[2]
,p[1]+1
指向 a[3]
,故 *(p[1]+1)
引用的是 a[3]
。
4.8 printf 从右到左执行!!!
程序中,p = a
使p
指向数组a
的首元素(值为1
)。printf
中,*p
先++ *p
对p
指向的元素(即a[0]
)自增,变为2
,随后该指针所指向自增后的值,输出值2
。最终输出:
2,2