【c语言指针精选题】


🤾‍♂️感觉指针难的可以看看这些题,肯定收获很大!

在这里插入图片描述

一、概念易错题

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,则更新rq(即当前更小字符位置)。因此选 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 函数内的 sp 的副本,修改 s 的指向不会影响 mainp 的值。最终 puts(p) 操作野指针,访问的内存地址不确定,程序运行结果无法预知。

答案:D

1.17 main函数形参1

在这里插入图片描述

注意第二个参数是个二维指针!

在命令行中,argc 表示参数个数,argv[0] 固定为可执行程序名(即 echo)。题目要求输出 china tianjin,需确保 argv[1]chinaargv[2]tianjin。此时命令行应输入可执行程序名 echo 后跟两个参数,即 echo china tianjin

  • A:缺少可执行程序名,错误。
  • Becho china tianjinargv[1]chinaargv[2]tianjin,符合输出要求,正确。
  • C:仅输入 echo,无后续参数,无法输出目标内容,错误。
  • D:格式不匹配,无法正确对应 chinatianjin 作为参数,错误。

答案: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函数形参固定为两个(argcargv形式),并非用户任意指定个数,错误。
  • Bmain函数形参名并非只能是argcargv,只是习惯使用这两个名称,语法上允许自定义命名,错误。
  • Cmain函数的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 当时没看懂的一个题

在这里插入图片描述
在这里插入图片描述

对变量进行 -- 运算,要求其为合法左值且指向可操作的内存空间。分析选项:

  • Aint *a = p;a 是指针,但初始指向数组首元素,直接 -- 会越界,逻辑不合理。
  • Bint *a = &k;a 是指针,指向单个变量 k,对指针 a 执行 -- 无实际意义(非指向数组元素)。
  • Cchar *a[3];a 是指针数组名,本质是常量地址,不能作为左值进行 -- 运算。
  • Dint *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 注意指针已经指向的东西

在这里插入图片描述
在这里插入图片描述

程序运行分析如下:

  1. main函数p指向b(初始值为2),调用pp(a+c, &b)pp(4, &b)
  2. pp函数
    • *p = *b + c:因p是全局指针,指向main中的b,执行后mainb变为2 + 4 = 6
    • a = *p - c*p6,计算得a = 6 - 4 = 2
    • 输出(2) 2 6 6a2*b6*p6)。
  3. 返回main:输出(1) 1 6 6a仍为1b已改为6*p6)。

最终运行结果:

(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++ *pp指向的元素(即a[0])自增,变为2,随后该指针所指向自增后的值,输出值2。最终输出:

2,2  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱康代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值