C语言高频率–typedef和const用法详解

2023-06-01 0 699

一、typedef用语简述

C词汇容许为两个数据类别起两个捷伊别称,就像给人起“外号”那样。

起别称的目地并非为的是提升程序代码工作效率,而要为的是代码方便快捷。比如有两个内部结构体的英文名字是 stu,要想表述两个内部结构体变量就得这种写:

struct stu stu1;

struct 看上去是累赘的,但不写又会收起。假如为 struct stu 起了两个别称 STU,手写出来就单纯了:

STU stu1;

此种读法更为明快,象征意义也十分明晰,无论是在国际标准子程序中却是之后的程式设计实践中,单厢大批采用此种别称。

1、采用URLtypedef能为类别起两个捷伊别称。typedef 的用语通常为:

typedef oldName newName;

oldName 是类别原本的英文名字,newName 是类别捷伊英文名字。比如:

typedef int INTEGER;

INTEGER a, b;

a = 10;

b = 20;

INTEGER a, b;全幅int a, b

typedef 还能给字符串、操作符、内部结构体等类别表述别称。先上看两个给字符串类别表述别称的范例:

typedef char ARRAY20[20];

则表示 ARRAY20 是类别char [20]的别称。它是两个宽度为 20 的字符串类别。接著能用 ARRAY20 表述字符串:

ARRAY20 a1, a2, s1, s2;

它等价于:

char a1[20], a2[20], s1[20], s2[20];

注意,字符串也是有类别的。比如char a1[20];

2、表述了两个字符串 a1,它的类别是 char [20]。

又如,为内部结构体类别表述别称:

typedef struct stu{

char name[20];

int age;

char sex;

} STU;

3、STU 是 struct stu 的别称,能用 STU 表述内部结构体表达式:

STU body1,body2;

它等价于:

struct stu body1, body2;

再如,为操作符类别表述别称:

typedef int (*PTR_TO_ARR)[4];

则表示 PTR_TO_ARR 是类别int * [4]的别称,它是两个二维字符串操作符类别。接著能采用 PTR_TO_ARR 表述二维字符串操作符:

PTR_TO_ARR p1, p2;

按照类似的读法,还能为函数操作符类别表述别称:

typedef int (*PTR_TO_FUNC)(int, int);

PTR_TO_FUNC pfunc;

4、【示例】为操作符表述别称。

#include <stdio.h>

typedef char(*PTR_TO_ARR)[100];

typedef int(*PTR_TO_FUNC)(int, int);

int max(int a, int b){

return a > b ? a : b;

}

char str[6][100] = {

C词汇经典编程100例实战-学习视频教程-腾讯课堂”,

数据内部结构算法(C词汇版)-学习视频教程-腾讯课堂“,

C词汇入门到精通-学习视频教程-腾讯课堂“,

“博新教育”,

“C Language programming.”,

“C/C++学习交流群:893287989”

};

int main()

{

PTR_TO_ARR parr = str;

PTR_TO_FUNC pfunc = max;

int i;

printf(“max: %d\n”, (*pfunc)(10, 20));

for (i = 0; i < 6; i++)

{

printf(“str[%d]: %s\n”, i, *(parr + i));

}

return 0;

}

4、运行结果如下:

C语言高频率–typedef和const用法详解

需要强调的是,typedef 是赋予现有类别两个捷伊英文名字,而并非创建捷伊类别。为的是“见名知意”,请尽量采用含义明晰的标识符,并且尽量大写。

typedef 和 #define 的区别

typedef 在表现上有时候类似于 #define,但它和宏替换之间存在两个关键性的区别。正确思考这个问题的方法是把 typedef 看成一种彻底的“封装”类别,声明之后不能再往里面增加别的东西。

1) 能采用其他类别说明符对宏类别名进行扩展,但对 typedef 所表述的类别名却不能这种做。如下所示:

#define INTERGE int

unsigned INTERGE n; //没问题

typedef int INTERGE;

unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned

2) 在连续表述几个表达式的时候,typedef 能够保证定义的所有表达式均为同一类别,而 #define 则无法保证。比如:

#define PTR_INT int *

PTR_INT p1, p2;

经过宏替换之后,第二行变为:

int *p1, p2;

这使得 p1、p2 成为不同的类别:p1 是指向 int 类别的操作符,p2 是 int 类别。

相反,在下面的代码中:

typedef int * PTR_INT

PTR_INT p1, p2;

p1、p2 类别相同,它们都是指向 int 类别的操作符。

二、const用语简述

有时候我们希望表述这种一种表达式,它的值不能被改变,在整个作用域中都保持固定。比如,用两个表达式来则表示班级的最大人数,或者则表示缓冲区的大小。为的是满足这一要求,能采用constURL对表达式加以限定:

const int MaxNum = 100; //班级的最大人数

这种 MaxNum 的值就不能被修改了,任何对 MaxNum 赋值的行为都将引发错误:

MaxNum = 90; //错误,试图向 const 表达式写入数据

我们经常将 const 表达式称为常量(constant)。创建常量的格式通常为:

const type name = value;

const 和 type 都是用来修饰表达式的,它们的位置能互换,也是将 type 放在 const 前面:

type const name = value;

但我们通常采用第一种方式,不采用第二种方式。另外建议将常量名的首字母大写,以提醒程序员这是个常量。

由于常量一旦被创建后其值就不能再改变,所以常量必须在表述的同时赋值(初始化),后面的任何赋值行为都将引发错误。一如既往,初始化常量能采用任意形式的表达式,如下所示:

#include <stdio.h>

int getNum()

{

return 10000;

}

int main()

{

int n = 90000;

const int MaxNum1 = getNum(); //运行时初始化

const int MaxNum2 = n; //运行时初始化

const int MaxNum3 = 80000; //编译时初始化

printf(“%d, %d, %d\n”, MaxNum1, MaxNum2, MaxNum3);

return 0;

}

运行结果如下:

C语言高频率–typedef和const用法详解

1、const 和操作符

const 也能和操作符表达式一起采用,这种能限制操作符表达式本身,也能限制操作符指向的数据。const 和操作符一起采用会有几种不同的顺序,如下所示:

const int *p1;

int const *p2;

int * const p3;

在最后一种情况下,操作符是只读的,也是 p3 本身的值不能被修改;在前面两种情况下,操作符所指向的数据是只读的,也是 p1、p2 本身的值能修改(指向不同的数据),但它们指向的数据不能被修改。

当然,操作符本身和它指向的数据都有可能是只读的,下面的两种读法能够做到这一点:

const int * const p4;

int const * const p5;

const 和操作符结合的读法多少有点让初学者摸不着头脑,大家能这种来记忆:const 离变量名近是用来修饰操作符表达式的,离表达式名远是用来修饰操作符指向的数据,假如近的和远的都有,那么就同时修饰操作符表达式以及它指向的数据。

2、const 和函数形参

在C词汇中,单独表述 const 表达式没有明显的优势,完全能采用#define 命令代替。const 通常用在函数形参中,假如形参是两个操作符,为的是防止在函数内部修改操作符指向的数据,就能用 const 来限制。

在C词汇国际标准库中,有很多函数的形参都被 const 限制了,下面是部分函数的原型:

size_t strlen ( const char * str );

int strcmp ( const char * str1, const char * str2 );

char * strcat ( char * destination, const char * source );

char * strcpy ( char * destination, const char * source );

int system (const char* command);

int puts ( const char * str );

int printf ( const char * format, … );

我们自己在表述函数时也能采用 const 对形参加以限制,比如查找字符串中某个字符出现的次数:

#include <stdio.h>

#include <string.h>

size_t strnchr(const char *str, char ch)

{

int i, n = 0, len = strlen(str);

for (i = 0; i<len; i++)

{

if (str[i] == ch)

{

n++;

}

}

return n;

}

int main()

{

char *str = “C词汇经典程式设计100例实战-学习视频教程-腾讯课堂“;

char ch = t;

int n = strnchr(str, ch);

printf(“%d\n”, n);

return 0;

}

运行结果如下:

C语言高频率–typedef和const用法详解

根据 strnchr() 的功能能推断,函数内部要对字符串 str 进行遍历,不应该有修改的动作,用 const 加以限制,不但能防止由于程序员误操作引起的字符串修改,还能给用户两个提示,函数不会修改你提供的字符串,请你放心。

3、const 和非 const 类别转换

当两个操作符表达式 str1 被 const 限制时,并且类似const char *str1

此种形式,说明操作符指向的数据不能被修改;假如将 str1 赋值给另外两个未被 const 修饰的指针表达式 str2,就有可能发生危险。因为通过 str1 不能修改数据,而赋值后通过 str2 能够修改数据了,象征意义发生了转变,所以编译器不提倡此种行为,会给出错误或警告。

也是说,const char *和char *是不同的类别,不能将const char *类别的数据赋值给char *类别的表达式。但反过来是能的,编译器容许将char *类别的数据赋值给const char * 类别的表达式。

这种限制很容易理解,char *指向的数据有读取和写入权限,而const char *指向的数据只有读取权限,降低数据的权限不会带来任何问题,但提升数据的权限就有可能发生危险。

C词汇国际标准库中很多函数的参数都被 const 限制了,但我们在以前的代码过程中并没有注意这个问题,经常将非 const 类别的数据传递给 const 类别的形参,这种做从未引发任何副作用,原因是上面讲到的,将非 const 类别转换为 const 类别是容许的。

四、const与#define相比及优势

1、区别

(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。

(2)就起作用的方式而言: #define只是单纯的字符串替换,没有类别检查。而const有对应的数据类别,是要进行判断的,能避免一些低级的错误。

(3)就存储方式而言:#define只是进行展开,有多少地方采用,就替换多少次,它表述的宏常量在内存中有若干个备份;const表述的只读表达式在程序代码过程中只有一份备份。

(4)从代码调试的方便快捷程度而言: const常量能进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉。

2、const优点

(1)const常量有数据类别,而宏常量没有数据类别。编译器能对前者进行类别安全检查。而对后者只进行字符替换,没有类别安全检查,并且在字符替换可能会产生意料不到的错误。

(2)有些集成化的调试工具能对const常量进行调试,但是不能对宏常量进行调试。

(3)const可节省空间,避免不必要的内存分配,提升工作效率。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务