【图解八股-C/C++①-C语言篇】

作者简介和专栏内容见专栏介绍:https://www.nowcoder.com/creation/manager/columnDetail/0eL5bM

麻烦看到贴子的伙伴点点赞大家点赞订阅支持下,提前祝各位offer多多,有问题评论区见~~

图解版

C语言

预处理&关键字

写一个max宏

一个简单的max宏可能定义为:

#define MAX (x,y) ((x) > (y) ? (x) : (y))

这个宏看起来很简单,但是如果我们用它来比较两个表达式,比如:

int a = 1; int b = 2; int c = MAX (a++, b++);

我们期望得到的结果是c = 2,但实际上得到的结果是c = 3。这是因为宏会对参数进行文本替换,导致a++和b++被计算了两次。展开后的代码如下:

int c = ((a++) > (b++) ? (a++) : (b++));

为了避免这种问题,我们可以使用一些技巧来改进宏的定义,比如:

  • 使用括号来避免运算符优先级的问题。
  • 使用typeof或decltype来避免类型不匹配的问题。
  • 使用复合语句或内联函数来避免重复计算的问题。

例如,一个改进后的max宏可能定义为:

#define MAX (x,y) ({typeof (x) _x = (x);typeof (y) _y = (y);_x > _y ? _x : _y;})

这个宏可以避免上述问题,但是仍然有一些局限性,比如:

  • 这个宏依赖于GCC扩展的语法,不是标准C语言的一部分。
  • 这个宏不能处理浮点数或其他特殊类型的比较。
  • 这个宏不能作为函数参数或返回值使用。

因此,一个更好的选择是使用函数而不是宏来实现max操作。函数可以提供类型安全、作用域控制、调试支持等优点。而且如果函数足够简单,编译器可以自动将其内联化,从而消除函数调用的开销。例如,一个简单的max函数可能定义为:

inline int max (int x, int y) { return x > y ? x : y; }

举例一个使用二级指针的例子

// 使用二级指针作为函数参数,交换两个一级指针所指向的数据 
void swap(int **x, int **y) { int *temp = *x; *x = *y; *y = temp; }

// 使用二级指针动态分配一个二维数组 
int **create_array(int rows, int cols) { 
    int *arr = malloc(rows * sizeof(int)); // 分配行数个一级指针 
    for (int i = 0; i < rows; i++) { 
        arr[i] = malloc(cols * sizeof(int)); 
    // 分配每行对应的列数个整型数据 
    } 
    return arr; 
}

int(*p)(void(*)(void*),int**)表示什么

  • p是一个指针
  • p指向一个函数
  • 这个函数有两个参数
  • 这个函数的返回类型是int

如何在C语言实现接口

如何用C语言实现读写寄存器变量?

答案:

#define rBANKCON0 (*(volatile unsigned long *)0x48000004) rBANKCON0 = 0x12;

解读:

(1)由于是寄存器地址,所以需要先将其强制类型转换为 ”volatile unsigned long *”。

(2)由于后续需要对寄存器直接赋值,所以需要解引用。

用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)。

答案:

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

解读:(1)注意预处理器将为你计算常数表达式的值,并且整个宏体要用括号括起来。

(2)注意这个表达式将使一个16位机的整型数溢出,因此要用到无符号长整型符号UL,告诉编译器这个常数是的无符号长整型数。

一个参数既可以是const还可以是volatile吗?一个指针可以是volatile吗?下面的函数有什么问题?

int square(volatile int *ptr)   
{   
    return *ptr * *ptr;   
}

(1)是的。一个例子是只读的状态寄存器,它是volatile因为它可能被意想不到地改变,它是const因为程序不应该试图去修改它。

(2)是的。一个例子是当一个中断服务子程序修改一个指向一个缓冲区的指针时。

(3)这个函数的目的是用来返回指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)   
{   
    int a, b;   
    a = *ptr;   
    b = *ptr;   
    return a * b;   
}

由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)   
{   
    int a;   
    a = *ptr;   
    return a * a;   
}  

关键字typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:

#define dPS struct s *

typedef struct s * tPS; //(顺序、分号、#号)

以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构体s的指针。哪种方法更好呢?为什么?

(1)typedef更好。

(2)举个例子:

dPS p1, p2;

tPS p3, p4;

第一行代码扩展为 struct s * p1, p2; 即定义p1为一个指向结构体的指针,p2为一个实际的结构体,这也许不是你想要的。第二行代码正确地定义了p3 和p4 两个指针。

下面代码能不能编译通过?

#define c 3 c++;

答案:不能。

解读:自增运算符++用于变量,3是常量

关键字extern的作用是什么?

volatile在编译阶段,extern在链接阶段。

答案:用于跨文件引用全局变量,即在本文件中引用一个已经在其他文件中定义的全局变量。

解读:

(1)注意引用时不能初始化,如extern var,而不能是extern var = 0。

(2)另外,函数默认是extern类型的,表明是整个程序(工程)可见的,加不加都一样。

extern”C”的作用?

答案:

(1)在C++代码中调用C函数,用法:extern “C”{C函数库头文件/函数声明}。

(2)在C代码中调用C++函数,用法:在C++的头文件中加extern“C”{头文件/函数声明}。

注意:extern”C”只能用于C++文件中。

关键字register的作用是什么?使用时需要注意什么?

(1)作用:编译器会将register修饰的变量尽可能地放在CPU的寄存器中,以加快其存取速度,一般用于频繁使用的变量。

(2)注意:register变量可能不存放在内存中,所以不能用&来获取该变量的地址;只有局部变量和形参可以作为register变量;寄存器数量有限,不能定义过多register变量。

数据类型

用变量a给出下面的定义

(5)一个有10个指针的数组,这10个指针是指向整型数的(指针数组): int *a[10]。(6)一个指向有10个整型数数组的指针(数组指针):int (*a)[10]。(7)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(函数指针):int (*a)(int)。(8)一个有10个指针的数组,这10个指针均指向函数,该函数有一个整型参数并返回一个整型数(函数指针数组): int (*a[10])(int)

下面代码有什

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

计算机实习秋招全阶段指南 文章被收录于专栏

作者简介:2个月时间逆袭嵌入式开发,拿下理想汽车-ssp、小米汽车-sp、oppo-sp、迈瑞医疗、三星电子等八家制造业大厂offer~ 专栏内容:涵盖算法、八股、项目、简历等前期准备的详细笔记和模板、面试前中后的各种注意事项以及后期谈薪、选offer等技巧。保姆级全阶段教程帮你获得信息差,早日收到理想offer~

全部评论

相关推荐

头像
不愿透露姓名的神秘牛友
05-06 15:54
上海丕锐科技有限公司 C++开发 10Kx13 本科其他
点赞 评论 收藏
转发
2 收藏 评论
分享
牛客网
牛客企业服务