C语言 typedef 枚举 宏定义 静态变量等



一、typedef

typedef 是对既有的类型另外再命名一个新的名字。新的名字与既有的名字都可使用。
这样做的目的主要是让代码的可阅读性更强些。

1. 利用typedef重命名

typedef char BYTE;
BYTE kb[1024];

char 通常理解为字符型。给人的概念就是保存一个字符。如果我们要表达 1K 的内
存空间,可以用 char kb[1024]表示,char 占一个字节,用它定义长度为 1024 的数组
就是一个 KB 的内存空间。但是从阅读感看,感觉是让其存放 1024 个字符。此时把 char
另起一个别名 BYTE 即字节,用这个名字命名变量或数组,代码的阅读感会更强。

2.用 typedef 简化指针相关类型的定义

基本数据类型的指针

typedef int* PINT;
PINT p1, p2;

上面代码中p1,p2都是int * 类型的变量。对比之前的int * q1, * q2;此写法就不用再额外多写 * 号了

数组指针与函数指针的定义,则需要把类型的描述过程用 typedef 封装起来

typedef double(*PFUN)(double);
typedef int(*P5INT)[5];

二、枚举类型:

1.什么是枚举类型?

枚举 enum 是 C 语言的一种扩展数据类型。主要用于某数据类型的数据值是有限的且有意义的少量可能的值。
例如血型可以是 A、B、O、AB。星期、星座、属相、方向等,这几个数据的值都是这样的特点。

2.枚举的基本用法

创建枚举类型

其格式为

enum 枚举名称 { 内容 1, 内容 2, 内容 3,... };
enum 枚举名称 { 内容 1=整数, 内容 2=整数, 内容 3=整数, ...};

比如

enum sex { nan,nv };

使用枚举类型的变量

其格式为: enum 枚举类型 = 预定的枚举值;

enum sex { nan,nv };
enum sex i = nan;

三、const 常量

const 可以翻译成“常量”,即不可被改变的量。通常用于修饰变量让其内容在初始化后都不可以改变它,
相当于只读。用它的目的是用语法的行为保护变量的值不被意外的修改

1. const 修饰全局变量与局部变量的差异

const double pi = 3.1415926;
int main()
{
const double e = 2.71828;
pi = 3.14;
e = 2.71;
return 0;
}

当我们试图修改变量 pi 和 e 的值时,在 vs2019 中就会报出“表达式必须是可修改的左值”这样的错误提示。
说明两个变量的存储空间不可以通过变量名进行修改。

const double pi = 3.1415926;
int main()
{
const double e = 2.71828;
double* p_pi = π
double* p_e = &e;
*p_pi = 3.14;
*p_e = 2.71;
printf("pi=%f e=%f\n",pi,e);
return 0;
}

在这里插入图片描述
通过提示可以看到,利用指针变量 p_pi 去改变值时报出了:“发生访问冲突”的错误,仔细观察指向的地址,从局部变量窗口可以看到,这个地址是对应的是一个全局的位置。其实 pi 这个变量加了 const 修饰符后,其创建的位置在常量区,常量区内的数据是不能被改变的,不论用直接访问还是间接访问的手段都不可以。把第9 行去掉,代码可以通过编译并执行。
在这里插入图片描述
从结果可以看到,利用 p_e 指针变量间接的修改了 e 变量空间的值。当然这种情况也和编译器的厂商及版本有关。这里只想说明全局变量加 const,其会被创建在常量区;而局部变量加 const 只能做语法级的初步检测,但是绕过变量名的访问就出现了矛盾。当然实际开发时不建议用这种手段。const 本质是提示开发者数据是只读的。

2.const 修饰指针变量

指向可改 内容不可改- (常量指针)

int m = 7, n = 8;
int const* p = &m
*p = 5;
p = &n;

指向不可改 内容可改(常量指针)

int m = 7, n = 8;
int * const p = &m;
*p = 5;
p = &n;

四、define宏定义

#define 称为宏替换简称宏,其作用相当于查找并替换,
即按预定义好的宏名称进行查找,并把符合的源代码部
位替换成对应的文字内容。

1.宏定义的用法

其基本格式为:#define 宏名 替换内容

注意:宏名与替换内容之间最好一个空格,另外替换内容后面不要随意加分号,因
为分号也将作为替换内容的一部分去参与替换。这样替换成的源代码极有可能产生编译
错误

我们可以用#define 来定义值,用于增强代码的可读性,有点类似枚举。
例如:

#define SUCCESS 0
#define FAIL 1
int main()
{
return SUCCESS;
}

将 SUCCESS 定义为 0,所以 return 0 的可读性就不如 return SUCCESS;的阅读感强。

2.typedef 与#define 的区别

结合如下代码分析二者的区别:

#define PINT int*
typedef int* P_INT;
int main()
{
PINT a, b, c;
P_INT p, q, k;
return 0;
}

在这里插入图片描述
可以看到:b,c 变量的类型都是 int。而 p、q、k 的类型都是 int*。

区别在哪里呢。首先#define 宏工作的时期是预处理期,即编译之前。它的作用是
把源代码变形替换成另一个源代码。这个任务完成后#define 也就不存在了。所以 PINT
变为 int* 后只有 a 变量是指针类型,其它的都是 int 类型变量。而 typedef 是要参加编
译的,编译后其命名的数据类型 P_INT 一直可被项目所使用。所以 p,q,k 都是 int*指针
类型。

另外两者的使用侧重不同。typedef 的目的是简化数据类型,可以把复杂的类型进
行封装。而#define 目的是增强代码的维护性、可读性;从而辅助开发者对一些代码的
查找替换。

3.有参数的宏

有参数的宏是指在替换时并不是整体替换,而是局部替换。模型如下:
在这里插入图片描述

下面通过一个例子说明。
典型的例子就是用宏封装一个计算一维数组长度的表达式:

int arr2[] = {1,3,6,9,8};
double a3[] = {1.2,3.2,5.6,8.9};
printf("%d\n", sizeof(arr2)/ sizeof(arr2[0]));
printf("%d\n", sizeof(a3) / sizeof(a3[0]));

上面代码中计算数组长度的表达式是固定的模式:

sizeof(数组名)/ sizeof(数组名[0])

如果想要计算数组长度只需要把数组名的位置替换成某个数组即可。我们可以把
sizeof(数组名)/ sizeof(数组名[0] ) 这个表达式当作一个模板,变化的部位就是数组
名,谁使用谁提供;用#define 来实现就是:

#define ARR_LEN(arr_name) sizeof(arr_name)/ sizeof(arr_name[0])

这样代码的简洁度、可读性就提高了。虽然用起来感觉像一个函数,但它本质是个
宏。所以在使用时注意宏的格式:宏 替换内容,如果是有参数的宏,那么宏名称与
(参数)之间不要有空格。一旦宏名与括号之间有空格,那么()就变成了替换内容而非
参数的作用了

五、extern 全局变量的声明

extern 通常用于表达变量或函数来自外部,函数本身就是全局定义的,extern 是不需要在函数
上写的。extern 用的比较多的地方还是对全局变量的声明。定义全局变量时一般是不需要写
extern 的,只需要给全局变量赋初始值就是明确定义了全局变量,而不写初始值则可能是声明。
如果想在另一个源代码文件中使用此全局变量,则需要使用前用 extern 做一下这个变量的外部
声明,用于告知此变量的创建来自其它源代码文件,这里仅仅是使用。注意声明是不能赋值的,
如果赋值了就是定义行为,从而会产生全局变量重定义的错误。

例如:
a.c 源代码文件的代码

int age = 5;

b.c 源代码文件中的代码

#include <stdio.h>
int main()
{
extern int age;
printf("%d\n",age);
return 0;
}

六、static 静态变量

static 通常译为“静态”,我们可以把它理解为“某范围内的唯一”。
这个范围指的是作用域,即变量可以访问的区域范围。访问范围可以分为三层。

第一层访问范围:这个变量在整个项目(project)内的任意函数内都可被访问。
第二层访问范围:这个变量只能被当前源代码文件内的所有函数访问,不可以被本源代
码文件外的其它源代码文件的函数访问。
第三层访问范围:这个变量仅能在函数内被访问即函数被调用执行过程中,变量能够被
函数内访问,函数外用变量名不可访问到。

那么 static 在这三个层次的访问范围起到什么作用呢。先看如下代码:
文件 1:s1.c

int age = 5;

文件 2:s2.c

static int money = 1000;
int sayMoney()
{
printf("my money is %d\n",money);
}

文件 3:s3.c

#include <stdio.h>
void test_static();
int sayMoney();

int main()
{
extern int age;
extern int money;
printf("age is %d\n",age);
printf("money is %d\n", money);
sayMoney();
test_static();
test_static();
test_static();
return 0;
}

void test_static()
{
int num = 5;
static int sum;
num += 5;
sum += num;
printf("sum = %d\n",sum);
}

在这里插入图片描述
上面代码中的age变量是一个全局变量,在整个项目内都可以访问它。只需要用
extern int age;做一下外部变量的声明即可。但是s2.c文件中的money变量,虽然写在
了全局的位置上,但是其用static修饰后,它的作用域就被限定在了s2.c这个源代码文
件内。所以s3.c要访问这个变量,即使做了extern int money;外部变量声明,但是当
真正访问时:printf(“money is %d\n”, money); 就会报出:“无法解析外部符合
money”这个编译错误。但是通过调用s2.c的int sayMoney();函数是可以访问money变
量的。最后看void test_static()函数内的两个局部变量num和sum。其中num是非静态
局部变量,当函数调用时它被创建在栈区,函数执行结束后,它的内存空间就被回收,
函数第二次调用再重新创建…,而sum变量前用了static修饰符,它是在程序启动时,
static修饰的变量就会创建在全局静态区并初始值为0,当函数被调用时,sum变量可以
被函数内的代码访问到,当函数调用结束后,栈区的局部变量被回收,而static修饰的
sum变量是不被回收的,放置全局静态区的变量是在程序关闭后才被回收。所以我们从
打印结果可以看到sum变量的值在不断的累加并没有像num变量那样每一次都被重新创
建。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值