Arrays of Length Zero 长度为0的数组的使用

文章讨论了C语言中GNU扩展的零长度数组特性,它不占用结构体的大小,常用于表示可变长对象的头部。相比于使用指针,零长度数组可以简化内存管理和释放,避免内存泄漏。文中通过示例代码对比了两种方法的差异,并给出了在实际应用中的例子,如bluez源码中的用法。

        声明零长度数组是 GNU 对标准 C 的扩展,可以参考这里 Zero-Length 。若要在一个结体体中声明一个 0 长度数组则这个声明必须放在结构体的最后,如:

struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
};

这个0长度数组的声明是不占用所在结构体的大小的,如:
 

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
        uint16_t dev_id;
        uint32_t dev_opt;
};

struct hci_dev_list_req {
        uint16_t dev_num;
        struct hci_dev_req dev_req[0];  /* hci_dev_req structures */
};

int main()
{
        printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
        printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));
        return 0;
}

sizeof(hci_dev_list_req) 的大小只是计算了 uint16_t dev_num 的大小,因为默认是4字节对齐,所以这里sizeof 结果为 4。在上面的链接中有这样一句话:Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero。

那这样声明有什么好处呢?

A zero-length array can be useful as the last element of a structure that is really a header for a variable-length object。翻译:零长度数组可以用作结构的最后一个元素,该元素实际上是可变长度对象的头。它的好处就是用于可变长对象,那如果用指针代替是不是也可以实现可变长对象呢?我觉得是可以的,如可以这样声明:

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
        uint16_t dev_id;
        uint32_t dev_opt;
};

struct hci_dev_list_req {
        uint16_t dev_num;
        struct hci_dev_req *dev_req;    /* hci_dev_req structures */
};

int main()
{
        printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
        printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));
        return 0;
}

此时 sizeof(hci_dev_list_req) = 16,因为在64 位系统下一个指针占用 8 byte 大小,这里应该是按8 byte 对齐了,所以为16,如果设置成 4 byte 对齐,那应该为 12,如:

 

可以看到仅仅是换成了指针,数据结构的长度就增加了 12 byte。而且在实际使用的时候,这个指针可能指向的是 malloc 分配的内存空间,即:

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

#pragma pack(4)
struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req *dev_req;    /* hci_dev_req structures */
};
#pragma pack()

int main()
{
	printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
	printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));

	int num = 16;//
	struct hci_dev_list_req dl;

	dl.dev_req = (struct hci_dev_req*)malloc(sizeof(struct hci_dev_req) * num);

	/* do something*/

	free(dl.dev_req);

	return 0;
}

 使用malloc 或 new 分配的内存,你还必须进行手动释放,否则就会造成内存泄漏。那使用0长度数组的好处呢?看下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma pack (1)
typedef struct private_data {
    char tag[4];
    int version;
    char type;
    char data[0];
}PrivateData;

#pragma pack ()


int main()
{
    printf("sizeof PrivateData = %lu\n", sizeof(PrivateData)); // 9 bytes

	/*这样申请内存后,sendBuf->data 就指向了 sendBuf+sizeof(PrivateData) 的位置,
	* 即可以存放数据的内存的头了,这样做的好处是只需要释放一次 free(sendBuf) 即可。
	*/
	size_t totalMem = sizeof(PrivateData) + 256;
	PrivateData *sendBuf = (PrivateData*)malloc(totalMem);
	memset(sendBuf, 0, totalMem);
	
    sendBuf->tag[0] = 'D';
    sendBuf->tag[1] = 'H';
    sendBuf->tag[2] = 'A';
    sendBuf->tag[3] = 'V';
    sendBuf->version = 0x10;
    sendBuf->type = 0x11;
    memcpy(sendBuf->data, "hello world", 11);
    
    printf("sendBuf = %c, %c, %c, %c, data[0] = %s\n", sendBuf->tag[0], sendBuf->tag[1], sendBuf->tag[2], sendBuf->tag[3], sendBuf->data);

	//这里只要释放sendBuf就可以
	free(sendBuf);
    return 0;
}

使用0长度数组的好处就是实现了可变长对象,方便内存的申请和释放。

在 bluez 源码中就有这种用法,如下:

//有类型声明如下
struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
};


static void print_dev_list(int ctl, int flags)
{
	struct hci_dev_list_req *dl;
	struct hci_dev_req *dr;
	int i;

    /* 申请了一大块内存 */
	if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
		sizeof(uint16_t)))) {
		perror("Can't allocate memory");
		exit(1);
	}
	dl->dev_num = HCI_MAX_DEV;
	dr = dl->dev_req;/* 这里指向了数据位置 */

	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
		perror("Can't get device list");
		free(dl);
		exit(1);
	}

    /* 这里操作实际的设备个数  */
	for (i = 0; i< dl->dev_num; i++) {
		di.dev_id = (dr+i)->dev_id;
		if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
			continue;
		print_dev_info(ctl, &di);
	}

	free(dl);
}

 

import java.util.Arrays; /** * 大整数类,支持符号和任意精度的整数运算 * 使用倒序数组存储数字(个位在索引0位置) */ public class testBigInteger { private int[] digits; // 数字的倒序数组表示 private boolean isNegative; // 符号标志:true表示负数,false表示正数 /** * 构造函数 - 从字符串构造大整数 * @param numberStr 数字字符串,可包含正负号 */ public testBigInteger(String numberStr) { if (numberStr == null || numberStr.isEmpty()) { throw new IllegalArgumentException("Number string cannot be null or empty"); } // 处理符号 if (numberStr.charAt(0) == '-') { this.isNegative = true; numberStr = numberStr.substring(1); } else if (numberStr.charAt(0) == '+') { this.isNegative = false; numberStr = numberStr.substring(1); } else { this.isNegative = false; } // 去除前导零 numberStr = removeLeadingZeros(numberStr); if (numberStr.isEmpty()) { // 如果去除前导零后为空,说明是0 this.digits = new int[]{0}; this.isNegative = false; // 0没有负号 return; } // 转换为倒序数组 this.digits = stringToReverseArray(numberStr); } /** * 构造函数 - 从倒序数组和符号构造大整数 * @param digits 数字的倒序数组 * @param isNegative 是否为负数 */ public testBigInteger(int[] digits, boolean isNegative) { this.digits = removeLeadingZeros(digits); this.isNegative = isNegative; // 如果是0,确保不是负数 if (isZero()) { this.isNegative = false; } } /** * 拷贝构造函数 * @param other 另一个大整数 */ public testBigInteger(testBigInteger other) { this.digits = Arrays.copyOf(other.digits, other.digits.length); this.isNegative = other.isNegative; } //大数法乘法 public testBigInteger multipy(testBigInteger other){ int[] result = multipyBigInteger(this.digits,other.digits); if(this.isNgeative == other.isNegative) { this.isNegative = false;} else this.isNegative = true; return testBigInteger(result,this.isNegative); } /** * 大整数加法 * @param other 另一个大整数 * @return 两个大整数的和 */ public testBigInteger add(testBigInteger other) { // 同号相加 if (this.isNegative == other.isNegative) { int[] resultDigits = addAbsolute(this.digits, other.digits); return new testBigInteger(resultDigits, this.isNegative); } // 异号相加转换为减法 if (compareAbsolute(this.digits, other.digits) >= 0) { int[] resultDigits = subtractAbsolute(this.digits, other.digits); return new testBigInteger(resultDigits, this.isNegative); } else { int[] resultDigits = subtractAbsolute(other.digits, this.digits); return new testBigInteger(resultDigits, other.isNegative); } } /** * 大整数减法 * @param other 另一个大整数 * @return 两个大整数的差 */ public testBigInteger subtract(testBigInteger other) { // 转换为加上相反数 testBigInteger negatedOther = new testBigInteger(other.digits, !other.isNegative); return this.add(negatedOther); } /** * 判断是否为零 * @return 如果是零返回true,否则返回false */ public boolean isZero() { for (int digit : digits) { if (digit != 0) return false; } return true; } /** * 获取符号 * @return 如果是负数返回-1,如果是正数返回1,如果是0返回0 */ public int signum() { if (isZero()) return 0; return isNegative ? -1 : 1; } /** * 取相反数 * @return 当前大整数的相反数 */ public testBigInteger negate() { if (isZero()) { return this; } return new testBigInteger(this.digits, !this.isNegative); } /** * 取绝对值 * @return 当前大整数的绝对值 */ public testBigInteger abs() { if (!isNegative) { return this; } return new testBigInteger(this.digits, false); } /** * 比较两个大整数的大小 * @param other 另一个大整数 * @return 如果当前数大于other返回1,等于返回0,小于返回-1 */ public int compareTo(testBigInteger other) { if (this.isNegative && !other.isNegative) return -1; if (!this.isNegative && other.isNegative) return 1; int absCompare = compareAbsolute(this.digits, other.digits); if (this.isNegative) { return -absCompare; } else { return absCompare; } } /** * 转换为字符串表示 * @return 大整数的字符串表示 */ @Override public String toString() { if (isZero()) { return "0"; } StringBuilder sb = new StringBuilder(); if (isNegative) { sb.append('-'); } // 从最高位开始输出 for (int i = digits.length - 1; i >= 0; i--) { sb.append(digits[i]); } return sb.toString(); } /** * 判断是否相等 * @param obj 要比较的对象 * @return 如果相等返回true,否则返回false */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; testBigInteger other = (testBigInteger) obj; return this.isNegative == other.isNegative && Arrays.equals(this.digits, other.digits); } // ========== 私有辅助方法 ========== private static int[] multipyBigInteger(int[]a,int[]b){ int[] result = int [ a.length + b.length]; for(int i = 0;i<a.length;i++){ for(int j = 0 ; j<b.length;j++) { result[i+j]+=a[i]*b[j]; } } int carry = 0; for(int i =0;i<result.length;i++) { result[i]+=carry; total = result[i]; carry = total/10; result[i] = total%10; } return result; } /** * 绝对值加法 */ private static int[] addAbsolute(int[] a, int[] b) { int maxLen = Math.max(a.length, b.length); int[] result = new int[maxLen + 1]; int carry = 0; for (int i = 0; i < maxLen; i++) { int digitA = (i < a.length) ? a[i] : 0; int digitB = (i < b.length) ? b[i] : 0; int sum = digitA + digitB + carry; result[i] = sum % 10; carry = sum / 10; } if (carry > 0) { result[maxLen] = carry; return result; } else { return Arrays.copyOf(result, maxLen); } } /** * 绝对值减法 (a >= b) */ private static int[] subtractAbsolute(int[] a, int[] b) { int maxLen = Math.max(a.length, b.length); int[] result = new int[maxLen]; int borrow = 0; for (int i = 0; i < maxLen; i++) { int digitA = (i < a.length) ? a[i] : 0; int digitB = (i < b.length) ? b[i] : 0; int diff = digitA - digitB - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } result[i] = diff; } return removeLeadingZeros(result); } /** * 比较两个绝对值的大小 */ private static int compareAbsolute(int[] a, int[] b) { int lenA = a.length; int lenB = b.length; if (lenA > lenB) return 1; if (lenA < lenB) return -1; // 长度相等,从最高位开始比较 for (int i = lenA - 1; i >= 0; i--) { if (a[i] > b[i]) return 1; if (a[i] < b[i]) return -1; } return 0; // 相等 } /** * 去除数组前导零 */ private static int[] removeLeadingZeros(int[] arr) { int i = arr.length - 1; while (i > 0 && arr[i] == 0) { i--; } return Arrays.copyOf(arr, i + 1); } /** * 去除字符串前导零 */ private static String removeLeadingZeros(String str) { int i = 0; while (i < str.length() - 1 && str.charAt(i) == '0') { i++; } return str.substring(i); } /** * 字符串转换为倒序数组 */ private static int[] stringToReverseArray(String numStr) { int[] arr = new int[numStr.length()]; for (int i = 0; i < numStr.length(); i++) { char c = numStr.charAt(i); if (c < '0' || c > '9') { throw new IllegalArgumentException("Invalid digit: " + c); } arr[numStr.length() - 1 - i] = c - '0'; } return arr; } // ========== 测试代码 ========== public static void main(String[] args) { // 测试基本功能 testBasicOperations(); // 测试大数运算 testLargeNumberOperations(); // 测试边界情况 testEdgeCases(); } private static void testBasicOperations() { System.out.println("=== 基本功能测试 ==="); testBigInteger a = new testBigInteger("123"); testBigInteger b = new testBigInteger("456"); // 加法测试 testBigInteger sum = a.add(b); System.out.println(a + " + " + b + " = " + sum); // 减法测试 testBigInteger diff = b.subtract(a); System.out.println(b + " - " + a + " = " + diff); // 符号测试 testBigInteger negativeA = new testBigInteger("-123"); System.out.println("负号测试: " + negativeA); // 比较测试 System.out.println(a + " compareTo " + b + " = " + a.compareTo(b)); System.out.println(b + " compareTo " + a + " = " + b.compareTo(a)); System.out.println(a + " compareTo " + a + " = " + a.compareTo(a)); System.out.println(); } private static void testLargeNumberOperations() { System.out.println("=== 大数运算测试 ==="); String num1 = "9876543210123456789"; String num2 = "1234567890123456789"; testBigInteger a = new testBigInteger(num1); testBigInteger b = new testBigInteger(num2); System.out.println("数字1: " + a); System.out.println("数字2: " + b); // 加法 testBigInteger sum = a.add(b); System.out.println("加法结果: " + sum); // 减法 testBigInteger diff1 = a.subtract(b); testBigInteger diff2 = b.subtract(a); System.out.println("减法结果1: " + diff1); System.out.println("减法结果2: " + diff2); // 负数运算 testBigInteger negativeA = new testBigInteger("-" + num1); testBigInteger negativeB = new testBigInteger("-" + num2); System.out.println("负数加法: " + negativeA + " + " + negativeB + " = " + negativeA.add(negativeB)); System.out.println("负数减法: " + negativeA + " - " + negativeB + " = " + negativeA.subtract(negativeB)); System.out.println(); } private static void testEdgeCases() { System.out.println("=== 边界情况测试 ==="); // 零测试 testBigInteger zero = new testBigInteger("0"); testBigInteger num = new testBigInteger("123"); System.out.println("零加法: " + zero + " + " + num + " = " + zero.add(num)); System.out.println("零减法: " + num + " - " + zero + " = " + num.subtract(zero)); System.out.println("零的符号: " + zero.signum()); // 负零测试(应该自动转为正零) testBigInteger negativeZero = new testBigInteger("-0"); System.out.println("负零自动转为: " + negativeZero); // 前导零测试 testBigInteger withLeadingZeros = new testBigInteger("00000123"); System.out.println("前导零处理: " + withLeadingZeros); // 绝对值测试 testBigInteger negativeNum = new testBigInteger("-456"); System.out.println("绝对值: " + negativeNum.abs()); // 相反数测试 System.out.println("相反数: " + num.negate() + ", " + negativeNum.negate()); System.out.println(); } }帮我查看我的大整数乘法是否有错误,并且帮我生成对应的测试用例
最新发布
11-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值