一、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 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、运行结果如下:
需要强调的是,typedef 是赋予现有类别两个捷伊英文名字,而并非创建捷伊类别。为的是“见名知意”,请尽量采用含义明晰的标识符,并且尽量大写。
typedef 和 #define 的区别
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在两个关键性的区别。正确思考这个问题的方法是把 typedef 看成一种彻底的“封装”类别,声明之后不能再往里面增加别的东西。
1) 能采用其他类别说明符对宏类别名进行扩展,但对 typedef 所表述的类别名却不能这种做。如下所示:
#define INTERGE int
unsigned INTERGE n; //没问题
typedef int INTERGE;
unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned2) 在连续表述几个表达式的时候,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;
}
运行结果如下:
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;
}
运行结果如下:
根据 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可节省空间,避免不必要的内存分配,提升工作效率。