define typedef的区别以及常量的定义

typedef用法 
1、 为现有数据类型定义同义词 
 typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。 例如: 
typedef int size; 
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:    void measure(size psz);  
size array[4]; 2、 typedef 还可以掩饰复合类型,如指针和数组 
 数组 
例如: typedef char Line[81];表示使用Line代表具有81个元素的char类型数组。如果要生成类似的数据可以使用如下形式:  
Line text, secondline; 
相当于char test[81];char secondline[81];

 

typedef的语法规则其实很简单,一句话来说就是定义对象的语法前加关键字typedef,剩下的不变,原本定义的对象标识符换成类型标识符,对应语义从定义一个对象改成定义一个类型别名。typedef看起来复杂根本原因是对象定义的语法比较复杂,例如分隔符*和[]的用法。

typedef struct node{

datatype data;

struct node *lchild,*rchild;

}bintnode;

对应的对象定义:

struct node{

datatype data;

struct node *lchild,*rchild;

}bintnode;

去除bintnode来看就是一个类型struct node的完整描述。加上了bintnode来看,表示定义了一个struct node类型的对象bintnode。

现在前面有typedef,因此这个bintnode不是对象名而是类型名。也就是定义了一个类型别名bitnode,实际上指的就是struct node这个完整类型。

typedef定义的类型别名在作用域内和被定义的原类型语义上等价,都是表示同一个类型的名称。这里typedef之后bitnode可以和struct node互相代替(注意在C++中,如果同一命名空间内类型名和对象名没有重复,那么struct可以省略,struct node等价于node)。

更复杂一点的:

struct node{

datatype data;

struct node *lchild,*rchild;

}bintnode, *bintree;

注意定义对象时*修饰对象本身而不是修饰类型。因此这里定义了struct node对象bintnode和struct node*类型的对象bintree。

对应的类型定义:

struct node{

datatype data;

struct node *lchild,*rchild;

}bintnode, *bintree;

这里定义了类型别名bintnode表示完整类型struct node,以及bintree表示类型struct node*。

拆开来就成了

typedef struct node{

datatype data;

struct node *lchild,*rchild;

}bintnode;



typedef bintnode *bintree;

这种写法看起来应该稍微清楚了一点。

至于下面的cirqueue,和上面的bintnode类似,只是这里省略了结构体名称,是一个匿名类型。这样写的主要好处是类型名可以省略struct(C++里面不这样写也可以省略)。

Typedef和define都可以用来给对象定义另外一个别名,但是两者却有着很大不同。

1. 首先,二者执行时间不同

关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

Define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

 

#define用法例子:

#define f(x) x*x
main( )
{
 int a=6,b=2,c;
 c=f(a) / f(b);
 printf("%d \\n",c);
}

程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换。

 

2. 功能不同

Typedef用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。

如: typedef int (*PF) (const char *, const char *);

定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *。

 

typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:typedef long double REAL;
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:typedef double REAL;
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:typedef float REAL;

 

#include
typedef int arr[10];
void main()
{
 arr a;
 int i;
 for(i=0;i<10;i++)
 {
  a=i*i;
  printf("%d\n",a);
 }
}

 

#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

 

3. 作用域不同

#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。

而typedef有自己的作用域。

void   fun()   
  {   
      #define   A   int   
  }   
    
  void   gun()   
  {   
        //在这里也可以使用A,因为宏替换没有作用域,   
        //但如果上面用的是typedef,那这里就不能用A  ,不过一般不在函数内使用typedef
  }

4. 对指针的操作

二者修饰指针类型时,作用不同。

Typedef int * pint;

#define PINT int *

 

Const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;

Const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;


pint s1, s2; //s1和s2都是int型指针
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。

 

 

typedef的四个用途和两个陷阱

用途一: 
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如: 
char*     pa,     pb;       //     这多数不符合我们的意图,它只声明了一个指向字符变量的指针,   
//     和一个字符变量; 
以下则可行: 
typedef     char*     PCHAR;       //     一般用大写 
PCHAR     pa,     pb;                   //     可行,同时声明了两个指向字符变量的指针 
虽然: 
char     *pa,     *pb; 
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。 

用途二: 
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为:     struct     结构名     对象名,如: 
struct     tagPOINT1 

          int     x; 
          int     y; 
}; 
struct     tagPOINT1     p1;   

而在C++中,则可以直接写:结构名     对象名,即: 
tagPOINT1     p1; 

估计某人觉得经常多写一个struct太麻烦了,于是就发明了: 
typedef     struct     tagPOINT 

          int     x; 
          int     y; 
}POINT; 

POINT     p1;     //     这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候 

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。 

用途三: 
用typedef来定义与平台无关的类型。 
比如定义一个叫     REAL     的浮点类型,在目标平台一上,让它表示最高精度的类型为: 
typedef     long     double     REAL;   
在不支持     long     double     的平台二上,改为: 
typedef     double     REAL;   
在连     double     都不支持的平台三上,改为: 
typedef     float     REAL;   
也就是说,当跨平台时,只要改下     typedef     本身就行,不用对其他源码做任何修改。 
标准库就广泛使用了这个技巧,比如size_t。 
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。 

用途四: 
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例: 

1.     原声明:int     *(*a[5])(int,     char*); 
变量名为a,直接用一个新别名pFun替换a就可以了: 
typedef     int     *(*pFun)(int,     char*);   
原声明的最简化版: 
pFun     a[5];   

2.     原声明:void     (*b[10])     (void     (*)()); 
变量名为b,先替换右边部分括号里的,pFunParam为别名一: 
typedef     void     (*pFunParam)(); 
再替换左边的变量b,pFunx为别名二: 
typedef     void     (*pFunx)(pFunParam); 
原声明的最简化版: 
pFunx     b[10]; 

3.     原声明:doube(*)()     (*e)[9];   
变量名为e,先替换左边部分,pFuny为别名一: 
typedef     double(*pFuny)(); 
再替换右边的变量e,pFunParamy为别名二 
typedef     pFuny     (*pFunParamy)[9]; 
原声明的最简化版: 
pFunParamy     e;   

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例: 
int     (*func)(int     *p); 
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明 (*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。 
int     (*func[5])(int     *); 
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。 

也可以记住2个模式: 
type     (*)(....)函数指针   
type     (*)[]数组指针   
--------------------------------- 

陷阱一: 
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如: 
先定义: 
typedef     char*     PSTR; 
然后: 
int     mystrcmp(const     PSTR,     const     PSTR); 

const     PSTR实际上相当于const     char*吗?不是的,它实际上相当于char*     const。 
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char*     const。 
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。 

陷阱二: 
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如: 
typedef     static     int     INT2;     //不可行 
编译将失败,会提示“指定了一个以上的存储类”。

#define,const,typedef三者简单的联系与区别

一. #define与const

联系:都可以用来定义常量

区别:

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

2. 前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量罢了。或者说,const的常量是一个Run-Time的概念,他在程序中确确实实的存在并可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。


3.const常量存在于程序的数据段,#define常量存在于程序的代码段。

4. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试、

常量是常数或代表固定不变值的名字。
  程序中如果想让变量的内容自初始化后一直保持不变,可以定义一个常量。
  例如,在圆面积计算中经常要用常数丌,此时,通过命名一个容易理解和记忆的名字来改进程序的可读性,同时在定义中加关键字const,给它规定为常量性质,以帮助预防程序出错。
  如果在整个程序中的许多地方都要用到一个常数,那么,在这些地方的一个或多个地方写错了这个值就会导致计算错误。如果给常数取个名字,每处常数都以该名字代替,那么编译器就能检查名字拼写错误,避免常数值的不一致性。
  丌字符不属于C++语言的字符描述集,不能用来作C++中的名字。我们用pi来表示丌:
   const float pi=3.1415926;
  由于有效位的限制,在下面常量定义中,最后3位不起作用:
   const float pi=3.141592653;
  尽管等号后面的常数是double型的,但因为float常量只能存储7位有效位精度的实数,所以pi的实际值为3.141593(最后1位4舍5人)。如果将常量pi的类型改为double型,则能全部接受上述10位数字。
  定义成const后的常量,程序中对其只能读不能修改,从而可以防止该值被无意地修改。由于不可修改,所以,常量定义时必须初始化。例如:
   const float pi;
   pi=3.1415926; //error
  常量名不能放在赋值语句的左边。
  常量定义中初始化的值可以是一个常量表达式。常量在程序运行之前就已经知道了其值,所以,编译时就能求值。但表达式中不能含有某个函数。例如:
   const int size=100 * sizeof(int); //ok
   const int number=max(15,23);   //error
  因为sizeof不是函数,而是C++的基本操作符,该表达式的值在编译之前能确定,所以第一个常量定义语句合法。第二个语句要求函数值,函数一般都要在程序开始运行时才能求值,该表达式不能在编译之前确定其值,所以是错误的。
一般来说,相同类型的变量和常量在内存中占有相同大小的空间。只不过常量不能通过常量名去修改其所处的内存空间,而变量却可以。
  

        ->关于#define
  在C中,另一种定义常量的方法是用编译预定义指令(#define)。例如:
   #define PI 3.1415926
  这条指令的格式是#define后面跟一个常量名再跟一串字符, 中间用空格隔开。由于它不是C++语句,所以行末不用分号。
  当程序被编译时,它要先被编译预处理。当预处理遇到#define指令时,就用数值3.1415926替换程序中所有的PI。
  尽管它具有常量的所有属性,但是,在编译预处理完成后,PI不属于C++程序中的名字了(全部被字串3.1415926所替代),所以它不是一个具有一定类型的常量名。随后的编译无法发现由它引起的数据类型误用的错误。
  C++容许#define定义常量是为了兼容C,使C程序能在C++编译器中顺利通过。在C++编程中,常量定义都用const,不用#define。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
definetypedef都是C语言中用来定义别名的关键字,但在使用方式和作用范围上有所不同。 define是用来定义宏的关键字。通过使用#define,可以给一个常量、表达式、函数等定义一个符号常量,以后在代码中可以使用该符号常量来代替对应的值。define的替换是在预处理阶段进行的,是一种简单的字符串替换。它没有类型概念,只是将代码中的某个标识符替换为指定的文本。 typedef是用来定义类型别名的关键字。通过使用typedef,可以为已有的数据类型定义一个别名,使得在后续的代码中可以使用该别名来代替原有的类型名称。typedef是在编译阶段进行的,实际上并没有创建新的数据类型,只是为已有的类型起了一个新的名字。typedef可以用来同时声明指针型的多个对象、帮助旧的C代码中的struct、定义与平台无关的类型以及为复杂的声明定义一个新的简单的别名。 总结来说,define是用来定义常量或者宏,而typedef是用来定义类型别名。它们在作用范围和使用方式上有所不同,但都是为了提高代码可读性和可维护性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [typedef和#define的用法与区别](https://blog.csdn.net/yueguangmuyu/article/details/114073337)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [【C语言】typedefdefine区别](https://blog.csdn.net/Goforyouqp/article/details/131498673)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值