C语言总结之预处理&关键字

C语言面试总结之预处理&关键字

预处理

C语言编译过程分为:预处理、编译、汇编、链接

常用的预处理命令如下表所示。编译预处理:头文件包含、宏替换、条件编译、去除注释、添加行号。
在这里插入图片描述
其中宏定义是最为常用的。
**优点:**代码复用性;提高性能
**缺点:**不可调试(预编译阶段进行了替换);无类型安全检查;可读性差,容易出错。

宏定义的应用举例:
(1)写一个“标准”宏MAX,这个宏输入两个参数并返回较大的一个。


#define MAX(A,B)  ((A) >= (B) ? (A) : (B))
//指的注意的是在宏中要小心地把参数用括号括起来,并且整个宏也要用括号括起来,防止替换时出现错误。

将*p++代入宏体,指针p会做两次自增操作

#include <stdio.h>
#define MAX(A,B)  ((A) >= (B) ? (A) : (B))

int main()
{
	int A[5] = {1,2,3,4,5}, B =  0, *p;
	p = A;
	printf("%d\n", *p);     			//赋值时 *p 为1
    printf("%d\n", MAX(*p++ , B));      //计算时 *p 为2
	printf("%d\n", *p);					//计算后 *p 为3
    return 0;
}

(2)已知数组table,用宏求数组元素个数。

#define  COUNT(table)  (sizeof(table) / sizeof(table[0]))  
//sizeof是C语言的32个关键字之一,并非“函数”也叫长度(求字节)运算符。
//sizeof是一种单目运算符,以字节为单位返回某操作数的大小,用来求某一类型变量的长度。运算对象可以是任何数据类型或变量。

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

#define  SECONDS_PER_YEAR  (60 * 60 * 24 * 365)UL 
//注意预处理器将为你计算常数表达式的值,并且整个宏体要用括号括起来。
//注意这个表达式将使一个16位机的整型数溢出,因此要用到无符号长整型符号UL,告诉编译器这个常数是的无符号长整型数。

带参宏和函数的区别?
(1)带参宏只是在编译预处理阶段进行简单的字符替换;而函数则是在运行时进行调用和返回。
(2)宏替换不占运行时间,只占编译时间;而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。
(3)带参宏在处理时不分配内存;而函数调用会分配临时内存。
(4)宏不存在类型问题,宏名无类型,它的参数也是无类型的;而函数中的实参和形参都要定义类型,二者的类型要求一致。
(5)而使用宏定义次数多时,宏替换后源程序会变长;而函数调用不使源程序变长。

关键字

C语言常用的32个关键字,用途及说明。
在这里插入图片描述
在这里插入图片描述
以下几种常常被考察
存储类:auto、const、extern、register、static
数据类型:volatile、typedef
运算符:sizeof

关键字auto的作用
用来定义自动局部变量,自动局部变量在进入声明该变量的语句块时被建立,退出语句块时被注销,仅在语句块内部使用。普通局部变量就是自动局部变量,只是省略了auto这一关键字。

关键字static的作用
(1)static修饰局部变量时:①改变了其存储位置,存储在静态区;②同时改变了其生命周期,为整个源程序,因此它只被初始化一次,若没显式初始化则自动初始化为0。
(2)static修饰全局变量时:改变了其作用域,只可以被文件内所用函数访问。
(3)static修饰函数时:改变了其作用域,只可被这一文件内的其它函数调用。

关键字extern的作用
用于跨文件引用全局变量,即在本文件中引用一个已经在其他文件中定义的全局变量。extern在链接阶段。
注意引用时不能初始化,如extern a,而不能是extern a = 0。另外,函数默认是extern类型的,表明是整个程序(工程)可见的,加不加都一样。

关键字register的作用
编译器会将register修饰的变量尽可能地放在CPU的寄存器中,以加快其存取速度,一般用于频繁使用的变量。注意的是register变量可能不存放在内存中,所以不能用&来获取该变量的地址;只有局部变量和形参可以作为register变量;寄存器数量有限,不能定义过多register变量。

关键字const的作用
定义只读变量的关键字,常类型的变量或对象的值是不能被改变或更新的。虽然和预编译指令一样都可用来定义常数。但const有数据类型,编译器可以做静态类型检查;而宏定义没有类型,可能会导致类型出错。

1const int a;           // a是一个整形常量  
   int const a;           // a是一个整形常量  2const int *a;          // a是一个指向整型常量的指针变量  
   int * const a;         // a是一个指向整型变量的指针常量  
   int const * const a = &b;  // a是一个指向整型常量的指针常量  3char *strcpy(char *strDest, const char *strSrc);  // 参数在函数内部不会被修改  
    const int strcmp(char *source, char *dest);     // 函数的返回值不能被修改 

const 推出的初始目的,正是为了取代预编译指令,如:#define。消除它的缺点,同时继承它的优点。

关键字volatile的作用
用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

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

例如这段代码,函数的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,*ptr的值可能被意想不到地该变,return *ptr * *ptr; 中的 *ptr 可能代表不同的值。所以这段代码可能返不是你所期望的平方值!

作用:告诉编译器不要去假设(优化)这个变量的值,因为这个变量可能会被意想不到地改变。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
例如:
①并行设备的硬件寄存器(如:状态寄存器)。
②一个中断服务子程序中会访问到的非自动变量。
③多线程应用中被几个线程共享的变量(防止死锁)。
看着难以理解,该链接有详解。

https://www.runoob.com/w3cnote/c-volatile-keyword.html

一个参数既可以是const还可以是volatile,例如只读的状态寄存器,它是volatile因为它可能被意想不到地改变,它是const因为程序不应该试图去修改它。
一个指针可以是volatile,例如当一个中服务子程序修改一个指向一个缓冲区的指针时。

关键字typedef的作用
在C语言中我们一般都是声明一个已经存在的数据类型的同义字。
例如

typedef unsigned char UC;
typedef unsigned int  U8;
typedef int  U8;

也可以用预处理器做类似的事。

#define  dPS  struct s *   
typedef  struct s *  tPS;  //(顺序、分号、#号)  
dPS  p1, p2;   
tPS  p3, p4;   

以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构体s的指针。
define是字符串的替换,因此dPS p1, p2;其实写成struct s * p1, p2; 即定义p1为一个指向结构体的指针,p2为一个实际的结构体,这也许不是你想要的。tPS p3, p4; 正确地定义了p3 和p4 两个指针。

关键字sizeof的作用

sizeof关键字用来计算变量、数据类型所占内存的字节数。sizeof(数组名)得到数组所占字节数。sizeof(字符串指针名)得到指针所占字节数。此外strlen()函数则用来测试字符串所占字节数,不包括结束字符 ’\0’。strlen(字符数组名)得到字符串所占字节数,strlen(字符串指针名)得到字符串所占字节数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值