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*);
这个函数接受两个参数:
- 第一个参数是
int
类型。 - 第二个参数是
int*
类型(指向整型的指针)。
变量声明:
int a = 2, *p = &a;
a
是一个整型变量,其类型是int
。p
是一个指向整型变量的指针,其类型是int*
。*p
是对指针p
的解引用,其类型是int
。&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
本质上是一个指向数组第一个元素的指针,因此 a
和 p
都可以用于访问数组中的元素。
目标:判断哪一个表达式不等价于 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 的整型数组,索引范围是0
到9
。p
是指向数组起始地址的指针。- 问题是判断哪个表达式对数组
a
的引用不正确。
各选项分析:
A. p[i]
p[i]
等价于*(p + i)
,表示指针p
偏移i
个位置后的值。- 因为
p
指向a[0]
,索引i
在0
到9
的范围内时,p[i]
是合法的。 - 当
i = 10
或更大时,访问越界,但题目约定0 ≤ i ≤ 9
,因此是正确的引用。
结论:正确引用。
B. *p
*p
表示对指针p
指向地址的解引用,获取a[0]
的值。- 由于
p
初始化为&a[0]
,所以*p
总是合法的。
结论:正确引用。
C. a[10]
a[10]
是对数组索引为10
的元素的访问。- 数组
a
的有效索引范围是0
到9
,索引10
超出范围,会导致数组越界访问。
结论:不正确引用。
D. *(&a[i])
&a[i]
是取数组中第i
个元素的地址。*(&a[i])
对这个地址解引用,获取第i
个元素的值。- 只要
i
在0
到9
的范围内,这个引用是合法的。
结论:正确引用。
正确答案:
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);
}
-
静态局部变量
w
:- 静态变量
w
在函数调用之间保持值不变。 - 初始值为
0
,在第一次调用后被更新。
- 静态变量
-
操作分析:
*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;
}
-
静态局部变量
x
:- 在
main
中,静态局部变量x
的初始值为0
。 p(&x, y)
调用时,传入了该静态变量的地址。- 注意:
p
函数不会直接修改静态变量x
的值。
- 在
-
调用
p(&x, y)
的行为:x
是静态局部变量,初始值为0
,通过*y
解引用操作被读取,但未被修改。- 输出计算:
x++
(局部变量,与静态局部变量x
无关)使局部x
的值为2
。*--y
指向静态局部变量x
的值,仍为0
。w = x + *--y
,结果为2 + 0 = 2
。
-
返回到主函数后的输出:
- 静态局部变量
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);
}
-
循环初始条件:
i = 0
,p = alpha[0]
(指向"ABCD"
)。
-
循环体执行:
*p
解引用,获取当前字符串的第一个字符。printf("%c", *p)
输出该字符。
-
循环更新条件:
p = alpha[++i]
更新p
指向下一个字符串的首地址。
-
循环结束条件:
- 当
i < 4
不满足时,退出循环。
- 当
循环的详细执行过程
迭代次数 | 变量 i 的值 | 指针 p 指向 | *p 的值 | 输出 |
---|---|---|---|---|
第一次 | i = 0 | alpha[0] | 'A' | A |
第二次 | i = 1 | alpha[1] | 'E' | E |
第三次 | i = 2 | alpha[2] | 'I' | I |
第四次 | i = 3 | alpha[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
这个程序有以下问题需要注意:
分析程序:
- 定义了一个数组
a
并初始化为{1, 2, 3, ..., 12}
。 - 指针
p
被初始化为a + 5
,即指向数组第6个元素(值为6)。 - 指针
q
被初始化为NULL
,这是一个空指针,不能被解引用。- 语句
*q = *(p + 5)
试图通过q
解引用赋值,这会导致运行时错误,因为q
指向无效的内存地址(NULL
指针)。
- 语句
- 代码中的
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
当前指向的位置。 - 然后,
s1
和s2
分别向后移动一位(自增操作)。
- 这表示将指针
while
循环:- 条件是
*s1
的值(即s1
当前指向的字符)。 - 当
*s1
为'\0'
(字符串结束符)时,赋值操作完成,同时条件为假,退出循环。
- 条件是
功能描述
- 该函数从
s1
开始逐个字符复制到s2
,包括字符串的结束符'\0'
,从而完成字符串的复制。 - 它不会改变
s1
和s2
的内容顺序。 - 这就是 字符串复制 的功能。
各选项分析
-
A. 串反向:
函数没有执行任何字符顺序的反转操作,不符合描述。 -
B. 串复制:
函数将s1
的内容逐字符复制到s2
,功能正是字符串复制,符合描述。 -
C. 求串长:
函数没有统计字符串长度的操作,不符合描述。 -
D. 串比较:
函数没有比较s1
和s2
的内容,不符合描述。
正确答案:B. 串复制
———————————————————————————————————————————
9.对于下面的程序段,叙述正确的是______
char s[] = “china”, *p = s;
A.*p与s[0]相等
B.数组s中的内容和指针变量p中的内容相等
C.s和p完全相同
D.数组s的长度和p所指向的字符串长度相等
分析题目所提供的代码以及选项,正确答案为:
A. *p与s[0]相等
原因分析:
-
选项 A:
p
是一个指针,初始化为指向数组s
的首地址(char *p = s
等价于char *p = &s[0]
)。*p
是p
所指向地址处的值,即s[0]
。- 因此,
*p == s[0]
是正确的。
-
选项 B:
- 数组
s
的内容是字符串"china"
,而p
是一个指针变量,存储的是数组s
的地址。 - 数组
s
的内容和指针变量p
的内容本质不同,二者不相等。
- 数组
-
选项 C:
s
是数组名,表示数组的起始地址(指针常量),而p
是一个普通指针变量。- 尽管
p
的初始值与s
的地址相同,但它们的语义不同:s
不能被修改,而p
是可以改变的。 - 因此,
s
和p
不完全相同。
-
选项 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
逐个分析各个选项:
-
选项 A:
*(s + 3)
s
是一个char[2][3]
类型的二维数组。s + 3
表示移动数组s
的指针 3 个元素,它的类型是char (*)[3]
,所以*(s + 3)
会访问s
数组中越界的位置(实际上访问的是s[3]
,这超出了s
数组的范围)。- 结果:会导致越界访问,语法合法但行为未定义,因此 A 错误。
-
选项 B:
*++p + 2
p
是char*
类型的指针,指向s[0][0]
。++p
将p
移动到s[0][1]
(即字符'b'
)。*++p
解引用p
,此时得到的是字符'b'
。- 然后
*++p + 2
表示'b' + 2
,即'd'
。但是由于p
是char*
类型,*++p
返回的是字符,+2 会增加两个字符的偏移量,从'b'
开始,经过两个字符后得到s[1][1]
,即'd'
。 - 结果:值与
s[1][1]
相等,因此 B 正确。
-
选项 C:
*s + 2
s
是char[2][3]
类型,*s
表示s[0]
,即char[3]
类型的数组,*s
是{'a', 'b', '\0'}
。*s + 2
是将*s
解引用后加 2,等价于访问s[0][2]
,即'\0'
(字符串结束符)。- 结果:与
s[1][1]
不相等,因此 C 错误。
-
选项 D:
p[1][1]
p
是char*
类型,指向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
。