热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

c语言typedef函数,C语言的typedef

typedef是一种有趣的声明形式:它为一种类型引入新的名字,而不是为变量分配空间。在某些方面,typedef类似于宏文本替换——它并没有

typedef是一种有趣的声明形式:它为一种类型引入新的名字,而不是为变量分配空间。在某些方面,typedef类似于宏文本替换——它并没有引入新的类型,而是为现有类型取个新名字,但它们之间存在一个关键性的区别。

typedef关键字可以是一个常规声明的一部分,可以出现在靠近声明开始部分的任何地方。事实上,typedef 的格式与变量声明完全一样,只是多了这个关键字,向你提醒它的实质。由于typedef看上去跟变量声明完全一样,它们读起来也是一样的。普通的声明表示“这个名字是一个指定类型的变量”,而typedef关键字并不是创建一个变量,而是宣称“这个名字是指定类型的同义词”。

一般情况下,typedef用于简洁的表示指向其他东西的指针。典型的例子是signal()原型的声明。signal()是一种系统调用,用于通知运行时系统:当某种特定的“软件中断”发生时调用特定的程序。调用signal()并通过传参告诉它中断的类型以及用于处理中断的程序。在ANSI C标准中,signal()的声明如下:

void (*signal(int sig,void(*func)(int)))(int);

用分析C语言的声明(2)介绍的方法分析这个声明:

首先:

void (*signal( ))(int);

signal是一个函数,它返回一个函数指针,后者所指向的函数接受一个int参数并返回void。

其中提取出来的通用的部分是:

void (*func)(int);

这个表示一个函数指针,所指向的函数接受一个int参数,返回值是void。下面用typedef来“代表”通用部分,从而进行简化:

typedef void (*ptr_to_func) (int);

/* 它表示ptr_to_func是一个函数指针,该函数接受一个int参数,返回值为void。*/

ptr_to_func signal(int, ptr_to_func);

/* 它表示signal是一个函数,它接受两个参数,

其中一个是int,另一个是ptr_to_func,返回值是ptr_to_func。*/

然而,说到typedef就不能不说一下它的缺点。它同样具有其他声明一样的混乱语法,同样可以把几个声明器塞到一个声明中去。对于结构体,除了可以在书写时省掉struct关键字之外,typedef并不能提供显著的好处,而少些一个struct其实并没有多大帮助。在任何typedef声明中,甚至不必把typedef放在声明的开始位置。

typedef和#define的区别

typedef和define之间有一个关键性的区别。正确思考这个问题的方法就是把typedef看成是一种彻底的“封装”类型——在声明它之后不能再往里面增加别的东西。它和define的区别体现在两个方面:

1.可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。如下所示:

#define peach int

unsigned peach i ; //没有问题

typedef int banana;

unsigned banana i;//错误,非法

2.在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一种类型,而用#define定义的类型则无法保证。如下所示:

#define int_ptr int *

int_ptr chalk, cheese;

经过宏扩展,第二行变为:

int * chalk, cheese;

这使得chalk和cheese成为不同的类型:chalk是一个指向int的指针,而cheese则是一个int。

typedef char * char_ptr;

char_ptr Bentley, Rolls_Royce;

使用typedef定义的类型能够保证它们的类型相同,都是指向char的指针。

typedef struct foo{ ... foo;}的含义

C语言存在多种名字空间:

标签名(label name)。

标签(tag):这个名字空间用于所有的结构体、枚举和联合。

成员名:每个结构体或联合都有自身的名字空间。

其他。

在同一个名字空间里,任何名字必须具有唯一性,但在不同的名字空间里可以存在相同的名字。由于每个结构体或联合具有自己的名字空间,所以同一个名字可以出现在许多不同的结构内。有时可以看到这样的代码:

struct foo { int foo;} foo;

这显然会让将来维护这段代码的程序员感到困惑和沮丧,你说sizeof(foo)到底是表示哪个foo呢?

有些东西更加稀罕,像下面这样的声明竟然也是合法的:

typedef struct baz { int baz; } baz;

struct baz variable_1;

baz variable_2;

太多的“baz”了!让我们换一些清楚的名字,整理一下:

typedef struct my_tag { int i; } my_type;

struct my_tag variable_1;

my_type variable_2;

这个typedef声明引入了my_type这个名字作为"struct my_tag { int i; }"的简写形式。如果你用typedef声明一个标识符表示struct 结构类型,那么以后使用这个标识符时前面就不必加上关键字struct 了。但这个typedef声明同时也引入了结构标签my_tag,在它面前加个关键字struct可以表示同样的意思。

下面两个声明具有相似的形式:

typedef struct fruit { int weight , price_per_lb; } fruit; // 语句1

struct veg { int weight , price_per_lb; } veg; //语句2

但它们代表的意思却完全不一样,语句1声明了结构体标签”fruit“和由typedef声明的结构体类型”fruit“,其实际效果如下:

struct fruit mandarin; //使用结构体标签”fruit“

fruit mandarin; //使用结构体类型”fruit“

语句2声明了结构体标签”veg“和变量veg。只有结构体标签能够在以后的声明中使用,如:

struct veg potato;

如果试图使用veg cabbage这样的声明,将是一个错误。

使用typedef的一些提示:

1.不要在一个typedef中放入几个声明器,如下所示:

typedef int *ptr, (fun()), arr[5];

/* ptr是“指向int的指针”类型,

* fun是“指向返回值为int函数的指针”类型

* arr是“长度为5的int型数组”类型

*/

2.千万不要把typedef嵌到声明的中间部分,如下所示:

unsigned const long typedef int volatile *kumquat;

3.typedef为数据类型创建别名,而不是创建新的数据类型,可以对任何类型进行typedef声明。

typedef int (*array_ptr)[100];

4.应该只对所希望的变量类型进行typedef声明,为变量类型取一个喜欢的别名。关键字typedef应该如前所述出现声明的开始位置。在同一个代码块中,typedef引入的名字不能与其他标识符同名。

5.不要为了方便起见对结构体使用typedef。这样做唯一的好处是能使你不必书写”struct“关键字,但这个关键字可以向你提示一些信息,你不应该把它省掉。应该始终在结构体的定义中使用结构标签,即使它并非必须。这种做法可以使代码更为清晰。

6.typedef应该用在:

数组、结构体、指针以及函数的组合类型。

可移植类型。比如当你需要一种至少20bit的类型时,可以对它进行typedef操作。这样,当把代码移植到不同的平台时,要选择正确的类型如short,int,long时,只要在typedef中进行修改就可以了,无需对每一个声明都加以修改。

typedef也可以为后面的强制类型转换提供一个简单的名字,如:

typedef int (*ptr_to_int_fun)(void);

char * p;

...;

... = (ptr_to_int_fun) p;



推荐阅读
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文详细介绍了C语言中链表的两种动态创建方法——头插法和尾插法,包括具体的实现代码和运行示例。通过这些内容,读者可以更好地理解和掌握链表的基本操作。 ... [详细]
  • 本文介绍了如何通过扩展 UnityGUI 创建自定义和复合控件,以满足特定的用户界面需求。内容涵盖简单和静态复合控件的实现,并展示了如何创建复杂的 RGB 滑块。 ... [详细]
  • 本文详细探讨了C语言中指针的概念,特别是指针在变量和数组中的应用。通过实例讲解,帮助读者更好地掌握指针的使用方法。 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
author-avatar
桃子小1992
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有