PTA | 指针相关题目及解析2

1-1

设有下列语句,则( )是对a数组元素的不正确引用,其中0≤i<10,int a[10]={0,1,2,3,4,5,6,7,8,9}, * p=a;

A.p[i]

B.p=a[2]

C.*(p+3)

D.*(a+i)

ChatGPT

在这个问题中,我们需要识别哪个选项是对数组 a 的元素进行了不正确的引用。首先,让我们逐一分析每个选项:

A. p[i] - 这是正确的。因为 p 是指向 a 的指针,p[i] 相当于 a[i]

B. p = a[2] - 这是不正确的。这里尝试将 p 赋值为 a[2] 的值,而不是 a[2] 的地址。p 应该指向数组中的一个地址,而 a[2] 是一个 int 值。

C. *(p+3) - 这是正确的。p+3 是指向 a[3] 的指针,*(p+3) 是访问该位置的值,即 a[3]

D. *(a+i) - 这也是正确的。这是数组 a 的另一种标准引用方式,等价于 a[i]

因此,选项(B)是对 a 数组元素的不正确引用。

1-2

若有说明:int n=2,*p=&n,*q=p,则以下非法的赋值语句是: ( )。

  1. p=q

  2. *p=*q

  3. n=*q

  4. p=n

ChatGPT

在这个问题中,我们需要找出哪个赋值语句是非法的。首先,让我们分析每个选项:

A. p=q - 这是合法的。因为 pq 都是指向 int 类型的指针,所以可以把 q 的值(即指向的地址)赋给 p

B. *p=*q - 这也是合法的。*p*q 都解引用为 n 的值,这个语句相当于将 n 的值赋给自己。

C. n=*q - 这是合法的。*q 解引用为 n 的值,这个语句是将 n 的值赋给 n 自己。

D. p=n - 这是非法的。这里尝试将 n(一个整数值)赋给 p(一个指向整数的指针)。整数值不能直接赋给指针,因为它们是不兼容的类型。

因此,选项(D)是非法的赋值语句。

1-3

若有语句int a[3][4];,则与*(a+1)+2等价的是 ( )。

  1. a[1][2]

  2. *a+3

  3. &a[1][2]

  4. &a[1]+2

ChatGPT

为了理解这个问题,我们需要分析表达式 *(a+1)+2 并找出与之等价的表达式。a 是一个 3x4 的二维数组,int a[3][4]

  • *(a+1):
    • a 是一个指向数组首元素的指针,在这里,数组的首元素是一个含有4个整数的一维数组。因此,a 可以被视作指向 int[4] 类型的指针。
    • a+1 移动到下一个 int[4] 数组,即 a 数组的第二行。
    • *(a+1) 解引用这个指针,得到第二行的首地址。
  • *(a+1)+2:
    • 加2意味着从第二行的首地址向前移动2个 int 元素。
    • 因此,这个表达式指的是第二行(即 a[1])中的第三个元素(即 [2])。

所以,*(a+1)+2 等价于访问 a[1][2]。这是因为二维数组在内存中是连续存放的,且 a[1][2] 正是第二行第三个元素的位置。

因此,答案是:

A. a[1][2]

1-4

若有定义int (*p)[4]; 则标识符p( )

  1. 是一个指向整型变量的指针

  2. 是一个指针数组名

  3. 是一个指针,它指向一个含有四个整型元素的一维数组

  4. 定义不合法

ChatGPT

在这个问题中,我们要分析 int (*p)[4]; 这个声明的含义。让我们来逐一解读每个选项:

  • A. 是一个指向整型变量的指针
    • 这不正确。虽然 p 是一个指针,但它不是指向单个整型变量的。而是指向一个整型数组的。
  • B. 是一个指针数组名
    • 这也不正确。p 不是一个指针数组,而是单个指针。指针数组会有形式如 int* arr[10];,表明 arr 是一个包含10个指向 int 的指针的数组。
  • C. 是一个指针,它指向一个含有四个整型元素的一维数组
    • 这是正确的。int (*p)[4]; 表示 p 是一个指针,指向一个具有四个整数的数组。这里的括号非常重要,它指出 p 是指向一个整个数组的指针,而不是指向一个单独整数的指针。
  • D. 定义不合法
    • 这不正确。int (*p)[4]; 是完全合法且有意义的声明。

因此,正确答案是 C. 是一个指针,它指向一个含有四个整型元素的一维数组。

1-5

若有定义int n=0,*p=&n,**q=&p;则以下选项中,正确的赋值语句是( )。

  1. p=1;

  2. *q=2;

  3. q=p;

  4. *p=5;

ChatGPT

为了找出正确的赋值语句,我们首先需要理解变量 npq 的定义。

  • n 是一个整型变量,初始化为 0
  • p 是一个指向整型的指针,初始化为指向 n
  • q 是一个指向指针的指针(双重指针),初始化为指向 p

现在,让我们逐个分析每个选项:

A. p=1; - 这是不正确的。p 是一个指针,不能直接赋值为整数。应该赋值为指向整型的地址。

B. *q=2; - 这也是不正确的。q 是指向 p 的指针,所以 *qpp 作为一个指针不能直接赋值为整数 2。如果是 **q = 2; 则是正确的,因为它相当于 *p = 2;,即改变 n 的值。

C. q=p; - 这也是不正确的。q 是一个指向指针的指针,而 p 是一个指向整数的指针。二者类型不匹配,不能直接赋值。

D. *p=5; - 这是正确的。*p 是解引用 p,也就是 n 的别名。这个语句将 5 赋值给 n

因此,正确的赋值语句是 D. *p=5;

1-6

设有如下定义char * aa[2]={"abcd", "ABCD"};, 则以下说法中正确的是()。

  1. aa数组的元素的值分别是字符串" abcd"和"ABCD"的内容

  2. aa是指针变量, 它指向含有两个数组元素的字符型一维数组

  3. aa数组的两个元素分别存放的是字符串的首地址

  4. aa数组的两个元素中各自存放了字符"a"和"A"

ChatGPT

为了理解这个声明 char * aa[2] = {"abcd", "ABCD"};,我们需要分析每个选项:

A. aa数组的元素的值分别是字符串 "abcd" 和 "ABCD" 的内容 - 这是不正确的。aa 数组的元素实际上是指针,它们指向字符串常量 "abcd" 和 "ABCD" 的首地址,而不是直接存储这些字符串的内容。

B. aa是指针变量,它指向含有两个数组元素的字符型一维数组 - 这也是不正确的。aa 不是单个指针变量,而是一个数组,它包含了两个指针。每个指针分别指向一个字符数组(字符串)。

C. aa数组的两个元素分别存放的是字符串的首地址 - 这是正确的。aa 是一个指针数组,其两个元素 aa[0]aa[1] 分别存储了两个字符串 "abcd" 和 "ABCD" 的首地址。

D. aa数组的两个元素中各自存放了字符 "a" 和 "A" - 这是不正确的。如前所述,aa 数组存放的是指向字符串的指针,而不是单个字符。

因此,正确的答案是 C. aa数组的两个元素分别存放的是字符串的首地址。

1-7

若有下面的程序片段, 则以下选项中对数组元素的错误引用的是()。

int a[12] ={0}, * p[3], ** pp, i;
for(i =0; i<3; i++) p[i] = &a[i*4 ]; pp =p;

  1. pp[0][1]

  2. a[10]

  3. p[3][1]

  4. ((p+2)+2)

ChatGPT

为了找出对数组元素的错误引用,我们需要先理解这段代码的含义和各个变量的作用。

  1. int a[12] ={0}: 这是一个大小为12的整型数组,所有元素初始化为0。
  2. *p[3]: 这是一个大小为3的指针数组,每个元素是指向 int 的指针。
  3. **pp: 这是一个指向指针的指针。
  4. for(i = 0; i < 3; i++) p[i] = &a[i*4];: 这个循环将 p 数组的每个元素指向 a 数组中每4个元素的起始地址。即 p[0] 指向 a[0]p[1] 指向 a[4]p[2] 指向 a[8]
  5. pp = p;: 这将 pp 指向 p 数组的首地址。

现在,让我们检查每个选项:

A. pp[0][1] - 这是合法的。pp[0] 相当于 p[0],指向 a[0]pp[0][1] 相当于 a[1]

B. a[10] - 这是合法的。a 是一个有12个元素的数组,a[10] 是第11个元素。

C. p[3][1] - 这是非法的。p 是一个有3个元素的指针数组,有效索引范围是0到2。p[3] 超出了其界限,是未定义的。

D. ((p+2)+2) - 这是不合法的。首先,p+2 是合法的,指向 p[2]。但 ((p+2)+2) 试图再次偏移这个指针,结果是一个指向 p 数组之外的位置,这在这个上下文中没有意义。

因此,正确答案是 C. p[3][1]

1-8

下列程序段的输出是____。

int c[]={1, 3, 5};
int *k=c+1;
printf("%d", *++k);

  1. 3

  2. 4

  3. 5

  4. 6

1-9

对于如下说明,语法和语义都正确的赋值是_____。

int c, *s, a[]={1, 3, 5};

  1. c=*s;

  2. s[0]=a[0];

  3. s=&a[1];

  4. c=a;

ChatGPT

为了确定哪个赋值语句是在给定的上下文中既语法上正确又语义上正确,我们需要分析每个选项:

  1. c=*s;
    • 这个语句在语法上是正确的,但在语义上可能不正确。因为 s 是一个指针,但在这个代码片段中,我们没有看到 s 被初始化或指向某个具体的地址。如果 s 没有初始化,那么尝试解引用它 (*s) 将导致未定义的行为。
  2. s[0]=a[0];
    • 这个语句在语法上是正确的,但在语义上同样可能不正确。这里假设 s 是指向某个整数的指针,因此 s[0] 是合法的。但是,因为 s 没有被初始化指向一个具体的地址,这可能导致错误。
  3. s=&a[1];
    • 这是语法和语义上都正确的。这里 s 被赋值为指向数组 a 的第二个元素的地址。在 C 语言中,数组元素的地址可以赋给指针,所以这个语句是有效的。
  4. c=a;
    • 这在语法上是错误的。a 是一个数组名,它在大多数上下文中表示数组的首地址,而 c 是一个整数。不能将数组地址赋值给一个整数变量。

综上所述,正确的选项是 C. s=&a[1];。这是唯一一个在给定上下文中既语法上正确又语义上正确的赋值语句。

1-10

函数调用程序如下:

void f(int a[], int n)
{
    int i;
    for(i=0;i<n;i++)
        a[i]=i;
}

int main( )
{
    int a[100],n=30;
    f(a,n);
    ...
}

相关说法错误的是:

  1. f函数头改为int f(int *a, int n),无需其它改变,效果完全一样。

  2. main函数中增加 int *p=a;f(a,n);改为f(p,n);,效果完全一样。

  3. 用数组名作f()的参数,则main()中的数组与f()中的数组是同一数组。

  4. 用数组名作f()的参数,实现了实参与形参双向的值传递。

ChatGPT

为了评估这些关于函数调用的说法,我们需要仔细分析每一个选项:

A. f函数头改为int f(int *a, int n),无需其它改变,效果完全一样。 - 这是正确的。在 C 语言中,当数组作为参数传递给函数时,它实际上是以指针的形式传递的。因此,不管是使用 int a[] 还是 int *a,效果是一样的,因为在这个上下文中,它们都是指向数组首元素的指针。

B. main函数中增加 int *p=a; 将 f(a,n); 改为 f(p,n);,效果完全一样。 - 这也是正确的。由于 p 是指向 a 的首元素的指针,传递 pf 函数与直接传递数组 a 是等效的。

C. 用数组名作 f() 的参数,则 main() 中的数组与 f() 中的数组是同一数组。 - 这是正确的。在 C 语言中,数组参数是通过指针传递的,所以在 f 函数中对数组的修改会反映在 main 函数中的同一个数组上。

D. 用数组名作 f() 的参数,实现了实参与形参双向的值传递。 - 这是错误的。虽然数组的修改在函数间是可见的,这是由于数组名作为参数传递的是地址,而不是值的副本。因此,这不是双向的值传递。实际上,这是通过引用(或地址)传递实现的,其中形参指向实参的同一内存地址。

因此,错误的说法是 D. 用数组名作 f() 的参数,实现了实参与形参双向的值传递。

2-1

在主函数中输入N个字符串,调用func()函数用选择排序算法对他们进行升序排序,然后输出已经排好序的字符串。 要求使用指针数组进行处理。

#include <stdio.h>
#include <string.h>
#define N 5
void func(char **str);

int main(void)
{
    int i;
    char s[N][1024], *pStr[N];
    for (i=0; i<N; i++)
    {
        gets(s[i]);
        pStr[i]=s[i];
    }
    func(pStr);
    //输出排序后的结果
    for (i=0; i<N; i++)
        puts(pStr[i]);
    return 0;
}

void func(char **str)
{   
    char *temp;
    int i,j,min;
    for(i=0;i<N-1;i++)
    {   
        min=i;
        for(j=i+1;j<N;j++)
        if(strcmp(str[min],str[j])>0)  
            min=j;
        if(min!=i)
        { 
            temp=str[min];
            str[min]=str[i];
            str[i]=temp;
        }
    }
}

2-2 求定积分通用函数

下述程序用于求解上述定积分。 【问题分析】 根据数学知识,求定积分的问题实际上就是求函数f(x)当x在区间[a,b]时由x=a、x=b、y=0和y=f(x)围成的曲边四边形的面积。使用计算机解决这类问题的常用方法有矩形法、梯形法、辛普生法等。以梯形法为例,求定积分有如下几个步骤,如图1所示。

    1. 将区间[a,b]划分为若干等分,等分数取决于要求的精度;
    1. 计算出所有等分点的函数值f(xi);
    1. 连接相邻两个等分点的函数值,将所求曲边四边形区域用若干个小的梯形代替。此时相邻两等分点函数值之间的曲线和连接的直线之间的区域即为误差;
    1. 按平面几何公式求出所有小梯形的面积,然后求和得到曲边四边形面积的近似值。当积分区间的等分数趋近于无穷等分时,梯形面积之和无限趋近于真实的积分值。注意用计算机求解时只能求出满足精度要求的近似值。

请将下述程序补充完整,使其可以正常运行。 提示:integrate()的第1个参数应为函数指针,代表了被积分的函数;a,b分别为积分下界及上界,N为细分数。

#include <stdio.h>

double f1(double x){
    return 1+x;
}

double f2(double x){
    return 1.0/(1.0+4*x*x);

}

double integrate(double (*f)(double x), double a, double b, int N){
    double h = (b-a)/N;
    double area = 0.0;
    for (int i=0;i<N;i++)
        area += ((f(a+i*h)+f(a+(i+1)*h))*h/2.0);
    return area;
}

int main()
{
    printf("integrate(f1,0,2,100)=%.2f\n",integrate(f1,0,2,100));
    printf("integrate(f2,-1,1,100)=%.2f\n",integrate(f2,-1,1,100));
    return 0;
}

【提示】 程序的执行结果应为:

integrate(f1,0,2,100)=4.00
integrate(f2,-1,1,100)=1.11

PTA | 指针相关题目及解析2
https://acm.nanyan.cc/posts/60fc.html
作者
nanyan
发布于
2023年12月18日
许可协议