C语言的Struct Hack笔记

最近在搞Compiler的CodeGenerator实验,有一部分需要把Java程序翻译成C程序,比如:


代码如下:

int [] array;
array = new int[10];
System.out.println(array.length); //10

这段代码翻译成C很自然的想法是:


代码如下:

int * array; // int array[] not support in C
array = (int*)malloc(sizof(int)*10);
printf("%d\n",sizof(array)/sizeof(int)); // 1

但很可惜这样是错误的,因为malloc操作在堆上分配空间,不一定是连续的,sizof(array)得到的是指针本身所占的单元,和sizeof(int)相等,无法通过sizof求得数组长度。它和下面还不一样:


代码如下:

int array[10];
printf("%d\n",sizof(array)/sizeof(int)); // 10

这里array是数组,是指向整个连续存储空间的常量,所以sizeof对其操作求得的是整个区域的长度。但是当数组名作为函数的参数传递时,数组就退化为指针,又回到了刚才问题。

我们应该怎么做?

在 StackOverflow 搜了一下,发现ANSI C根本没有直接办法通过指向内存的指针求得分配长度。但Windows下提供了计算指针指向的内存大小的方法[malloc.h]:

_msize : returns the size (in bytes) as an unsigned integer.


代码如下:

size_t _msize(
void *memblock
);

但由于操作系统策略的原因,实际分配到的大小可能会比指定的大一些.

在Linux下,指针往前偏移一个整形大小的单元也会记录实际分配的大小,我们来窥探一下那个单元的内容:


代码如下:

//test.c
int main(){
 int * p;
 int i;
 int size;
 for (i=1;i<11;i++)
 printf("%d ",i);
 printf("\n");
 for (i=0;i<10;i++){
 p = (int*)malloc(sizeof(int)*i);
 size = *(int*)((char*)p-sizeof(int));
 printf("size:%d ",size);
 free(p);
 }
 printf("\n");
}

$gcc test.c
$./a.out
1  2  3  4  5  6  7  8  9  10
17 17 17 17 25 25 33 33 41 41

看来Linux的分配策略不能使得内存大小和元素个数一一对应,此法不可用。 后来发现在Linux下原来也有类似_msize的函数[malloc.h]:

代码如下:

int * array;
int size;
array = (int*)malloc(sizof(50);
size = malloc_usable_size(array);
printf("%d\n",size);//50

但是malloc.h不属于标准C,我们还要继续寻找通用之法。经过大量查阅,终于发现了一种code trick,称作struct-hack. 前面提到过,在C语言中,int a[]是违法的,但是把它作为struct的最后一个成员却是可以的:


代码如下:

typedef struct array{
 int size;
 int free;
 int buf[];
 }array,*Tiger_array;

这是在C语言的后期加入的特性,目的就是为了实现flexible array, 这样每次给数组分配空间时,需要同步记录size大小。而求size的时候,直接取出来即可:


代码如下:

Tiger_array ta;
ta = (int*)malloc(sizeof(array)+100);
ta->size = 100;
ta->free = 0;

需要注意一点,这时分配的大小应该是sizeof(struct)加上需求的数组大小。

这个问题就说到这里。

(0)

相关推荐

  • C语言中char*和char[]用法区别分析

    本文实例分析了C语言中char* 和 char []的区别.分享给大家供大家参考之用.具体分析如下: 一般来说,很多人会觉得这两个定义效果一样,其实差别很大.以下是个人的一些看法,有不正确的地方望指正. 本质上来说,char *s定义了一个char型的指针,它只知道所指向的内存单元,并不知道这个内存单元有多大,所以: 当char *s = "hello";后,不能使用s[0]='a':语句进行赋值.这是将提示内存不能为"written". 当用char s[]=&q

  • c语言字符数组与字符串的使用详解

    1.字符数组的定义与初始化字符数组的初始化,最容易理解的方式就是逐个字符赋给数组中各元素.char str[10]={ 'I',' ','a','m',' ','h','a','p','p','y'};即把10个字符分别赋给str[0]到str[9]10个元素如果花括号中提供的字符个数大于数组长度,则按语法错误处理:若小于数组长度,则只将这些字符数组中前面那些元素,其余的元素自动定为空字符(即 '\0' ). 2.字符数组与字符串在c语言中,将字符串作为字符数组来处理.(c++中不是)在实际应用

  • C语言变长数组 struct中char data[0]的用法详解

    今天在看一段代码时出现了用结构体实现变长数组的写法,一开始因为忘记了这种技术,所以老觉得作者的源码有误,最后经过我深思之后,终于想起以前看过的用struct实现变长数组的技术.下面是我在网上找到的一篇讲解很清楚的文章. 在实际的编程中,我们经常需要使用变长数组,但是C语言并不支持变长的数组.此时,我们可以使用结构体的方法实现C语言变长数组. struct MyData { int nLen; char data[0];}; 在结构中,data是一个数组名:但该数组没有元素:该数组的真实地址紧随结

  • C语言的Struct Hack笔记

    最近在搞Compiler的CodeGenerator实验,有一部分需要把Java程序翻译成C程序,比如: 复制代码 代码如下: int [] array;array = new int[10];System.out.println(array.length); //10 这段代码翻译成C很自然的想法是: 复制代码 代码如下: int * array; // int array[] not support in Carray = (int*)malloc(sizof(int)*10);printf

  • c语言内存泄露示例解析

    正确的内存管理的重要性存在内存错误的 C 和 C++ 程序会导致各种问题.如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行:如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击.从 1988 年著名的莫里斯蠕虫 攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关:"大多数计算机安全漏洞都是缓冲区溢出",Rodney Bates 在 2004 年写道. 在可以使用 C 或 C++ 的地方,也广泛支持使用其他许多通用语言(如 Java™.

  • 浅析c与c++中struct的区别

    这里有两种情况下的区别.(1)C的struct与C++的class的区别.(2)C++中的struct和class的区别.在第一种情况下,struct与class有着非常明显的区别.C是一种过程化的语言,struct只是作为一种复杂数据类型定义,struct中只能定义成员变量,不能定义成员函数(在纯粹的C语言中,struct不能定义成员函数,只能定义变量).例如下面的C代码片断: 复制代码 代码如下: struct Point        {                int x; //

  • C++在C语言基础之上增强的几个实用特性总结

    变量的定义 C语言中的变量都必须在作用域开始的位置定义!!  C++中更强调语言的"实用性",所有的变量都可以在需要使用时再定义. #include <iostream> using namespace std; int main11() { int i = 0; printf("ddd"); int k; // 这段代码在vc6,C语言编译情况下就会报错.就是因为这里的定义 system("pause"); return 0; }

  • C++ class和struct到底有什么区别详解

    C++ 中保留了C语言的 struct 关键字,并且加以扩充.在C语言中,struct 只能包含成员变量,不能包含成员函数.而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数. C++中的 struct 和 class 基本是通用的,唯有几个细节不同: 使用 class 时,类中的成员默认都是 private 属性的:而使用 struct 时,结构体中的成员默认都是 public 属性的. class 继承默认是 private 继承,而 struct 继承默

  • 一文读懂c语言结构体在单片机中的应用

    Struck 看到单片机中有很多struck 的应用,但是呢我当初学C语言的时候又没有很认真的去学习,今天复习下,写一篇小小的交流,希望能够给大家带来帮助. 1.struck的定义 /***********方式一**********/ struct Book { char title[128]; char aurhor[40]; float price; unsigned int date; char pubilsher[40]; }; /*定义了Book这个模板*/ struct Book b

  • Go语言映射内部实现及基础功能实战

    目录 写在前面 映射的内部实现和基础功能 内部实现 创建和初始化 使用映射 在函数间传递映射 写在前面 嗯,学习GO,所以有了这篇文章博文内容为<GO语言实战>读书笔记之一主要涉及映射相关知识 你要爱就要像一个痴情的恋人那样去爱,像一个忘死的梦者那样去爱,视他人之疑目如盏盏鬼火,大胆去走你的夜路.——史铁生<病隙碎笔> 映射的内部实现和基础功能 映射是一种数据结构,是用于存储一系列无序的键值对.类比Java里的Map,Python里的字典,可以理解为以哈希值做索引,期望索引可以在一

  • C++与C语言常用的语法对比

    目录 前言 1.头文件 2.结构体struct 3.动态数组的创建与删除 4.函数顺序问题 5.类(class) 前言 本人在校学习的第一门语言是C++,由于操作系统这门课程实验的需要,要求在linux下使用GCC编译器编译C程序代码,为了写代码的方便,本人先采用VS2017写了C++版本的代码,再根据C++和C语言两个语法的不同将程序进行修改成C程序.由于本人没有学过C语言,对C语言的语法也不是很熟悉,写本文的目的是记录下修改过程的遇到的几个注意点,方面以后参考, 1.头文件 c++ #inc

  • go语言中函数与方法介绍

    在C#或者Java里面我们都知道,一个Class是要包含成员变量和方法的,对于GO语言的Struct也一样,我们也可以给Struct定义一系列方法. 一.怎么定义一个方法? Go的方法是在函数前面加上一个接收者,这样编译器就知道这个方法属于哪个类型了.例如: package demo1 import ( "fmt" ) type Student struct { Name string Age int Class string } func (stu Student) GetUserI

  • 关于PHP5和PHP7中数组实现方式的比较总结

    目录 ⒈ 数据结构 ⒉ 添加/修改元素 ⒊ 删除元素 ⒋ 数组遍历 ⒌ hash 碰撞 ⒍ 扩容 ⒎ PHP 7 中的 packed hashtable 总结 从 PHP 5 到 PHP 7 ,PHP 通过对 hashtable 数据结构和实现方式的修改,使得数组在内存占用和性能上有了很大的提升. ⒈ 数据结构 // PHP 5 中 hashtable 的数据结构定义 typedef struct bucket { ulong h; /*对于索引数组,存储 key 的原始值:对于关联数组,存储

随机推荐