c语言常识新知

1.数组下标编号为什么从0开始,而不是1开始?

  • 假设从数组a[i]标号1开始计数,数组访问方式如下:
    第1个元素的地址=首地址a;
    第2个元素的地址=首地址a + 1元素大小;
    第3个元素的地址=首地址a + 2
    元素大小;

    第i个元素的地址=首地址a + (i-1)*元素大小;

  在使用1作为编号时,在计算地址时总要进行一次减法运算;

  • 假设从数组a[i]标号0开始计数,数组访问方式如下:
    第0个元素的地址=首地址a;
    第1个元素的地址=首地址a + 1元素大小;
    第2个元素的地址=首地址a + 2
    元素大小;

    第i个元素的地址=首地址a + i*元素大小;

  在使用0作为编号时,在计算地址时不进行减法运算,这样用违背常规的方式换来计算速度。

2.结构体和共同体

2.1.定义:

  • struct:把不同类型的数据组合成一个整体,自定义类型。
  • union: 使几个不同类型的变量共同占用一段内存。

2.2.地址对齐
  基本数据(如单字节、双字节、四字节整数)存放处的地址必须能被自己数据类型的大小整除。比如,双字节整数存放的地址必须被2整除,四字节整除存放的地址要被4整除。如果不满足要求,不同体系结构的cpu反应不同。默认对齐方式有1字节,2字节,4字节,8字节和16字节对齐。

  struct和union都有内存对齐,结构体的内存布局依赖于CPU、操作系统、编译器及编译时的对齐选项。

2.3.struct和union区别

  • 在struct中,各成员都占有自己的内存空间,它们是同时存在的。sizeof(struct)是内存对齐后所有成员长度的加和。
  • 在任何同一时刻,union只存放了一个被选中的成员,当union中存入新的数据后,原有的成员就失去了作用,新的数据被写到union的地址中。sizeof(union)是最长的数据成员的长度。
  • 在union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。

2.4.结构体对齐

案例1(visual studio2017):

#include<stdio.h>
struct Persion 
{
	char age;
	short no;
};

int main() 
{
	struct Persion p;
	p.age = 10;
	p.no = 2;
	printf("albert:%d\n",sizeof(p));    
	return 0;
}

VS选择对齐方式为1字节:
通过watch窗口查看各个变量:
在这里插入图片描述
  变量p.age地址为0x0037f794,占一个字节;变量p.no地址为0x0037f795,占两个字节;所以按照常理:sizeof p 应该是3个字节。
  根据对齐原则,双字节整数存放的地址必须被2整除,而p.no地址为奇数,不能被2整除,造成该变量存放的位置没有对齐

VS选择对齐方式为2字节:
通过watch窗口查看各个变量:
在这里插入图片描述
  变量p.age地址为0x0027fa68,占2个字节;变量p.no地址为0x0027fa6a,占两个字节;sizeof p = 4个字节。同理如果选择对齐方式为4,8,16字节,变量p.age占2个字节;变量p.no占两个字节;sizeof p = 4个字节。

案例2:
将short no;改为int no;

#include<stdio.h>
struct Persion 
{
	char age;
	int no;
};

int main() 
{
	struct Persion p;
	p.age = 10;
	p.no = 2;
	printf("albert:%d\n",sizeof(p));   
	return 0;
}

VS选择对齐方式为2字节:
通过watch窗口查看各个变量:
在这里插入图片描述
  变量p.age地址为0x0039f948,占2个字节;变量p.no地址为0x0039f94a,占4个字节;sizeof p = 6个字节。

VS选择对齐方式为4字节:
通过watch窗口查看各个变量:
在这里插入图片描述
  变量p.age地址为0x0014fc08,占4个字节;变量p.no地址为0x0014fc0c,占4个字节;sizeof p = 8个字节。

小结:

  • 变量p.age长度是根据p.no变量类型和对齐方式有关,长度如下表所示:
    在这里插入图片描述
  • 变量p.no所占长度 = min{max{sizeof(成员变量)},对齐长度}。

案例3:
  如下所示,sizeof p = 16。

#include<stdio.h>
struct Persion
{
	char a;
	char b;
	int c;
	short d;
	char e;
	short f;
}; 

int main()
{
	struct Persion p;
	p.a = 1;
	p.b = 2;
	p.c = 3;
	p.d = 4;
	p.e = 5;
	p.f = 6;
	printf("albert:%d\n", sizeof(p));
	return 0;
}

优化后:sizeof p = 12

#include<stdio.h>
struct Persion
{
	char a;
	char b;
	char e;
	short d;
	short f;
	int c;
};

int main()
{
	struct Persion p;
	p.a = 1;
	p.b = 2;
	p.c = 3;
	p.d = 4;
	p.e = 5;
	p.f = 6;
	printf("albert:%d\n", sizeof(p));
	return 0;
}

2.5.Union

2.5.1.大小端

  • 大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
  • 小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。

  union 型数据所占的空间等于其最大的成员所占的空间。对union 型的成员的存取都是相对于该联合体基地址的偏移量为0 处开始,也就是联合体的访问不论对哪个变量的存取都是从union 的首地址位置开始。

int main(int argc, char *argv[])
{
	union
	{
		int i;
		char a[2];
	}*p, u;

	p = &u;
	p->a[0] = 0x39;
	p->a[1] = 0x38;

	printf("albert:%d\n",sizeof(u));   //4
	printf("albert:0x%x\n",u.i);       //0xcccc3839
}

如下所示,&u.i和&u.a[0]都是相同的地址,所以对数组赋值保存到 i。
在这里插入图片描述
2.5.2.union 内存对齐

#include <stdio.h> 

int main(int argc, char *argv[])
{
	union u1
	{
		double a;
		int b;
	}p1;

	union u2
	{
		char a[13];
		int b;
	}p2;

	union u3
	{
		char a[13];
		char b;
	}p3;

	printf("albert:%d\n", sizeof(p1));   //8
	printf("albert:%d\n", sizeof(p2));  //16
	printf("albert:%d\n", sizeof(p3));  //13
}

  对于u2和u3,最大的空间都是char[13]类型的数组,为什么u3的大小是13,而u2是16呢?关键在于u2中的成员int b。由于int类型成员的存在,使u2的对齐方式变成4,也就是说,u2的大小必须在4的对界上,所以占用的空间变成了16(最接近13的对界)。对界是可以更改的,使用#pragma pack(x)宏可以改变编译器的对界方式,

3.i++和++i 汇编理解

3.1. i++

#include <stdio.h>

int main()
{
	int a;
	int i = 10;
	a = i++;
	printf("a=%d,i=%d\n",a,i);  //输出a=10,i=11
}

汇编执行如下:
在这里插入图片描述

3.2. ++i

#include <stdio.h>

int main()
{
	int a;
	int i = 10;
	a = ++i;
	printf("a=%d,i=%d\n",a,i);  //输出a=11,i=11
}

汇编如下:
在这里插入图片描述
4.指针汇编新解

案例1: p++ 先将p赋值给变量b,然后执行p++,即p执行数组下一个元素。

#include <stdio.h>

int main()
{
	int a[] = { 1,5,10,20 };
	int *p  = &a[0];
	int b = *p++;               //*p++ 等同于*(p++)
	printf("b = %d\n",b);    //b= 1
	printf("*p = %d\n", *p); //*p=5
}

汇编如下所示:
在这里插入图片描述
案例2:(p)++ 先将p赋值给变量b,然后将*p+1。

#include <stdio.h>

int main()
{
	int a[] = { 1,5,10,20 };
	int *p  = &a[0];
	int b = (*p)++;
	printf("b = %d\n",b);   // b =1
	printf("*p = %d\n", *p);  //*p=2
}

在这里插入图片描述
案例3: *++p 先将p变量+4,指向下一个数据元素,然后取此时p指向的数据。

#include <stdio.h>

int main()
{
	int a[] = { 1,5,10,20 };
	int *p  = &a[0];
	int b = *++p;         //等同于*(++p)
	printf("b = %d\n",b);    //b = 5
	printf("*p = %d\n", *p); //*p = 5
}

在这里插入图片描述
案例4: ++*p 先取p指向的值,然后将该值自增1。

#include <stdio.h>

int main()
{
	int a[] = { 1,5,10,20 };
	int *p  = &a[0];
	int b = ++*p;         //等同于++(*p)
	printf("b = %d\n",b);    //b = 2
	printf("*p = %d\n", *p); //*p = 2
}

在这里插入图片描述
案例5: *d++ = *s–;

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

int main(int argc, char *argv[])
{
	char *src = "hello,world";
	char *dest = NULL;

	int len = strlen(src);
	dest = (char *)malloc(len + 1);

	printf("len=%d\n", len);
	char *d = dest;
	char *s = &src[len - 1];

	while (len-- != 0)
		*d++ = *s--;

	*d = 0;
	printf("*dest=%s\n", dest);

	free(dest);
	dest = NULL;

	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值