本文主要讲解:
- typedef在基础数据类型、在指针、在结构体、在指向结构体的指针、在枚举类型的用法;
- const和define语法和本质区别,用法注意事项;
typedef
1、typedef为各种数据类型定义一个新名字。
#include<stdio.h>
typedef int Interger;
typedef unsigned int UInterger;
typedef float Float;
int main()
{
Interger i = -1;
UInterger ui = 1;
Float f = 12.33f;
printf("%d %d %.2f", i, ui, f);
}
2、typedef与指针
#include <stdio.h>
typedef char *String;
int main(int argc, const char * argv[])
{
// 相当于char *str = "This is a string!";
String str = "This is a string!";
printf("%s", str);
return 0;
}
3、typedef与结构体
#include<stdio.h>
struct MyPoint
{
float x;
float y;
};
typedef struct MyPoint point; //起别名
int main()
{
struct MyPoint p;
p.x = 10.0f;
p.y = 20.0f;
point pp;
pp.x = 10.0f;
pp.y = 20.0f;
return 0;
}
// 定义一个结构体,顺便起别名
typedef struct MyPoint {
float x;
float y;
} Point;
//甚至可以省略结构体名称:
typedef struct {
float x;
float y;
} Point;
4、typedef与指向结构体的指针
#include <stdio.h>
// 定义一个结构体并起别名
typedef struct {
float x;
float y;
} Point;
// 起别名
typedef Point *PP;
int main(int argc, const char * argv[]) {
// 定义结构体变量
Point point = { 10, 20 };
// 定义指针变量
PP p = &point;
// 利用指针变量访问结构体成员
printf("x=%f,y=%f", p->x, p->y);
return 0;
}
5、typedef与枚举类型
// 定义枚举类型
enum Season { spring, summer, autumn, winter };
// 给枚举类型起别名
typedef enum Season Season;
int main(int argc, const char * argv[]) {
// 定义枚举变量
Season s = spring;
return 0;
}
// 定义枚举类型,并且起别名
typedef enum Season {spring, summer, autumn, winter} Season
甚至可以省略枚举名称,简化为:
typedef enum {spring, summer, autumn, winter} Season;
6、typedef与指向函数的指针
#include <stdio.h>
// 定义一个sum函数,计算a跟b的和
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
//给指向函数的指针类型,起了个别名叫MySum,被指向的函数接收2个int类型的参数,
//返回值为int类型。
typedef int(*MySum)(int, int);
int main(int argc, const char * argv[]) {
// 定义一个指向sum函数的指针变量p
//int (*p)(int, int) = sum; //原始
MySum p = sum; //使用了typedef后
// 利用指针变量p调用sum函数
(*p)(4, 5);
return 0;
}
const和define
一句话说明白它们之间的区别:const是定义一个不可再变的量(常量),define是为一个变量或表达式定义一个别名,在使用时替换成变量或表达式。
const
- 本质上来说,const修饰的是假常量,它本质还是变量!只不过编译器不让你修改!const 是 constant 的缩写,意思是“恒定不变的”!
- const一般修饰于定义变量的前面,比如cosnt int a = 10,意味着a就一直是19,是一个不可修改的变量了。往后试图给a重新赋值都会引发错误。这就是const修饰之后的结果,但请注意,这里不可修改的原因其实是编译器在做检查,检查是否有修改这块内存上的值,一旦有就会报错,所以从根本上说,cosnt修饰的变量依然是一个变量!只不过编译器不让你修改而已!
- 用const float MAX = 255; 定义的常量有类型名字,存放在内存的静态区域中,在程序运行过程中const变量只有一个拷贝
- const是不可以定义函数的
- 用const定义的常量是可以用指针去指向该常量的地址的;
define
- 本质上来说,define修饰出来的是常量!并且是真常量!
- 为什么说define修饰出来的是真常量呢?因为,define是宏定义,是宏替换,意味着程序在编译前期会进行文本替换,例如代码define PI 3.1415926 这句话,编译器会将代码中所有出现PI的地方全部用3.1415926替换,然后在进行编译,所以本质它就是一个常量!
- 用#define MAX 255定义的常量是没有类型的,所给出的是一个立即数,编译器只是把所定义的常量值与所定义的常量的名字联系起来,define所定义的宏变量在预处理的时候进行替换,在程序中使用到该常量的地方都要进行拷贝替换;#define 所定义的宏变量却有多个拷贝,所以宏定义在程序运行过程中所消耗的内存要比const变量的大得多;
- 用define可以定义一些简单的函数
- 用define定义的常量是不可以用指针变量去指向
const与指针
const int* p; //p可变,p指向的对象不可变
int const* p; //p可变,p指向的对象不可变
int* const p; //p不可变,p指向的对象可变
const int* const p; //指针p和p指向的对象都不可变
编译器处理方式不同
- define – 在预处理阶段进行替换
- const – 在编译时确定其值
- define是宏定义,程序在预处理阶段将用define定义的内容进行替换,因此程序运行时常量表中并没有用define定义的常量,系统不为其分配内存。
- const常量是编译运行时的常量,系统为其分配内存。
类型与安全检查不同
- define – 无类型,不进行类型安全检查,可能会产生意想不到的错误(边际效应)
- const – 有数据类型,编译时会进行类型检查
define注意“边际效应”。
例:
#define N 2+3 //N的值是5
int a = N/2; //在编译时预想a=2.5,实际打印结果是3.5原因是在预处理阶段,编译器将a=N/2处理成a=2+3/2,这就是define宏的边际效应;所以我们应该写成#define N (2+3)
存储方式不同
- define – 不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大
- const – 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝,const可以节省空间,避免不必要的的内存分配
其他
- 在编译时, 编译器通常不为const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
- define是宏定义,程序在预处理阶段将用define定义的内容进行替换,因此程序运行时常量表中并没有用define定义的常量,只作替换,不做计算,不做表达式求解。
#include <stdio.h>
#define N 10 //将代码中出现N的地方全部替换为10
int main()
{
const int a = 10; //const强调不变性
//a = 20; 报错!
printf("%d, %d", N, a);
return 0;
}