https://blog.csdn.net/weixin_48896613/article/details/127371045(内存对齐)

简介:C语言命名规范,内存管理与错误处理,C语言数据类型与内存布局,C语言数据类型转换与精度丢失。

微软命名规范

微软(Microsoft)有严格的编程命名规范,以确保代码的可读性和一致性。以下是一些主要的命名规范和最佳实践:

一般原则

  1. 可读性:命名应简洁明了,便于理解。

  2. 一致性:保持命名风格的一致性,遵循团队和项目的规范。

  3. 描述性:命名应描述变量、函数、类等的用途或功能。

类、接口和结构(都使用驼峰体)

  • 类名:使用 PascalCase(首字母大写的驼峰式),例如 CustomerOrder

  • 接口名:使用 PascalCase,并以 I 开头,例如 ICustomerRepository

  • 结构名:与类名相同,使用 PascalCase,例如 CustomerRecord

方法和属性

  • 方法名:使用 PascalCase,方法名应为动词或动词短语,例如 CalculateTotal

  • 属性名:使用 PascalCase,属性名应为名词或名词短语,例如 OrderDate

变量和字段

  • 局部变量:使用 camelCase(首字母小写的驼峰式),例如 orderTotal

  • 私有字段:使用 camelCase,并以下划线 _ 开头,例如 _customerName

常量

  • 常量名:使用 PascalCase,例如 MaxOrderQuantity。对于常量,所有字母都大写并使用下划线分隔单词,例如 MAX_ORDER_QUANTITY

参数

  • 参数名:使用 camelCase,参数名应为描述性名词,例如 customerName

其他

  • 缩写和首字母缩略词:当缩写在标识符中时,使用 PascalCase 或 camelCase 处理,例如 XmlParserparseXml

变量

//全局变量前面加g_

示例代码

C#
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
public interface ICustomerRepository { Customer GetCustomerById(int customerId); void AddCustomer(Customer customer); } public class CustomerOrder { private string _customerName; private DateTime _orderDate; public string CustomerName { get { return _customerName; } set { _customerName = value; } } public DateTime OrderDate { get { return _orderDate; } set { _orderDate = value; } } public void CalculateTotal() { // Implementation here } }

参考资料

C
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
#include <stdlib.h> #include <stdio.h> int main(){ char szBuf[20] = {0}; scanf("%19[0-9]s", szBuf);//0到9 scanf("%19[0, 9, a]s", szBuf);//只有0 9 a scanf("%19[^8]s", szBuf);//排除8 //scanf的正则 system("pause"); return 0; }

编译器分配局部变量

编译器在分配局部变量时,通常会依据一些规则和优化策略。以下是一些常见的规则和因素:

  1. 栈帧布局: 局部变量通常存储在栈上。每个函数调用都会在栈上创建一个栈帧,栈帧包括局部变量、返回地址和一些其他状态信息。栈帧的大小和布局由编译器决定,通常会根据变量声明的顺序进行分配。

  2. 变量声明顺序: 通常情况下,编译器会按照变量在代码中声明的顺序分配内存。即先声明的变量会被分配到较低的内存地址,而后声明的变量会被分配到较高的内存地址(在栈向下增长的系统中)。

  3. 对齐要求: 编译器会考虑变量的对齐要求。某些类型的数据(如 intdouble 等)需要在特定的内存地址对齐。编译器会插入适当的填充字节(padding)来满足这些对齐要求。

  4. 优化策略: 编译器在优化代码时可能会改变变量的分配顺序和位置。例如,它可能会将不常使用的变量分配到离栈指针更远的位置,或将常用的变量保存在寄存器中,而不是在栈上。

  5. 函数调用约定: 不同的系统和编译器可能有不同的函数调用约定,这些约定会影响栈帧的布局。例如,有些约定要求保存调用者的寄存器,有些约定则要求保存被调用者的寄存器。

  6. 局部变量的生命周期: 编译器会考虑局部变量的生命周期,尽量重用栈空间。比如,一个变量在一个作用域内有效,当离开该作用域时,其占用的栈空间可能会被后续的变量使用。

示例代码

让我们通过一个示例代码来展示局部变量的内存分配:

C
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
#include <stdio.h> void func() { int a = 1; int b = 2; int c = 3; printf("Address of a: %p\n", (void*)&a); printf("Address of b: %p\n", (void*)&b); printf("Address of c: %p\n", (void*)&c); } int main() { func(); return 0; }

编译并运行这段代码,你可能会看到如下输出(地址因系统和编译器不同而不同):

C
  • 01
  • 02
  • 03
Address of a: 0x7ffd5fbff71c Address of b: 0x7ffd5fbff718 Address of c: 0x7ffd5fbff714

在这个例子中,变量 abc 的地址是相邻的,且按照声明的顺序依次递减(假设栈向下增长)。这说明编译器按照变量声明的顺序分配了内存。

内存对齐

https://blog.csdn.net/weixin_48896613/article/details/127371045

一、为什么要内存对齐

内存对齐是为了提高计算机读取数据的效率。对齐地址通常是2、4、8的倍数。

简单来说,就是方便计算机去读写数据。

C
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
对齐的地址一般都是 n(n = 248)的倍数。 (1). 1 个字节的变量,例如 char 类型的变量,放在任意地址的位置上; (2). 2 个字节的变量,例如 short 类型的变量,放在 2 的整数倍的地址上; (3). 4 个字节的变量,例如 floatint 类型的变量,放在 4 的整数倍地址上; (4). 8 个字节的变量,例如 long longdouble 类型的变量,放在 8 的整数倍地址上;

二、基本变量类型所占大小

不同系统下基本变量类型占用字节数不同,如char为1字节,int为4字节,指针在32位系统下为4字节,在64位系统下为8字节等。

三、影响内存对齐的情况

1、变量排列顺序。

2、attribute((packed)):取消变量对齐,按照实际占用字节数对齐(就是让变量之间排列紧密,不留缝隙)。(gcc才支持)

3、#pragma pack (n):让变量强制按照 n 的倍数进行对齐,并会影响到结构体结尾地址的补齐(详见四的通常情况下关于结尾地址补齐的描述)。

四、重要结论

系统默认对齐规则总结:

  • 结构体中间:各结构体的起始地址按类型变量默认规则排列,char 类型除外,通常按 2 的倍数对齐。

  • 结构体最后:根据最大变量类型对齐。例如,int 需按 4 的倍数对齐,double 需按 8 的倍数对齐。

  • 结构体嵌套:子结构体的成员变量起始地址按子结构体中最大变量类型对齐。

  • 数组成员:按一个字节对齐。

  • 联合体成员:按联合体中最大类型的整数倍对齐。