常见字符串函数+qsort的模拟实现+结构体对齐+位段+枚举优点


说明主要用于自我复习 不足之处还请见谅

常见字符串函数

1,strlen

size_t strlen ( const char * str );
字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
参数指向的字符串必须要以 ‘\0’ 结束。
注意函数的返回值为size_t,是无符号的( 易错 )

2, strcpy

char* strcpy(char * destination, const char * source );
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变

3,strcat

将原来的字符串添加到目标字符串后面并且新的字符串以\0结尾
源字符串必须有’\0’;

4,strcmp

比较字符串主要就是根据字典序进行排序

5,strstr

char * strstr ( const char *str1, const char * str2);
返回str2在str1中出现的第一个地址,如果str1中不存在str2那么便返回NULL

memcpy与memmove

void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理
例如假设有一个数组a,如果是memcpy(a,a+3)的话那么这个操作是未被定义的,具体的复制方式以及复制能否成功主要还是看编译器是否支持。但是如果使用memmove确实可以直接使用的
memcpy模拟实现
由于不知道复制的类型,所以我们模拟的时候都是先将指针强制转换成char*然后再解引用

void* my_memcpy(const void* e1, const void* e2,size_t num)
{

	assert(e1 && e2);
	void* ret = e1;
	while (num--)
	{
		*(char*)e1 = *(char*)e2;
		e1 = (char*)e1+1;
		e2 = (char*)e2+1;
	}
	return ret;
}

memmove函数的模拟实现

void* my_memmove(const void* e1, const void* e2,size_t num)
{
	assert(e1 && e2);
	void* ret = e1;//待会要返回的就是ret;
	if (e1 > e2)//如果e1>e2那么就是从e2从后开始往前拷贝
		      //因为这样才不会覆盖
		//否则从前开始拷贝
	{
		e1 = (char*)e1 + num - 1;
		e2 = (char*)e2 + num - 1;
		while (num--)
		{
			*(char*)e1 = *(char*)e2;
			e1 = (char*)e1 - 1;
			e2 = (char*)e2 - 1;
		}
	}
	else
	{
		while (num--)
				{
					*(char*)e1 = *(char*)e2;
					e1 = (char*)e1+1;
					e2 = (char*)e2+1;
				}
	}
	return ret;
}

qsort函数的简单模拟实现

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
bace:表示需要排序数组的起始地址
num:表示一共需要排序的元素个数
width:表示需要排序的元素字节大小
最后一个参数是比较函数
我使用的是冒泡函数进行模拟的。

普通版冒泡排序
void bubble_sort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		for(int j = 0;j<n-1-i;j++)
		{
			if (a[j] > a[j + 1])
				Swap(&a[j + 1], &a[j]);
		}
	}
}

其实我们可以发现,不管什么排序,排序的大致步骤是不变的,变化的就是比较的方式以及交换的方式也就是

if (a[j] > a[j + 1])
				Swap(&a[j + 1], &a[j]);

然后想办法替换掉这两句代码但是作用不变就可以了

由于事先并不知道需要排序的类型,所以都是用char*进行强制类型转换

//大致框架是没变的,主要是比较的方式变化了,然后交换的方式也变了
					//if (a[j] > a[j + 1])
						//Swap(&a[j + 1], &a[j]);
					if (compare((char*)bace + j * width, (char*)bace + (j + 1) * width)>0)
					{
					             //交换两个元素此时传进去的是两个地址
						Swap_sort((char*)bace + j * width, (char*)bace + (j + 1) * width,width);//一定要传width否则不知道
						//具体交换多少个字节

全部代码:

void bubble_sort(void* bace, int n, int width, int(*compare)(const void* e1, const void* e2))
{
	for (int i = 0; i < n - 1; i++)
			{
				for(int j = 0;j<n-1-i;j++)
				{
					//大致框架是没变的,主要是比较的方式变化了,然后交换的方式也变了
					//if (a[j] > a[j + 1])
						//Swap(&a[j + 1], &a[j]);
					if (compare((char*)bace + j * width, (char*)bace + (j + 1) * width)>0)
					{
					             //交换两个元素此时传进去的是两个地址
						Swap_sort((char*)bace + j * width, (char*)bace + (j + 1) * width,width);//一定要传width否则不知道
						//具体交换多少个字节
					}
				}
			}

}

如果要使用qsort这个库函数的时候,compare我们自己写的时候要注意参数一定得是void,否则会报错
当比较的数是int时

int compare_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;


}

当比较的数是字符串时

int compare_by_str(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

结构体面试题之为什么要结构体对齐

struct Test
{
	int a;
	char b;
	char c;

};

如果没有内存对齐,那么结构体大小就是4+1+4 = 9但是我把他放在vs编译器上测试发现大小是12。内存如何对齐?为什么要对齐?

1,结构体内存对齐的规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8(Linux环境下没有默认对齐数)
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
    在这里插入图片描述

为什么存在内存对齐

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
    总体来说:结构体的内存对齐是拿空间来换取时间的做法
    所以在设计结构体的时候,我们既要满足对齐,又要节省空间,我们应当让
    占用空间小的成员尽量集中在一起

修改默认对齐数

#pragma pack(X)//设置默认对齐数为X
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

位段

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字
数字表示占有几个比特位

1,位段的内存分配

在window环境下的VS编译器下:
位段先开辟四个字节还是一个字节是根据首个位段成员类型开辟的,如果首个成员是int 那么便开辟四个字节,然后根据成员自身大小填充已经分配的空间,下一个位段成员观察此时剩余空间大小是否够容纳自己,如果能,则继续填充,如果不能的话,直接与初始一样,开辟新的空间(注意假设开辟新空间是要求内存对齐的)直到该位段的最后一个成员,需要注意的是,整个位段的大小是成员类型大小中最大值的整数倍。
先看一个例子
[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblo.sdnjIpVimg.cn/14379e46ttps://img-blog.csdnimg.cn/14379e46]
db454549b69f1558ea20c05e.png)在开辟新的空间之前一定要考虑是否内存对齐,上述例子刚好开辟下一个空间时恰好内存对齐

具体空间开辟

在这里插入图片描述

我的一个问题

struct A
{
	int _a : 3;
	char _b : 5;
	int _c : 2;
	char _d : 7;
};

有一个位段是这样的,如果按照上述规则的计算此位段的大小应该是4但是我用vs编译器发现是16个字节然后我将

struct A
{
	int _a : 3;
	int _b : 5;
	int _c : 2;
	char _d : 7;
};

第二个元素类型变成int此时计算位段的大小又会是8个字节,我不免产生疑惑了,为什么会这样?难道是遇到不同类型变量空间直接不管剩下的空间是否满足填充,都要重新开辟空间吗(开辟空间还是满足内存对齐)?我至今不太理解,这个问题我解决了就会更新,要是有大佬看到了就更完美了hh

位段存在的跨平台问题

位段在不同的平台上有不同的规则:

1、int 位段被当成有符号数还是无符号数是不确定的。

2、位段中的成员从左到右分配还是从右到左分配是不确定的。

3、当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,也是不确定的。

所以跟结构体相比,位段更节省空间,但是有跨平台的问题。

枚举的优点

enum Day//星期
{
Mon,
Tues,
Wed,
};
enum Day是枚举类型
{}中的内容是枚举类型的 可能取值也叫做枚举常量
这些取值默认是从0开始的依次递增1,也可以在最初定义的时候就赋值
eg
enum Day//星期
{
Mon,
Tues,
Wed,
Thur = 80,

};

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

联合体

多个成员共同使用同一快空间,但是每次使用变量只能使用一个,联合体的大小 至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
联合体内的成员是共用一块空间的,如果改变其中一块也是会间接改变另外一个成员变量值的,因为他们是公用一块空间
给一个例子证明一下这个
在这里插入图片描述
联合体空间的大小最大成员大小不是最大对齐数的整数倍的时候
eg
union Un1
{
char c[5];
int i;
};
因为此时数组c的大小是5,而此时结构体中的最大对齐数是4,所以要内存对齐,此时该联合体的总大小应该是最大对齐数的整数倍.

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天少点debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值