C语言指针错题集

1.若有函数原型:void f(int,int*);和变量声明:int a=2,*p=&a;则下列函数调用正确的是( )。

A. f(a,*p);

B. f(*p,a);

C. f(a,&p);

D. f(*p,&a);


在这道题中,我们分析函数调用是否正确时,主要关注参数类型是否与函数原型匹配

函数原型:

void f(int, int*);

这个函数接受两个参数:

  1. 第一个参数是 int 类型。
  2. 第二个参数是 int* 类型(指向整型的指针)。

变量声明:

int a = 2, *p = &a;
  1. a 是一个整型变量,其类型是 int
  2. p 是一个指向整型变量的指针,其类型是 int*
  3. *p 是对指针 p 的解引用,其类型是 int
  4. &p 是指针变量 p 的地址,其类型是 int**(指向指针的指针)。

选项分析:

A. f(a, *p);
  • 第一个参数 a:是 int 类型,与 int 匹配,正确。
  • 第二个参数 *p:是解引用指针 p 后的值,类型是 int,但函数需要 int*,因此类型不匹配。

结论:错误。

B. f(*p, a);
  • 第一个参数 *p:是解引用指针 p 后的值,类型是 int,与 int 匹配,正确。
  • 第二个参数 a:是 int 类型,但函数需要 int*,因此类型不匹配。

结论:错误。

C. f(a, &p);
  • 第一个参数 a:是 int 类型,与 int 匹配,正确。
  • 第二个参数 &p:是指针变量 p 的地址,类型是 int**,但函数需要 int*,因此类型不匹配。

结论:错误。

D. f(*p, &a);
  • 第一个参数 *p:是解引用指针 p 后的值,类型是 int,与 int 匹配,正确。
  • 第二个参数 &a:是变量 a 的地址,类型是 int*,与 int* 匹配,正确。

结论:正确。

正确答案:

*D. f(p, &a);

———————————————————————————————————————————

2. (单选题, 2分)如果定义int a[10], *p=a; 则不与a[5]等价的是( )。

A. p + 5

B. p[5]

C. *(p + 5)

D. *(a + 5)


问题解析:

int a[10], *p = a; 定义了一个长度为 10 的整型数组 a,并且将指针 p 指向数组的起始地址 a。在 C 语言中,数组名 a 本质上是一个指向数组第一个元素的指针,因此 ap 都可以用于访问数组中的元素。

目标:判断哪一个表达式不等价于 a[5]

各选项分析:

A. p + 5
  • p + 5 是一个指针运算,结果是指针 p 偏移 5 个元素后的地址。
  • 它并未解引用,只是一个地址,和 a[5] 不等价。

结论不等价

B. p[5]
  • p[5] 等价于 *(p + 5),即访问指针 p 偏移 5 个位置后的值。
  • 它的值是数组 a 中第 5 个元素的值,等价于 a[5]

结论:等价。

C. *(p + 5)
  • *(p + 5) 是对 p + 5 的解引用,访问偏移 5 个位置的数组元素。
  • 它的值等价于 a[5]

结论:等价。

D. *(a + 5)
  • *(a + 5) 是对 a + 5 的解引用,访问偏移 5 个位置的数组元素。
  • 它的值等价于 a[5]

结论:等价。

正确答案:

A. p + 5

原因:

p + 5 仅是一个地址,而 a[5] 表示该地址处的值。两者不等价。

———————————————————————————————————————————

3. (单选题, 2分)若有:int a[] = {0,1, 2, 3, 4, 5, 6, 7, 8, 9}, *p = &a[0], i; 其中0≤i≤9,则对a数组元素不正确的引用是( )。

A. p[i]

B. *p

C. a[10]

D. *(&a[i])


问题分析:

给定:

int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &a[0], i;
  • a 是一个长度为 10 的整型数组,索引范围是 09
  • p 是指向数组起始地址的指针。
  • 问题是判断哪个表达式对数组 a 的引用不正确

各选项分析:

A. p[i]
  • p[i] 等价于 *(p + i),表示指针 p 偏移 i 个位置后的值。
  • 因为 p 指向 a[0],索引 i09 的范围内时,p[i] 是合法的。
  • i = 10 或更大时,访问越界,但题目约定 0 ≤ i ≤ 9,因此是正确的引用。

结论:正确引用。


B. *p
  • *p 表示对指针 p 指向地址的解引用,获取 a[0] 的值。
  • 由于 p 初始化为 &a[0],所以 *p 总是合法的。

结论:正确引用。


C. a[10]
  • a[10] 是对数组索引为 10 的元素的访问。
  • 数组 a 的有效索引范围是 09,索引 10 超出范围,会导致数组越界访问。

结论:不正确引用。


D. *(&a[i])
  • &a[i] 是取数组中第 i 个元素的地址。
  • *(&a[i]) 对这个地址解引用,获取第 i 个元素的值。
  • 只要 i09 的范围内,这个引用是合法的。

结论:正确引用。


正确答案:

C. a[10】

原因:

索引 10 超出数组 a 的范围,导致越界访问,是不正确的引用。

———————————————————————————————————————————

4.以下函数的返回值是( )。

char *fun(char * p) {
return p;
}

A. p自身的地址值

B. p[0]这个字符

C. p指向的地址值

D. 无意义的值


问题分析:

函数定义如下:

char *fun(char *p) {
    return p;
}
函数的功能:
  • 参数 p 是一个 char* 类型的指针,指向一个字符数组或字符串的起始地址。
  • 函数的返回值类型是 char*,它直接返回输入参数 p

各选项解析:

A. p自身的地址值
  • p 是一个局部变量,存储的是指向某个字符的地址,但 fun 函数返回的是 p 的值,而不是 p 自己的地址。
  • 因此,返回的不是 p 自身的地址,而是 p 中存储的内容。

结论:错误。

B. p[0]这个字符
  • p[0]p 指向的地址中存储的第一个字符。
  • 但函数返回的是 p,而不是 p[0],所以返回的不是一个字符。

结论:错误。

C. p指向的地址值
  • p 是指向某个字符的地址,而函数返回的正是 p 的值(即指向某字符的地址值)。
  • 因此,函数的返回值是 p 指向的地址。

结论:正确。

D. 无意义的值
  • 返回值 p 是调用者提供的有效地址,所以返回值是有意义的。
  • 只有在传递非法指针或未初始化指针时,返回的才可能是无意义的值。

结论:错误。


正确答案:

C. p指向的地址值

说明:

函数返回的是指针 p 的值,而 p 存储的是指向某字符的地址值,因此函数的返回值是 p 指向的地址值。

———————————————————————————————————————————

5.以下代码段的输出结果是________。

int x, y, z, w;
void p(int *y, int x){
static int w;
*y++;
x++;
w = x+*--y;
printf(“%d#%d#%d#%d#”, x, *y, z, w);
}
int main(void) {
int x, y, z, w;
x=y=z=w=1;
do {
static int x;
p(&x, y);
printf(“%d#%d#%d#%d”, x, y, z, w);
} while(0);
return 0;
}


代码逐步解析

全局变量
int x, y, z, w;
  • 全局变量 x, y, z, w 默认初始化为 0

函数 p 定义
void p(int *y, int x) {
    static int w; // 静态局部变量 w,初始值为 0
    *y++;         // y 指针自增,但不影响其原指向的数据
    x++;          // 局部变量 x 自增,值变为 2
    w = x + *--y; // 指针 y 回退并解引用,计算 w
    printf("%d#%d#%d#%d#", x, *y, z, w);
}
  1. 静态局部变量 w

    • 静态变量 w 在函数调用之间保持值不变。
    • 初始值为 0,在第一次调用后被更新。
  2. 操作分析

    • *y++ 使指针 y 向后移动一位,但对其指向的值没有修改作用。
    • x++ 将局部变量 x 自增。
    • *--y 使指针 y 回退,访问原来的值。

主函数
int main(void) {
    int x, y, z, w;
    x = y = z = w = 1; // 局部变量初始化为 1
    do {
        static int x; // 静态局部变量 x,初始值为 0
        p(&x, y);     // 调用函数 p,将静态变量 x 的地址传入
        printf("%d#%d#%d#%d", x, y, z, w);
    } while(0);
    return 0;
}
  1. 静态局部变量 x

    • main 中,静态局部变量 x 的初始值为 0
    • p(&x, y) 调用时,传入了该静态变量的地址。
    • 注意p 函数不会直接修改静态变量 x 的值。
  2. 调用 p(&x, y) 的行为

    • x 是静态局部变量,初始值为 0,通过 *y 解引用操作被读取,但未被修改。
    • 输出计算:
      • x++ (局部变量,与静态局部变量 x 无关)使局部 x 的值为 2
      • *--y 指向静态局部变量 x 的值,仍为 0
      • w = x + *--y,结果为 2 + 0 = 2
  3. 返回到主函数后的输出

    • 静态局部变量 x 保持为 0(未被修改)。
    • 其他局部变量(y, z, w)的值保持为 1

输出分析

第一次调用 p(&x, y) 的输出:
  • x(局部变量):2
  • *y:静态局部变量 x 的值,0
  • z:全局变量,值为 0
  • w:静态局部变量,计算结果为 2

输出:

2#0#0#2#
回到主函数后的输出:
  • 静态局部变量 x:保持为 0
  • 局部变量 y:值为 1
  • 局部变量 z:值为 1
  • 局部变量 w:值为 1

输出:

0#1#1#1

最终完整输出

2#0#0#2#0#1#1#1

———————————————————————————————————————————

以下程序的输出结果是________。

#include <stdio.h>
int main(void) {
char *alpha[4] = {“ABCD”, “EFGH”, “IJKL”,“MNOP”};
char *p;
int i;
p = alpha[0];
for(i = 0; i < 4; p = alpha[++i])
printf(“%c”,*p);
printf(“\n”);
return 0;
}


我们逐步解析代码以确定输出结果:


代码分析

全局变量定义
char *alpha[4] = {"ABCD", "EFGH", "IJKL", "MNOP"};
  • 定义了一个字符串数组,其中 alpha 是一个包含 4 个字符串指针的数组。
  • 每个元素是一个字符串的首地址:
    alpha[0] -> "ABCD"
    alpha[1] -> "EFGH"
    alpha[2] -> "IJKL"
    alpha[3] -> "MNOP"
    

变量定义与初始化
char *p;
int i;
p = alpha[0];
  • 定义一个字符指针 p,并初始化为 alpha[0],即指向字符串 "ABCD" 的首地址。

循环逻辑
for (i = 0; i < 4; p = alpha[++i]) {
    printf("%c", *p);
}
  1. 循环初始条件

    • i = 0p = alpha[0](指向 "ABCD")。
  2. 循环体执行

    • *p 解引用,获取当前字符串的第一个字符。
    • printf("%c", *p) 输出该字符。
  3. 循环更新条件

    • p = alpha[++i] 更新 p 指向下一个字符串的首地址。
  4. 循环结束条件

    • i < 4 不满足时,退出循环。

循环的详细执行过程
迭代次数变量 i 的值指针 p 指向*p 的值输出
第一次i = 0alpha[0]'A'A
第二次i = 1alpha[1]'E'E
第三次i = 2alpha[2]'I'I
第四次i = 3alpha[3]'M'M
  • 循环退出时,i = 4,条件 i < 4 不满足,结束。

循环结束后
printf("\n");
  • 输出一个换行符。

输出结果

程序最终输出:

AEIM

———————————————————————————————————————————

6. (单选题, 2分) 两个指针变量,所指的类型相同,则它们之间不能进行的运算是( )。

A.<

B.-

C.+

D.==


在 C/C++ 中,两个指针变量指向相同类型的数据时,可以进行某些操作,但有些操作是不合法的。以下是对各选项的分析:


A. <

  • 比较两个指针的大小(例如 <>)是合法的,前提是它们指向同一块连续的内存区域(例如同一个数组)。因此 < 是合法的。

B. -

  • 两个指针相减是合法的,前提是它们指向同一个数组的元素。结果表示两个指针之间的元素个数(以 ptrdiff_t 类型表示)。因此 - 是合法的。

C. +

  • 两个指针相加是非法的。C/C++ 不支持两个指针直接相加,因为这种操作没有明确的语义意义。因此 + 是不能进行的操作

D. ==

  • 判断两个指针是否相等(==!=)是合法的操作,用于比较它们是否指向相同的位置。因此 == 是合法的。

正确答案:C. +

———————————————————————————————————————————

7.以下程序的输出结果是( )。

#include <stdio.h>
int main(void) {
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int *p = a + 5, *q = NULL;
*q= *(p + 5);
printf(“%d%d\n”, *p, *q);
return 0;
}

A.运行后报错

B.6 6

C.6 12

D.5 5


这个程序有以下问题需要注意:

分析程序:

  1. 定义了一个数组 a 并初始化为 {1, 2, 3, ..., 12}
  2. 指针 p 被初始化为 a + 5,即指向数组第6个元素(值为6)。
  3. 指针 q 被初始化为 NULL,这是一个空指针,不能被解引用
    • 语句 *q = *(p + 5) 试图通过 q 解引用赋值,这会导致运行时错误,因为 q 指向无效的内存地址(NULL 指针)。
  4. 代码中的 printf 尝试访问 *p*q,但由于程序在 *q = *(p + 5) 处已经崩溃,printf 不会执行。

错误原因:

  • 解引用空指针 q 会导致未定义行为(在大多数情况下会引发段错误)。

结论:

程序运行后会报错。

正确答案:A. 运行后报错

———————————————————————————————————————————

8.下面函数fun的功能是( )。

void fun(char *s1, char *s2) {
while(*s2++=*s1++);
}

A.串反向

B.串复制

C.求串长

D.串比较


我们来分析函数 fun 的功能:


函数内容解释

void fun(char *s1, char *s2) {
    while (*s2++ = *s1++);
}
  • *s2++ = *s1++:
    • 这表示将指针 s1 当前指向的字符复制到指针 s2 当前指向的位置。
    • 然后,s1s2 分别向后移动一位(自增操作)。
  • while 循环:
    • 条件是 *s1 的值(即 s1 当前指向的字符)。
    • *s1'\0'(字符串结束符)时,赋值操作完成,同时条件为假,退出循环。

功能描述

  • 该函数从 s1 开始逐个字符复制到 s2,包括字符串的结束符 '\0',从而完成字符串的复制。
  • 它不会改变 s1s2 的内容顺序。
  • 这就是 字符串复制 的功能。

各选项分析

  • A. 串反向
    函数没有执行任何字符顺序的反转操作,不符合描述。

  • B. 串复制
    函数将 s1 的内容逐字符复制到 s2,功能正是字符串复制,符合描述。

  • C. 求串长
    函数没有统计字符串长度的操作,不符合描述。

  • D. 串比较
    函数没有比较 s1s2 的内容,不符合描述。


正确答案:B. 串复制

———————————————————————————————————————————

9.对于下面的程序段,叙述正确的是______

char s[] = “china”, *p = s;

A.*p与s[0]相等

B.数组s中的内容和指针变量p中的内容相等

C.s和p完全相同

D.数组s的长度和p所指向的字符串长度相等


分析题目所提供的代码以及选项,正确答案为:

A. *p与s[0]相等

原因分析:

  1. 选项 A

    • p 是一个指针,初始化为指向数组 s 的首地址(char *p = s 等价于 char *p = &s[0])。
    • *pp 所指向地址处的值,即 s[0]
    • 因此,*p == s[0] 是正确的。
  2. 选项 B

    • 数组 s 的内容是字符串 "china",而 p 是一个指针变量,存储的是数组 s 的地址。
    • 数组 s 的内容和指针变量 p 的内容本质不同,二者不相等
  3. 选项 C

    • s 是数组名,表示数组的起始地址(指针常量),而 p 是一个普通指针变量。
    • 尽管 p 的初始值与 s 的地址相同,但它们的语义不同:s 不能被修改,而 p 是可以改变的。
    • 因此,sp 不完全相同。
  4. 选项 D

    • 数组 s 的长度是固定的(包括末尾的 '\0',总长度为 6 字节),而 p 所指向的字符串 "china" 的长度(即不包含 '\0' 时)是 5。
    • 二者的长度不同,因此该选项错误。

综上所述:

正确答案为 A

————————————————————————————————————

10. (单选题, 2分) 若定义:char s[2][3]= {“ab”, “cd”}, *p = (char *)s;那么下列表达式语法正确,并且其值与 s[1][1]相等的表达式(并非一定与其等价)是( )。

A.*(s+3)

B.*++p+2

C.*s+2

D.p[1][1]


正确答案是:*B. ++p+2

逐个分析各个选项:

  1. 选项 A: *(s + 3)

    • s 是一个 char[2][3] 类型的二维数组。
    • s + 3 表示移动数组 s 的指针 3 个元素,它的类型是 char (*)[3],所以 *(s + 3) 会访问 s 数组中越界的位置(实际上访问的是 s[3],这超出了 s 数组的范围)。
    • 结果:会导致越界访问,语法合法但行为未定义,因此 A 错误
  2. 选项 B: *++p + 2

    • pchar* 类型的指针,指向 s[0][0]
    • ++pp 移动到 s[0][1](即字符 'b')。
    • *++p 解引用 p,此时得到的是字符 'b'
    • 然后 *++p + 2 表示 'b' + 2,即 'd'。但是由于 pchar* 类型,*++p 返回的是字符,+2 会增加两个字符的偏移量,从 'b' 开始,经过两个字符后得到 s[1][1],即 'd'
    • 结果:值与 s[1][1] 相等,因此 B 正确
  3. 选项 C: *s + 2

    • schar[2][3] 类型,*s 表示 s[0],即 char[3] 类型的数组,*s{'a', 'b', '\0'}
    • *s + 2 是将 *s 解引用后加 2,等价于访问 s[0][2],即 '\0'(字符串结束符)。
    • 结果:与 s[1][1] 不相等,因此 C 错误
  4. 选项 D: p[1][1]

    • pchar* 类型,指向 s[0][0]
    • p[1] 表示 s[1][0],即字符 'c'
    • 然后 p[1][1] 表示访问 s[1][1],即字符 'd'
    • 结果:值与 s[1][1] 相等,因此 D 正确

结论:

*++p + 2 是与 s[1][1] 相等的表达式,因此 B 是正确答案。

————————————————————————————————————

11.以下代码段的输出结果是________。

char *a[2] = {“one”, “two”}, **p = a;
printf(“%s,”,*(p++) + 1);
printf(“%c\n”,**p - 1);


让我们逐步分析这段代码:

char *a[2] = {"one", "two"}, **p = a;
printf("%s,", *(p++) + 1);
printf("%c\n", **p - 1);

1. 变量初始化:

char *a[2] = {"one", "two"}, **p = a;
  • a 是一个大小为 2 的字符指针数组,a[0] 指向字符串 "one"a[1] 指向字符串 "two"
  • p 是一个指向指针的指针,初始化时指向数组 a 的第一个元素,即 a[0](指向字符串 "one")。

2. 第一次 printf 调用:

printf("%s,", *(p++) + 1);
  • p++ 先取 p 当前指向的值(即 a[0],即 "one"),然后再将 p 移动到 a[1]
  • *(p++) 取的是指针 a[0] 的值,即 "one"
  • *(p++) + 1 通过将字符串 "one" 的指针向后偏移 1 位,从而指向字符串 "ne"(即 *(p++) + 1"ne")。
  • 结果: printf("%s,", *(p++) + 1); 打印出 "ne,"

3. 第二次 printf 调用:

printf("%c\n", **p - 1);
  • 由于 p 在上一步已经执行了 p++,现在 p 指向 a[1](即指向字符串 "two")。
  • **p 解引用 p 两次,首先 *p 是指向 "two" 的指针,接着 **p"two" 字符串的第一个字符,即 't'
  • **p - 1 会将字符 't' 的 ASCII 值减去 1,ASCII 值为 116,减去 1 后变成 115,表示字符 's'
  • 结果: printf("%c\n", **p - 1); 打印出字符 's'

最终输出:

ne,s

结论:

代码的输出结果是 ne,s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值