Chinaunix首页 | 论坛 | 博客
  • 博客访问: 288896
  • 博文数量: 72
  • 博客积分: 2387
  • 博客等级: 大尉
  • 技术积分: 720
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-26 10:54
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(1)

2010年(70)

分类:

2010-08-26 12:48:33

1.零长数组代码片段
#include

#include

#include

#include

 

struct helloworld_t

{

    int num;

    char helloworld[0];

};

 

int main()

{

    struct helloworld_t *p;

    unsigned int size = sizeof(struct helloworld_t) + sizeof("Hello World!\n");

    p = (struct helloworld_t *) malloc(size);

    if (!p)

           return -1;

 

    memcpy(p, "\x01\x00\x00\x00Hello World!\n", size);

 

    while (p->num--)

    {

        printf("%s",p->helloworld);

    }

   

    free(p);

    return 0;

}


---零长数组
gcc方言零长数组就是零长数组,有基址没尺寸,不是指针。#include

 

struct stTest

{

    int iA;

    int iB;

    char ar[0];

    int iC;

};

 

struct stTest stA;

 

int main(void)

{

    printf("&stA: %p\n", &stA);

    printf("&stA.ar: %p\n", &stA.ar[0]);

    printf("&stA.iC: %p\n", &stA.iC);

}
复制代码&stA: 0x8049600
&stA.ar: 0x8049608
&stA.iC: 0x8049608

 

-----------------------一些知识

1. 这东西叫什么名字我忘了…… 有点像转义int a = '\x12';

int b = 0x12;

assert( a==b );
复制代码char const* a = "\x12\x12";

char const b[] = { 0x12, 0x12, 0x0 };

assert( strlen(a)==strlen(b) );

assert( strlen(b)+1==sizeof b );

assert( memcmp(a, b, sizeof b) == 0 );
复制代码2. 类型的表示

假设int是4字节, 且用二进制补码表示。int a = 0x120326;

// a是由4个字节组成, 从高权到低权依次是0x00, 0x12, 0x03, 0x26。

// 如果是大端, 内存中的布局是高权在低地址

 

unsigned char b[] = {0x00, 0x12, 0x03, 0x26 };

assert( memcmp(&a, b, sizeof a) == 0 );

 

// 如果是小端, 内存中的布局是低权在低地址

 

unsigned char b[] = {0x26, 0x03, 0x12, 0x00};

assert( memcmp(&a, b, sizeof a) == 0 );
复制代码3. 数组与指针

数组与指针的区别可以说一大堆。
但最容易理解的, 恰好是它们在结构中的不同行为。typedef struct

{

      int  i;

      char c[8];

} A;

typedef struct

{

      int   i;

      char* c;

} B;
复制代码还是设int是4字节, A的布局是:?? ?? ?? ?? ?? ?? ?? ?? ... ???

<-   i   -> <-  8字节 c      ->

<             A              ->
复制代码再设指针也是4字节, 且对齐需求与int相同, 那B的布局是:?? ?? ?? ?? ?? ?? ?? ??

<-   i   -> <-  c    ->

<          B         ->
复制代码这样, 下面的代码应该就好理解了:char layout[sizeof(A)] = { 0x26, 0x03, 0x12, 0x00, 'h' , 'e' , 'l', 'l', 'o', ' ', 'c', 0 };

A a;

memcpy(&a, layout, sizeof a);
复制代码前4个字节是i, 所以复制完毕后, a.i就是0x00120326(小端, 其他假设同上)。
a.c就是余下的内容, 一个c-style-string "hello c"

将中间的layout步骤省去:A a;

memcpy(&a, "\x26\x03\x12\x00hello c\x00" , sizeof a);
复制代码就差不多是那个样子了。
最后一个\x00是多余的, "literal" 本来末尾就有一个0。


4. flexible array memberA a;

assert( a.c == (char*)&a + offsetof(A, c) );
复制代码即a.c的地址, 是a的地址加上c在A中的偏移。B b;

char* p = *(char**)( (char*)&b + offsetof(B, c) );

assert( b.c == p );
复制代码将c在B中的偏移处的内容, 理解为一个指针, 该指针的值就是b.c的地址。

故A可以存放一个整数与一个长度不超过7的c-style-string。
而B可以存放一个整数与一个指针, 指针由可以存放另一块内存的地址。

上面的a的布局是:0x26 0x03 0x12 0x00 "hello c"

<-        i      -> <-  c  ->

<          a         ->
复制代码而B的布局:B b;

b.i = 0x120326;

void* p = malloc( sizeof("hello c") );

b.c = p;

memcpy(p, "hello c", sizeof("hello c") );

 

0x26 0x03 0x12 0x00 pp pp pp pp

<-        i      -> <- 值是p ->

<-                 b         ->

 

p :

"hello c"
复制代码a中, 0x120326和 "hello c"的地址是连续的
b中, 它们可能是分离的。


而以前的一些"聪明"的C程序员这样来实现一个变长且连续的整数、字符串pair:A* pa = malloc( offsetof(A, c) + 12 );
复制代码那么*pa的布局就是:pa:

?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??

<-   i   -> <-     c             -> <- rest  ->
复制代码理论上, pa->c只有8字节。
但pa->c是这8字节的首地址。
再根据指针运算, pa->c[8]就是rest的首地址。
所以, 通过pa->c, 其实可以操纵rest部分。
pa就可以存放一个整数与一个长度不超过11的c-style-string。

理论上, pa->c确实是一个8字节的数组, 但被当成了12字节的数组。
虽然大部分情况下不会出问题, 但这毕竟是一个类型漏洞, 说不通。
比如, C89没有规定C实现一定不能添加数组长度检查。
如果真有C编译器去实现了, 代码就要挂。


所以, C99把这个行为标准化, 称为flexible array member。
语法是不指定维度:typedef struct

{

      int i;

      char c[];

} A99;
复制代码这样, 编译器必须让这种"末尾是一个不定长成员"的做法合法。

C要求数组的维度>=1, 0长数组是gcc的方言。

阅读(986) | 评论(0) | 转发(0) |
0

上一篇:unix.dos回车换行

下一篇:数值转换

给主人留下些什么吧!~~