C语言进阶⑫(指针下)(指针和数组笔试题解析)(杨氏矩阵)(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: C语言进阶⑫(指针下)(指针和数组笔试题解析)(杨氏矩阵)

C语言进阶⑫(指针下)(指针和数组笔试题解析)(杨氏矩阵)(中):https://developer.aliyun.com/article/1513063

3笔试题二

3.1指向函数指针数组的指针

声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,

参数是int*,正确的是( )

A.(int *p[10])(int*)

B.int [10]*p(int *)

C.int (*(*p)[10])(int *)

D.int ((int *)[10])*p

解析:

A选项,第一个括号里是一个完整定义,第二个括号里是个类型,四不像。BD选项,[]只能在标识符右边,双双排除。只有C是能编过的。


3.2函数指针

定义一个函数指针,指向的函数有两个int形参并且返回一个函数指针,返回的指针指向一个有一个

int形参且返回int的函数。下面哪个是正确的?( )

A.int (*(*F)(int, int))(int)

B.int (*F)(int, int)

C.int (*(*F)(int, int))

D.*(*F)(int, int)(int)

解析:

D类型不完整先排除,然后看返回值,B的返回值是int,C的返回值是int *,故选A。判断返回值类型只需要删掉函数名/函数指针和参数列表再看就行了。int (*(*F)(int, int))(int)删掉(*F)(int, int)后剩下int (*)(int),符合题意。

3.3函数指针

设有以下函数void fun(int n,char *s){……},则下面对函数指针的定义和赋值均是正确的是:( )

A.void (*pf)(int,char); pf=&fun;

B.void (*pf)(int n,char *s); pf=fun;

C.void *pf(); *pf=fun;

D.void *pf(); pf=fun;

解析:

CD前半句压根就不是定义而是声明,A选项参数列表的第二个参数错了。应为char *,B选项正确。需要说明的是,对于函数名来说,前面的&和*都会被忽略,所以fun前面加不加取地址都没区别。只有定义出的函数指针变量(如题面中的pf)加上&后才会变成二级函数指针。

3.4指针+-

 
#include <stdio.h>
int main()
{
    int aa[2][5] = { 10,9,8,7,6,5,4,3,2,1 };
    int* ptr1 = (int*)(&aa + 1);
    int* ptr2 = (int*)(*(aa + 1));
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
}

A.1, 6

B.10, 5

C.10, 1

D.1, 5

解析:

&aa的类型是int (*)[2][5],加一操作会导致跳转一个int [2][5]的长度,直接跑到刚好越界的位置。减一以后回到最后一个位置1处。*(aa + 1)相当于aa[1],也就是第二行的首地址,自然是5的位置。减一以后由于多维数组空间的连续性,会回到上一行末尾的6处。故选A。


3.5数组指针

 
#include <stdio.h>
int main()
{
    int a[5] = { 5, 4, 3, 2, 1 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

A.5, 1

B.4, 1

C.4, 2

D.5, 2

解析:

*(a + 1)等同于a[1],第一个是4,a的类型是int [5],&a的类型就是int(*)[5],是个数组指针。所以给int(*)[5]类型加一,相当于加了一个int [5]的长度。也就是这个指针直接跳过了a全部的元素,直接指在了刚好越界的位置上,然后转换成了int *后再减一,相当于从那个位置向前走了一个int,从刚好越觉得位置回到了1的地址处,所以第二个是1,故选B。

3.6函数参数设计

下面代码中print_arr函数参数设计哪个是正确的?( )

 
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr(arr, 3, 5);

A.void print_arr(int arr[ ][ ],int row, int col);

B.void print_arr(int* arr, int row, int col);

C.void print_arr(int (*arr)[5], int row, int col);

D.void print_arr(int (*arr)[3], int row, int col);

解析:

二维数组相当于数组的数组,传到子函数变成数组的指针。int arr[3][5]相当于是3个元素的arr,每个元素是int [5],所以int [5]是类型说明不能省略。丢失的信息只有数组的元素个数,也就是3。A丢了类型中的5,B选项arr是首元素地址,首元素是第一行数组,指针层次错了。D选项5写成了3,

3.7函数设计

下面test函数设计正确的是:( )

 
char* arr[5] = {"hello", "bit"};
test(arr);

A.void test(char* arr);

B.void test(char** arr);

C.void test(char arr[5]);

D.void test(char* arr[5]);

解析:

指针的数组传递给子函数变为指针的指针,也就是二级指针。但是允许中括号写法,

写成char **arr、char *arr[]、char * arr[5]都可。所以BD正确。


3.8(编程)杨氏矩阵

杨氏矩阵


有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,


请编写程序在这样的矩阵中查找某个数字是否存在。


要求:时间复杂度小于O(N);


解题:


如杨氏矩阵:


1 2 3


4 5 6


7 8 9


再如:


1 2 3


2 3 4


3 4 5


查找某个元素——若遍历二维数组,若数组有n个元素,


则最坏情况下找n次,即时间复杂度为O(N),(时间复杂度是数据结构的内容,现在不懂可以略过)


而题目要求时间复杂度小于n。时间复杂度小于O(N)的算法:

 
#include<stdio.h>
int find_arr(int arr[3][3], int row, int col, int k)
{
    int x = 0, y = col - 1;//从右上角开始找
    while (x < row && y >= 0)
    {
        if (arr[x][y] > k)
        {
            y--;
        }
        else if (arr[x][y] < k)
        {
            x++;
        }
        else
        {
            printf("%d %d\n", x, y);
            return 1;
        }
    }
    return 0;
}
 
int main()
{
    int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
    //  1   2   3
    //  4   5   6
    //  7   8   9
    //若要找的元素是k=2,比右上角的3小,因为3是一列中最小的元素,所以去掉一列;
    //    若要找的元素是k=7,比右上角的3大,因为3是一行中最大的元素,
    //    所以去掉一行;7比在剩下的元素中右上角的6还要大,则又去掉一行;
    //    7与剩下元素中右上角的9小,则在这一行中查找7,
    //    (因为9已经是剩下元素所在列中最小的元素)去掉一列;
    //    8比7大说明还在8的左边,去掉一列;7与7相等则找到;
    //    如果7还找不到则结果就是找不到。
 
    int k = 0;
    scanf("%d", &k);
        //找到返回1 找不到返回0
    //传arr和行、列、要找的元素
    if (find_arr(arr, 3, 3, k))
    {
        printf("找到了\n");
        
    }
    else
    {
        printf("找不到\n");
    }
    return 0;
}

虽然封装了函数,但是找到的下标是自己打印的,这不是好的解决办法。

解决办法:用函数查找,用函数带回或返回找不到。

而且return不能直接带回两个值。

更好的代码实现:

 
#include<stdio.h>
int find_arr(int arr[3][3], int* row, int* col, int k)
{
    int x = 0, y = *col - 1;//从右上角开始找
    while (x < *row && y >= 0)
    {
        if (arr[x][y] > k)
        {
            y--;
        }
        else if (arr[x][y] < k)
        {
            x++;
        }
        else
        {
            *row = x;
            *col = y;
            return 1;
        }
    }
    return 0;
}
int main()
{
    int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
    int k = 0, x = 3, y = 3;
    scanf("%d", &k);
    //找到返回1 找不到返回0
    //传arr和行、列、要找的元素
    //传行和列的地址,则在函数中所求的下标就可以通过这两个地址放到x、y中
    if (find_arr(arr, &x, &y, k))
    {
        printf("找到了,下标是%d,%d\n",x,y);
        
    }
    else
    {
        printf("找不到\n");
    }
    return 0;
}
//这里传& x和& y的好处:
//既可以带进函数两个值:3和3,在函数中使用;
//又可以在函数结束的时候带回值——这种设计是返回型参数(输出型参数),
//用指针修改,把数值带回去。

3.9(编程)模拟实现qsort

自己敲出一个模拟的qsort并应用

目录
相关文章
|
27天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
25天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
15 2
|
26天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
26天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
25天前
|
编译器 C语言
【c语言】指针就该这么学(2)
本文详细介绍了指针与数组的关系,包括指针访问数组、一维数组传参、二级指针、指针数组和数组指针等内容。通过具体代码示例,解释了数组名作为首元素地址的用法,以及如何使用指针数组模拟二维数组和传递二维数组。文章还强调了数组指针与指针数组的区别,并通过调试窗口展示了不同类型指针的差异。最后,总结了指针在数组操作中的重要性和应用场景。
17 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3
|
8天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
25 6
|
28天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
35 10
|
22天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
27天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
54 7

推荐镜像

更多