首页 > 其他分享 >C语言 <stddef.h> 常用定义

C语言 <stddef.h> 常用定义

时间:2023-09-07 12:13:00浏览次数:35  
标签:__ 常用 struct endif Address C语言 printf offsetof 定义

<stddef.h> 头文件是C语言标准库的头文件之一,其中定义了常用的宏和类型,但是没有声明任何函数。

类型定义

ptrdiff_t :该类型主要用于指针的相减运算的返回类型,下面是在头文件中找到的源代码定义

#ifndef __PTRDIFF_TYPE__
    #ifdef _WIN64
        #define __PTRDIFF_TYPE__ long long int
    #else
        #define __PTRDIFF_TYPE__ long int
    #endif
#endif

#ifndef _PTRDIFF_T_DEFINED
    #define _PTRDIFF_T_DEFINED
    __MINGW_EXTENSION typedef __PTRDIFF_TYPE__ ptrdiff_t;
#endif

通过上面的代码,我们可以看到,ptrdiff_t 是 long int 或者 long long int 的重定义,具体是哪一个则取决于机器类型是否为 64 位。但无论是哪一个,该类型都是有符号类型,因此取值可正可负

size_t : 该类型主要用于 sizeof 运算符的返回值,下面是笔者在标准库中找到的源代码部分

#ifndef __SIZE_TYPE__
    #ifdef _WIN64
        #define __SIZE_TYPE__ long long unsigned int
    #else
        #define __SIZE_TYPE__ long unsigned int
    #endif
#endif

#if !(defined (__GNUG__) && defined ())
    __MINGW_EXTENSION typedef __SIZE_TYPE__ size_t;
    
    #ifdef __BEOS__
    typedef long ssize_t;
    #endif /* __BEOS__ */

#endif /* !(defined (__GNUG__) && defined (size_t)) */

通过上面的源代码,我们可以很容易的看到,size_t 类型为 long unsigned int 或者 long long unsigned int 两种类型之一,具体为哪一种则取决于机器是否为 64 位。但是无论哪一种都是 unsigned 类型,即无符号类型。

wchar_t :该类型主要用于表示宽字符 ,宽字符可以支持所有地区的所有字符,因此范围上肯定要比 char 类型大,以下是在库中找到的源代码定义部分:

#ifndef __WCHAR_TYPE__
    /* wchar_t is unsigned short for compatibility with MS runtime */
    #define __WCHAR_TYPE__ unsigned short
#endif

#ifndef __cplusplus
    typedef __WCHAR_TYPE__ wchar_t;
#endif

可以看到,wchar_t 是 unsigned short 类型,且该类型是在 MSVCRT 即MS运行库适用的

宏定义

NULL :老熟人了,用于指定指针为空,下面是标准库源代码中的相关定义

#if defined (_STDDEF_H) || defined (__need_NULL)
    #undef NULL		/* in case <stdio.h> has defined it. */
    
    #if defined(__GNUG__) && __GNUG__ >= 3
        #define NULL __null
    #else   /* G++ */
        #ifndef __cplusplus
            #define NULL ((void *)0)
        #else   /* C++ */
            #ifndef _WIN64
                #define NULL 0
            #else
                #define NULL 0LL
            #endif  /* W64 */
        #endif  /* C++ */
    #endif  /* G++ */
#endif	/* NULL not defined and <stddef.h> or need NULL.  */

 从以上定义不难看出,在 C 语言中 NULL 与 ((void*) 0) 等价,而在 C++ 中,NULL 就是 整数 0

offsetof 宏:该宏的用法较为特殊,,主要用于结构体,测试结构体成员在结构体中的偏移

/* Offset of member MEMBER in a struct of type TYPE. */
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)

以上代码只是说明了该宏的作用,但是并没有具体实现。笔者又在 VS 的编译器中找到了针对 MSVCRT 的定义

#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
    #ifdef __cplusplus
        #define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
    #else
        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
    #endif
#else
    #define offsetof(s,m) __builtin_offsetof(s,m)
#endif

通过上面的代码可以看出,offsetof 的具体实现,过程解释如下:

首先是 ((s*)0) 这部分是值将 0 强制转换为 结构体 s 类型的指针,而C语言又强制规定了结构体指针必定指向结构体的第一个成员起始位置,因此这一步相当于将结构体放在起始地址为0的位置上去,接下来,(((s*)0)->m) 是指对转换后的指针执行取成员的操作,但是咋这里我们只关心成员的偏移而不关心成员本身,因此 &(((s*)0)->m) 会去取出成员 m 的地址,由于前面的强制类型转换,已经将结构体起始位置放在了 地址 0 处,因此取地址操作所得的地址大小,就是相对于起始地址的偏移量,因此,直接将该地址强制转换为 size_t 类型即可得出成员 m 相对于 结构体 起始位置偏移。

因为结构体具有字节对齐、字节填充的特性,这些都是为了更快的处理效率,因此结构体成员的 偏移可能是我们选举要注意的地方,而这个宏的使用也很简单

s#include <stdio.h>
#include <stddef.h> 

struct A
{
	char a;
	int b;
	short c;
	float d;
};

int main(int argc, char *argv[]) 
{
	struct A s;
	printf("sizeof(A) = %d\n", sizeof(struct A));
	printf("Address of s = %p\n", &s);
	printf("Address of s.a = %p\n", &s.a);
	printf("Address of s.b = %p\n", &s.b);
	printf("Address of s.c = %p\n", &s.c);
	printf("Address of s.d = %p\n", &s.d);
	
	printf("offsetof(struct A, a) = %d\n", offsetof(struct A, a));
	printf("offsetof(struct A, b) = %d\n", offsetof(struct A, b));
	printf("offsetof(struct A, c) = %d\n", offsetof(struct A, c));
	printf("offsetof(struct A, d) = %d\n", offsetof(struct A, d));
	
	return 0;
}

// 运行结果
sizeof(A) = 16
Address of s = 000000000062FE10
Address of s.a = 000000000062FE10
Address of s.b = 000000000062FE14
Address of s.c = 000000000062FE18
Address of s.d = 000000000062FE1C
offsetof(struct A, a) = 0
offsetof(struct A, b) = 4
offsetof(struct A, c) = 8
offsetof(struct A, d) = 12

从上面的程序结果可以看出,char 类型 的 a 原本只有 1 字节大小,但是却占用了4个字节空间,a 与 b 之间有3个字节的填充,同理, c 与 d 之间也有 2 个字节的填充。这样就浪费了 5 个字节的空间。但是当我们调整以下结构体成员的先后顺序,就会减少这种空间浪费

#include <stdio.h>
#include <stddef.h> 

struct A
{
	int a;
	float b;
	short c;
	char d;
};

int main(int argc, char *argv[]) 
{
	struct A s;
	printf("sizeof(A) = %d\n", sizeof(struct A));
	printf("Address of s = %p\n", &s);
	printf("Address of s.a = %p\n", &s.a);
	printf("Address of s.b = %p\n", &s.b);
	printf("Address of s.c = %p\n", &s.c);
	printf("Address of s.d = %p\n", &s.d);
	
	printf("offsetof(struct A, a) = %d\n", offsetof(struct A, a));
	printf("offsetof(struct A, b) = %d\n", offsetof(struct A, b));
	printf("offsetof(struct A, c) = %d\n", offsetof(struct A, c));
	printf("offsetof(struct A, d) = %d\n", offsetof(struct A, d));
	
	return 0;
}

// 运行结果
sizeof(A) = 12
Address of s = 000000000062FE10
Address of s.a = 000000000062FE10
Address of s.b = 000000000062FE14
Address of s.c = 000000000062FE18
Address of s.d = 000000000062FE1A
offsetof(struct A, a) = 0
offsetof(struct A, b) = 4
offsetof(struct A, c) = 8
offsetof(struct A, d) = 10

当我们调整了 成员结构的先后位置,使得字节填充的机会减少,这样就会减少浪费的空间


补充

标准库是由一个组织定义的,但是各个编译器厂商并不一定和标准库完全一致,上面我所贴出的源代码也就就只是某一种编译器的,但是,无论是何种编译器,最后都会遵守标准库的标准,例如 size_t 类型,无论被定义成何种类型,都一定是无符号类型的整数类型,ptrdiff_t 类型同理,也一定是一种有符号的整数类型, NULL 无论是否定义为 0 ,在特定的环境下都一定表示空指针的含义,即什么也不指向。

因此,大家更重要的事要理解其含义及学会使用即可

标签:__,常用,struct,endif,Address,C语言,printf,offsetof,定义
From: https://www.cnblogs.com/clannad-summer/p/17684256.html

相关文章

  • 【C语言高阶篇】结构体 —— 什么是内存对齐?
    (文章目录)前言  <fontcolor=green>......
  • Microsoft SQL Server Management Studio 常用的快捷键不见了
    问题安装SSMS18.1后,发现隐藏/显示结果窗格的快捷键Ctrl+R没法用了分析当Ctrl+R时,状态栏看到有反应,说明快捷键默认被转给其他功能了。翻一翻菜单,发现是这里用了,如图。解决移除已有的快捷键为结果窗格设置设置快捷键......
  • Eclipse开发经典教程:常用快捷键
    window——preferences...——(General——keys)或者直接输入key(即可看到keys)编辑相关快捷键  Eclipse的编辑功能非常强大,掌握了Eclipse快捷键功能,能够大大提高开发效率。Eclipse中有如下一些和编辑相关的快捷键。  1.【ALT+/】  此快捷键为用户编辑的好帮手,能为用户......
  • JBPM常用一些api中的方法
    启动流程:Ø获取最新的流程定义:JbpmContext.getCurrentJbpmContext().getGraphSession().findLatestProcessDefinitions();//返回ListØ获取指定流程定义:ProcessDefinitionprocessDefinition=graphSession.loadProcessDefinition(processDefinitionId);Ø创建流程实例:Pr......
  • 掌握Java常用机制,让你的代码更优雅
    Java是一种面向对象的编程语言,它提供了许多常用的机制来简化编程过程。以下是Java中一些常用的机制:异常处理机制:Java中的异常处理机制可以帮助程序员捕获和处理程序运行时的错误。通过使用try-catch语句块,程序员可以捕获特定类型的异常,并在发生异常时执行相应的代码。例如:try{......
  • 定义变量的三个特征
    定义变量的三个特征定义变量的三个特征分别为:获取变量值的内存地址、查看变量值的数据类型、打印变量值height=180#变量名(描述性质)#获取变量值的内存地址,针对变量值print(id(height))#数据类型,针对变量值print(type(height))#int类型(integer整型)name='nick'print(t......
  • 音视频开发常用工具
    一、vlc播放各种音视频文件、网络串流、亦可作为服务器使用二、MediInfo分析视频和音频文件的编码和内容信息1.获取多媒体文件信息MediaInfo可以获取的多媒体文件的基本信息,具体包括以下几方面。(1)内容信息:标题、作者、专辑名、音轨号、日期、总时间等。(2)视频:编码器、宽高比......
  • Android自定义APP字体
    使用Android设备自带的字体时:<!--在你的APP主题或者需要用到的地方的主题中设置样式--><stylename="CustomStyle"parent="AppBaseTheme"><itemname="android:textViewStyle">@style/CustomFontStyleText</item><itemname="and......
  • uniapp项目实践总结(十一)自定义网络检测组件
    导语:很多时候手机设备会突然没网,这时候就需要一个网络检测组件,在没网的时候显示提示用户,提供用户体验。目录准备工作原理分析组件实现实战演练案例展示准备工作在components新建一个q-online文件夹,并新建一个q-online.vue的组件;按照前一篇所说的页面结构,编写好预......
  • STM32 HAL常用库函数
    1.设置GPIO引脚函数函数名称:voidHAL_GPIO_WritePin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin,GPIO_PinStatePinState);第一个参数:GPIOA,B,C,D…(取决于单片机型号)第二个参数:可以取GPIO_PIN_0~GPIO_PIN_15第三个参数:GPIO_PIN_SET(置高电平),GPIO_PIN_RESET(置低电平)例:HAL_GPIO_Writ......