当前位置:首页 » 服务存储 » c语言存储内存对齐
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

c语言存储内存对齐

发布时间: 2022-03-01 15:42:06

c语言数组在内存中是怎么分配的

C语言中内存为分三类:栈区、堆区、静态数据区。

局部变量在栈上分配,函数调用前的栈指针,要和函数返回后的栈指针一样,否则就会出错。

void test(void)

{

char i,a[10];

printf("0x%x", &i);

printf("0x%x", a);

printf("0x%x", a+1);

printf("0x%x", a+2);

printf("0x%x", a+3);

}

(1)c语言存储内存对齐扩展阅读

c语言数组在内存分配

示例:

#include<stdio.h>

int main()

{

int a[4] = {11,12,13,14};

int b[4] = {21,22,23,24};

int *pa = &a;

int i = 0;

while(i<8)

{

i++;

printf("now *p value = %d and",*pa);

printf("p addr value = %d ",pa);

pa++;

}

return 0;

}

㈡ C语言有关内存对齐的问题

//比如有这样的结构体
structA
{
chara;//占1个字节
intb;//占4个字节
};

//那这个结构体一共要占5个字节的内存吗?
//不是的,系统会把它填充成8个字节,这就是字节对齐
//那系统为什么要这样做呢,这涉及到存取的效率问题
//从硬件角度看,32位的CPU通过总线访问内存,一个总线周期访问32位内存数据
//刚刚好4个字节,所以4字节对齐的存取效率是较高的
//这就是牺牲空间换取效率了
//当然不对齐CPU也能访问,就是要花点功夫而已

㈢ C语言内存对齐问题.

为了有助于加快计算机的取数速度,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。


字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有
需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一
个成员之后加上填充字节(trailing padding)。

你这里struct的首地址要能被double的8字节整除,

  1. char占1字节

  2. int要被此时的地址整除,那么需要补上3字节,所以这里一共该是8字节

  3. short2字节的,一共10字节,不满足3),不能整除int,故添加2字节

  4. 最后是double 8字节,这里地址应该是首地址+12不能被8整除,所以+4字节。最后一共24B!

    问题解决求采纳!

㈣ 有经验的C语言程序常说的“内存对齐”,原因究竟是什么

在C语言程序开发中,有时有经验的程序员会提起“内存对齐”一词,事实上,这也是C语言中结构体的 size 不等于它所有成员 size 之和的原因(C语言中的结构体的size,并不等于它所有成员size之和,为什么?),那么,C语言程序为什么要“内存对齐”呢?

事实上,本节只是粗浅讨论,处理器的内存系统比这里描述的要复杂得多,涉及的内容也要复杂得多。不过,我们至少已经知道,在C语言程序中坚持内存对齐还是有很多好处的。

㈤ C语言结构体在内存中的分配

因为内存对齐。
为了CPU更快地寻址,编译器会进行内存对齐。
char c1; //分配1字节空间
char c2; //分配1字节空间
short s; //上面的两个char占用空间刚好等于一个short,不会发生对齐,分配2字节空间
int i; //上面的两个char一个short占用空间刚好等于一个int,不会发生对齐,分配4字节空间
因此最后结构体test2的size为8字节
char c1; //分配1字节空间
short s; //地址未对齐,会发生内存对齐,补齐1字节的空间,然后再为short分配2字节空间
char c2; //分配1字节空间
int i; //地址未对齐,会发生内存对齐,补齐3字节的空间,然后再为int分配4字节空间
因此最后结构体test1的size为1+1(补齐)+2+1+3(补齐)+4=12字节
欲知详情,请网络“内存对齐”,这里不再详述。

㈥ C语言中结构体在内存中的存储方式

结构体在内存中的存储方式,和常规的C语言变量、常量存储方式类似,唯的不同在于对齐。


只所以要进行数据对齐是因为编译器对结构的存储的特殊处理能提高CPU存储变量的速度,一般来说,32位的CPU内存以4字节对齐,64位的CPU的以8字节的对齐。一般可以使用#pragma pack()来指出对齐的字节数。比如下面的代码,在debug会显示结构体test的内存大小为28,如果生成release版则所占内存大小为32 。

#include<stdio.h>

#ifdef_DEBUG
#pragmapack(4)
structtest
{
charx[13];//13
intd;//4
doublef;//8
}ss;
#else
#pragmapack(8)
structtest
{
charx[13];//13
intd;//4
doublef;//8
}ss;
#endif

intmain(void){

printf("%d ",sizeof(ss));
return0;
}

㈦ C语言,如何保证malloc分配的内存字段对齐

不知道你说的内存字段对齐指的是什么
malloc在堆上分配所需内存
分配的内存逻辑上是连续的,你可以把它当成连续的来使用;
物理上未必是连续的,这由操作系统来管理,用户程序不用操心。

㈧ 关于C++的内存对齐

在代码的开头加上#pragma pack (1)就可以取消对齐了.
#pragma pack (n)的作用是按n字节对齐.实际上是取成员自身的对齐数和n中的较小者来作为对齐数,所以n为1的话就相当于取消对齐了.
你可以用#pragma pack ()来恢复为默认的对齐方式.

字节对齐C语言中也有要求.

㈨ C语言字节对齐问题

这个占多少个字节是不确定的!!!跟编译器有关的!!
对于visual
c++来说:
int
4字节
char
1字节
float
4字节
long
4字节
double
8字节
而对于gcc的编译器来说
double占12字节!

㈩ c语言访问内存冲突,这该怎么办啊

、C中内存分为四个区
栈:用来存放函数的形参和函数内的局部变量。由编译器分配空间,在函数执行完后由编译器自动释放。 
堆:用来存放由动态分配函数(如malloc)分配的空间。是由程序员自己手动分配的,并且必须由程序员使用free释放。如果忘记用free释放,会导致所分配的空间一直占着不放,导致内存泄露。 
全局局:用来存放全局变量和静态变量。存在于程序的整个运行期间,是由编译器分配和释放的。 
文字常量区:例如char *c = “123456”;则”123456”为文字常量,存放于文字常量区。也由编译器控制分配和释放。 
程序代码区:用来存放程序的二进制代码。
例子(一) 
int a = 0; //全局区 
void main() 

int b; //栈 
char s[] = abc; //s在栈,abc在文字常量区 
char *p1,*p2; //栈 
char *p3 = 123456; //123456在常量区,p3在栈上 
static int c =0; //全局区 
p1 = (char *)malloc(10); //p1在栈,分配的10字节在堆 
p2 = (char *)malloc(20); //p2在栈,分配的20字节在堆 
strcpy(p1, 123456); //123456放在常量区 

例子(二) 
//返回char型指针 
char *f() 

//s数组存放于栈上 
char s[4] = {'1','2','3','0'}; 
return s; //返回s数组的地址,但程序运行完s数组就被释放了 

void main() 

char *s; 
s = f(); 
printf (%s, s); //打印出来乱码。因为s所指向地址已经没有数据 

2、动态分配释放内存
用malloc动态分配内存后一定要判断一下分配是否成功,判断指针的值是否为NULL。 
内存分配成功后要对内存单元进行初始化。 
内存分配成功且初始化后使用时别越界了。 
内存使用完后要用free(p)释放,注意,释放后,p的值是不会变的,仍然是一个地址值,仍然指向那块内存区,只是这块内存区的值变成垃圾了。为了防止后面继续使用这块内存,应在free(p)后,立即p=NULL,这样后面如果要使用,判断p是否为NULL时就会判断出来。

NO.1
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,hello world);
printf(str);
}
请问运行Test函数后会是什么样的结果?
NO.2
char *GetMemory(void)
{
char p[] = hello world;
retrun p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
问题同NO.1
NO.3
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str,100);
strcpy(str,hello);
printf(str);
}
问题同NO.1
NO.4
void Test(void)
{
char *str = (char *)malloc(100);
strcpy(str,hello);
free(str);
if(str != NULL)

strcpy(str,world);
printf(str);
}
}
问题同NO.1
我对以上问题的分析:
NO.1:程序首先申请一个char类型的指针str,并把str指向NULL(即str里存的是NULL的地址,*str为NULL中的值为0),调用函数的过程中做了如下动作:1申请一个char 类型的指针p,2把str的内容到了p里(这是参数传递过程中系统所做的),3为p指针申请了100个空间,4返回Test函数.最后程序把字符串hello world拷贝到str指向的内存空间里.到这里错误出现了!str的空间始终为NULL而并没有实际的空间.深刻理解函数调用的第2步,将不难发现问题所在!(建议:画图理解)
NO.2:程序首先申请一个char类型的指针str,并把str指向NULL.调用函数的过程中做了如下动作:1申请一数组p[]并将其赋值为hello world(数组的空间大小为12),2返回数组名p付给str指针(即返回了数组的首地址).那么这样就可以打印出字符串"hello world"了么?当然是不能的!因为在函数调用的时候漏掉了最后一步.也就是在第2步return数组名后,函数调用还要进行一步操作,也就是释放内存空间.当一个函数被调用结束后它会释放掉它里面所有的变量所占用的空间.所以数组空间被释放掉了,也就是说str所指向的内容将不确定是什么东西.
NO.3:正确答案为可以打印出hello.但内存泄漏了! 
NO.4:申请空间,拷贝字符串,释放空间.前三步操作都没有任何问题.到if语句里的判断条件开始出错了,因为一个指针被释放之后其内容并不是NULL,而是一个不确定的值.所以if语句永远都不能被执行.这也是着名的"野"指针问题.所以我们在编写程序释放一个指针之后一定要人为的将指针付成NULL.这样就会避免出现"野"指针的出现.有人说"野"指针很可怕,会带来意想不到的错误.

C语言内存对齐

C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。

使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

还是让我们来看看例子。
示例1:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其内存布局为:
|_f1__|__f2__|_|____f3___|____|
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
0 3 7 8 1316
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(BF1)的结果为2。
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};

 

什么是内存对齐

    考虑下面的结构:

         struct foo
         {
           char c1;
           short s;
           char c2;
           int i;
          };
    
    假设这个结构的成员在内存中是紧凑排列的,假设c1的地址是0,那么s的地址就应该是1,c2的地址就是3,i的地址就是4。也就是
    c1 00000000, s 00000001, c2 00000003, i 00000004。

    可是,我们在Visual c/c++ 6中写一个简单的程序:

         struct foo a;
    printf("c1 %p, s %p, c2 %p, i %p/n",
        (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);
    运行,输出:
         c1 00000000, s 00000002, c2 00000004, i 00000008。

    为什么会这样?这就是内存对齐而导致的问题。

为什么会有内存对齐

    以下内容节选自《Intel Architecture 32 Manual》。
    字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
    无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
    一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
    某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。

编译器对内存对齐的处理

    缺省情况下,c/c++编译器默认将结构、栈中的成员数据进行内存对齐。因此,上面的程序输出就变成了:
c1 00000000, s 00000002, c2 00000004, i 00000008。
编译器将未对齐的成员向后移,将每一个都成员对齐到自然边界上,从而也导致了整个结构的尺寸变大。尽管会牺牲一点空间(成员之间有空洞),但提高了性能。
也正是这个原因,我们不可以断言sizeof(foo) == 8。在这个例子中,sizeof(foo) == 12。

如何避免内存对齐的影响

    那么,能不能既达到提高性能的目的,又能节约一点空间呢?有一点小技巧可以使用。比如我们可以将上面的结构改成:

struct bar
{
    char c1; 
    char c2;
    short s;
    int i;
};
    这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。在这个例子中,sizeof(bar) == 8。

    这个技巧有一个重要的作用,尤其是这个结构作为API的一部分提供给第三方开发使用的时候。第三方开发者可能将编译器的默认对齐选项改变,从而造成这个结构在你的发行的DLL中使用某种对齐方式,而在第三方开发者哪里却使用另外一种对齐方式。这将会导致重大问题。
    比如,foo结构,我们的DLL使用默认对齐选项,对齐为
c1 00000000, s 00000002, c2 00000004, i 00000008,同时sizeof(foo) == 12。
而第三方将对齐选项关闭,导致
    c1 00000000, s 00000001, c2 00000003, i 00000004,同时sizeof(foo) == 8。

如何使用c/c++中的对齐选项

    vc6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。也就是:
    min ( sizeof ( member ),  n)
    实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
    /Zpn选项是应用于整个工程的,影响所有的参与编译的结构。
    要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。

    要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令。指令语法如下:
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n  )
    意义和/Zpn选项相同。比如:

#pragma pack(1)
struct foo_pack
{
    char c1;
    short s;
    char c2;
    int i;
};
#pragma pack()

栈内存对齐

    我们可以观察到,在vc6中栈的对齐方式不受结构成员对齐选项的影响。(本来就是两码事)。它总是保持对齐,而且对齐在4字节边界上。

验证代码

#include <stdio.h>

struct foo
{
    char c1;
    short s;
    char c2;
    int i;
};

struct bar
{
    char c1; 
    char c2;
    short s;
    int i;
};

#pragma pack(1)
struct foo_pack
{
    char c1;
    short s;
    char c2;
    int i;
};
#pragma pack()

int main(int argc, char* argv[])
{
    char c1;
    short s;
    char c2;
    int i;

    struct foo a;
    struct bar b;
    struct foo_pack p;

    printf("stack c1 %p, s %p, c2 %p, i %p/n",
        (unsigned int)(void*)&c1 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&s - (unsigned int)(void*)&i,
        (unsigned int)(void*)&c2 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&i - (unsigned int)(void*)&i);

    printf("struct foo c1 %p, s %p, c2 %p, i %p/n",
        (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

    printf("struct bar c1 %p, c2 %p, s %p, i %p/n",
        (unsigned int)(void*)&b.c1 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.c2 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.i - (unsigned int)(void*)&b);

    printf("struct foo_pack c1 %p, s %p, c2 %p, i %p/n",
        (unsigned int)(void*)&p.c1 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.c2 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.i - (unsigned int)(void*)&p);

    printf("sizeof foo is %d/n", sizeof(foo));
    printf("sizeof bar is %d/n", sizeof(bar));
    printf("sizeof foo_pack is %d/n", sizeof(foo_pack));
    
    return 0;
}