Python检查c语言源代码结构体是否对齐

本文介绍了C语言结构体的内存对齐规则,并提到不遵循规则可能导致的错误。作者分享了使用Python编写的一个小工具,用于检查C语言结构体成员是否满足内存对齐要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言的结构体内存对齐又三条原则 如下:

数据成员对齐规则:

结构体的数据成员中,第一个成员从offset为0的地址开始,以后每一个成员存储的起始位置为该成员大小的整数倍

结构体作为成员:

如果一个结构体1作为另一个结构体2的数据成员,则在结构体2中结构体1要从1内部成员最大的整数倍地址开始存储。

结构体的总大小(sizeof):

为该结构体内部最大基本类型的整数倍,不足的要补齐,而不是简单的所有成员的大小总和。

如果结构体不满足对齐规则,编译器会又自动补齐机制(除非强制对齐.)

这个问题会导致在你以后操作中的一些不可预料错误. 


最近学习python时自己制作了一个小工具检查结构体成员是否对齐.代码如下:

import traceback

listScannedFile = []
dirtScannedMacro = {}
listDefinedMacro = []

class ReadFileEndError(Exception):
	"""Read File End"""
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)

def ScanStructInit():
	dirtScannedMacro['unsigned int'] = 4
	dirtScannedMacro['unsigned char'] = 1
	dirtScannedMacro['int'] = 4
	dirtScannedMacro['char'] = 1
	dirtScannedMacro['short'] = 2
	dirtScannedMacro['unsigned short'] = 2

def getCount(strLine):
	try:
		while True:
			pos = strLine.find('sizeof(')
			if -1 != pos:
				rpos = strLine.find(')', pos+7)
				strMacro = strLine[pos+7:rpos]
				strLine = strLine[0:pos] + str(dirtScannedMacro[strMacro]) + strLine[rpos+1:]
			else:
				break

		listStrMacro = []
		strTemp = ""
		bIsLeftFind = 0
		for i in range(len(strLine)):
			if not bIsLeftFind:
				if strLine[i].isalpha() or '_' == strLine[i]:
					strTemp += strLine[i]
					bIsLeftFind = 1
			else:
				if strLine[i].isalpha() or strLine[i].isdigit() or '_' == strLine[i]:
					strTemp += strLine[i]
				else:
					bIsLeftFind = 0
					listStrMacro.append(strTemp)
					strTemp = ""

		if 1 == bIsLeftFind:
			listStrMacro.append(strTemp)
			strTemp = ""
		
		
		for strTemp in listStrMacro:
			strLine = strLine.replace(strTemp, str(dirtScannedMacro[strTemp]))
		
		return eval(strLine)

	except:
		traceback.print_exc()

def ReadLineNotSpace(fileHandle):
	try:
		while True:
			strLine = fileHandle.readline()
			if not strLine:
				raise ReadFileEndError('Read File Finish')
		
			strLine = strLine[0:strLine.find(r'//')].rstrip()
			if strLine:
				return strLine
	except:
		raise

def ReadLineNoNote(fileHandle):
	try:
		while True:
			strLine = ReadLineNotSpace(fileHandle)

			while True:
				if not strLine.strip():
					break

				posLeft = strLine.find(r'/*')
				if -1 == posLeft:
					return strLine.rstrip()

				strNoteLine = strLine[posLeft+2:]
				strLine = strLine[0:posLeft]

				while True:
					posRight = strNoteLine.find(r'*/')
					if -1 == posRight:
						strNoteLine = ReadLineNotSpace(fileHandle)
					else:
						strLine += strNoteLine[posRight+2:]
						break
	except:
		raise

def ReadACompleteLine(fileHandle):
	try:
		strLine = ReadLineNoNote(fileHandle)
		while True:
			if '\\' != strLine[-1]:
				return strLine.strip()
			else:
				strLine = strLine.rstrip('\\')
				strLine +=  ReadLineNoNote(fileHandle)
	except:
		raise

def ScanInclude(strLine):
	try:
		pos = strLine.find('#include', 0, 8)
		if -1 == pos:
			return

		strLine = strLine[8:].strip()
		if '"' != strLine[0] and '<' != strLine[0]:
			return

		strLine = strLine[1:-1]
		if strLine:
			ScanFile(strLine)
	
	except:
		traceback.print_exc()

def ScanDefine(strLine):
	try:
		pos = strLine.find('#define', 0, 7)
		
		if -1 == pos:
			return
	
		if ' ' != strLine[7] and '\t' != strLine[7]:
			return

		strLine = strLine[7:].strip()
		strLine = ' '.join(strLine.split())
		pos = strLine.find(' ')
		
		if -1 == pos:
			listDefinedMacro.append(strLine)
		else:
			marcoName  = strLine[0:pos]
			marcoValue = strLine[pos+1:]
			if marcoValue in dirtScannedMacro:
				dirtScannedMacro[marcoName] = dirtScannedMacro[marcoValue]
			else:
				dirtScannedMacro[marcoName] = marcoValue

	except:
		traceback.print_exc()

def ScanEnum(strLine, fileHandle):
	try:
		pos = strLine.find('typedef enum', 0, 12)
		if -1 == pos:
			return

		strLine = strLine[12:].strip()
		while True:
			pos = strLine.find('{')
			if -1 == pos:
				strLine = ReadACompleteLine(fileHandle)
			else:
				strLine = strLine[pos+1:].strip()
				break
		while True:
			pos = strLine.find('}')
			if -1 == pos:
				strLine += ReadACompleteLine(fileHandle)
			else:
				strLine = strLine[0:pos].strip()
				break

		enumList = strLine.split(',')
		iValue = 0
		for strEnum in enumList:
			pos = strEnum.find('=')
			if -1 == pos:
				dirtScannedMacro[strEnum.strip()] = iValue
			else:
				strName = strEnum[0:pos].strip()
				strValue = strEnum[pos+1:].strip()
				if strValue in dirtScannedMacro:
					dirtScannedMacro[strName] = dirtScannedMacro[strValue]
					iValue = int(dirtScannedMacro[strValue])
				else:
					iValue = int(strValue)
					dirtScannedMacro[strName] = iValue
				iValue += 1
		return

	except ReadFileEndError:
		raise

	except:
		traceback.print_exc()

def AnalysisStruct(listSize, listCount):
	try:
		iSum = 0
		for i in range(len(listSize)):
			if iSum:
				iSum += listSize[i]*listCount[i]
				continue
			else:
				if listSize[i] > 8 and 0 != iSum % 8:
					return i
				if 0 != iSum % listSize[i]:
					return i
				
				iSum += listSize[i]*listCount[i]
		if 0 != (iSum % 8):
			return -1
		return 0
	except:
		traceback.print_exc()

def ScanStruct(strLine, fileHandle):
	try:
		pos = strLine.find('typedef struct', 0, 14)
		if -1 ==  pos:
			return
		strLine = strLine[14:]
		while True:
			pos = strLine.find('{')
			if -1 == pos:
				strLine = ReadACompleteLine(fileHandle)
			else:
				strLine = strLine[pos+1:].strip()
				break
		while True:
			pos = strLine.find('}')
			if -1 == pos:
				strLine += ReadACompleteLine(fileHandle)
			else:
				strLine = strLine[0:pos].strip()
				break

		listStruct = strLine.split(';')
		listStructSize = []
		listStructCount = []
		for strStruct in listStruct:
			strStruct = strStruct.strip()
			if not strStruct:
				continue
			
			strName  = strStruct.split()[0]
			strValue = strStruct[len(strName):].strip()

			if  '*' == strName[-1] or '*' == strValue[0]:
				listStructSize.append(8)
			else:
				listStructSize.append(int(dirtScannedMacro[strName]))

			pos = strValue.find('[')
			if -1 == pos:
				listStructCount.append(1)
			else:
				iCount = getCount(strValue[pos+1:-1].strip())
				if iCount <= 0:
					print("Struct Error iCount:%d" % (iCount))
					return
				listStructCount.append(iCount)

		iRet = AnalysisStruct(listStructSize, listStructCount)
		if 0 != iRet:
			print('Error Struct. iRet:%d' % iRet)
		
	except ReadFileEndError as ex:
		raise
	except:
		traceback.print_exc()

def ScanFile(fileName):
	if fileName in listScannedFile:
		return 0
	print("ScanFile %s" % (fileName))
	
	listScannedFile.append(fileName)
	try:
		fileHandle = open(fileName, 'r')
		while True:
			strLine = ReadACompleteLine(fileHandle)
			#print("read a line:%s, %d" % (strLine, len(strLine)))
			
			ScanInclude(strLine)
			ScanDefine(strLine)
			ScanEnum(strLine, fileHandle)
			ScanStruct(strLine, fileHandle)

	except ReadFileEndError as ex:
		print("Scan File %s Finished!" % (fileName))
		fileHandle.close()
	except IOError as ex:
		pass
	except:
		traceback.print_exc()
	return 0


if __name__ == '__main__':
	ScanStructInit()
	ScanFile('main.c')



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值