2022-07-08-106期-内存函数+作业讲解

上一节课,我们学习了memcpy,今天我们复习一下

首先,使用memcpy函数。

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

int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d", arr2[i]);
	}
	return 0;
}

我们进行编译:

 接下来,我们来模拟实现memcpy

void*my_memcpy(void*str1, const void*str2, size_t num)
{
	assert(str1&&str2);
	void*p = str1;
	while (num--)
	{
		*(char*)str1 = *(char*)str2;
		str1 = (char*)str1 + 1;
		str2 = (char*)str2 + 1;
	}
	return p;
}
int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d", arr2[i]);
	}
	return 0;
}

我们进行编译

 我们进行思考:我们做的都是两个不同空间的内存拷贝,那么相同空间的内存拷贝,memcpy能实现吗?

例如:

我们的函数定义依旧没有改变,我们对main函数进行修改


int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5 ,6,7,8,9,10};
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d", arr2[i]);
	}
	return 0;
}

我们要做的是把1,2,3,4,5拷贝到对应的3,4,5,6,7的位置,拷贝后的结果应该是1,2,1,2,3,4,5,8,9,10。我们进行编译,看结果是否正确。

我们发现,打印的结果和我们的预期不同,原因是什么呢?

答:我们写一个图像进行解释

 这是数组的元素,我们进行拷贝,我们画拷贝过两个元素后的图像。

 我们再进行拷贝时,我们可以发现,我们的第三个元素已经不是3,而是变成了1,第四个元素变成了2,所以我们拷贝的时候,拷贝的结果并不是我们预料的结果。

这时候,我们就引入了函数memmove,我们使用memmove函数进行尝试。

int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	memmove(arr1+2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

我们进行编译

可以发现,memmove可以实现我们的需求。 

 我们可以发现,memmove函数和我们的memcpy的类型相同,那我们想一想我们能不能模拟实现memmove

要实现memmove,我们要首先思考一个问题:我们该如何避免我们的元素提前覆盖呢?

就像刚才的情况,假如我们反着写呢?

我们可以发现,倒着拷贝不会产生元素提前覆盖的情况,那是不是所有的情况我们都倒着拷贝呢?

假如我们要把5,6,7,8,9拷贝到3,4,5,6,7中呢,我们倒着拷贝

 可以发现,倒着拷贝三次,就产生了问题:我们把9拷贝到7的位置,那我们拷贝7的时候,其实就变成拷贝9了,所以仍会产生问题。

这时候,我们可以采取正向拷贝

 这时候并不会产生提前覆盖的问题。

所以我们意识到:需要分情况,当没有重叠现象的时候,就像把1,2,3,4,5拷贝到6,7,8,9,10一样,正向逆向拷贝都行。

当有重叠现象1:把相对小的一组拷贝到相对大的一组,例如1,2,3,4,5拷贝到3,4,5,6,7用逆序拷贝的方法

2:把相对大的一组拷贝到相对小的一组,例如5,6,7,8,9拷贝到3,4,5,6,7,我们用正序拷贝的方法。

总结:当有重叠现象中把大的一组拷贝到小的一组时,需要用正序

没有重叠现象或者有重叠现象:把小的一组拷贝到大的一组时,需要用逆序,我们进行模拟实现

void*my_memmove(void*str1, const void*str2, size_t num)
{
	assert(str1&&str2);
	void*p = str1;
	if (str1 <str2)
	{

		while (num--)
		{
			*(char*)str1 = *(char*)str2;
			str1 = (char*)str1 + 1;
			str2 = (char*)str2 + 1;
		}
	}
	else{
		while (num--)
		{
			*((char*)str1+num) = *((char*)str2 + num);
		}
	}
	return p;
}
int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memmove(arr1+2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

这就是对应的my_memmove

注意:在vs2013中,这里的memcpy其实也可以完成上述工作,但这并不意味着我们自定义的my_memcpy是错的,原因是vs这里对这个函数进行了改进

接下来,我们介绍memcmp函数

参数有三个,两个const修饰的无类型指针,还有一个无符号整型。

返回值是整型

这个函数和strcmp有相同的特点:返回值都是整型,都是ptr1对应的元素大于ptr2对应的元素时,返回大于0的数字,ptr1对应的元素小于ptr2对应的元素,返回小于0的数字,ptr1对应的元素等于ptr2对应的元素,返回0.

 这个函数的作用:比较ptr1和ptr2指向的元素对应的ASCII码值,比较的数量是num个字节。

我们来使用一下这个函数

#include<stdio.h>

int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5 };
	int arr2[20] = { 1, 2, 3, 4 };
	int ret=memcmp(arr1, arr2, 12);
	printf("%d", ret);
	return 0;
}

我们只比较了12个字节,也就是四个整型,因为两个数组的前四个元素全部都相等,所以打印结果相等

memcmp的比较方式是什么呢?

我们写出arr1和arr2对应的内存,arr1:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00

arr2:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00

我们是一个字节一个字节的比较,一共比较12个字节。

01 和01比,相等,00再和00比等等,一直比较12个字节,比较的是对应字符的ASCII码值的大小。

我们讲下一个函数:memset:内存设置。

 memset的参数有三个:第一个参数是一个无类型指针,第二个参数是一个整型,第三个参数是一个无符号整型。

memset的第一个参数指向要被填充的内存块

第二个参数是要填充的内容

第三个参数是要填充的数量。

我们举一个例子:

int main()
{
	char arr[] = "hello bit";
	memset(arr, 'x', 5);
	printf("%s", arr);
	return 0;
}

这里memset的意思是填充五个x到字符串hello bit里去。

假如我们要把bit改成三个x该怎么做呢?

int main()
{
	char arr[] = "hello bit";
	memset(arr+6, 'x', 3);
	printf("%s", arr);
	return 0;
}

我们进行编译

 接下来,我们写一个字符串,我们来判断一下结果是否正确

int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 40);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

我们初始化数组10个元素,把里面全部放成1,打印的结果是什么?是全1吗?

答:打印的结果并不是全1,我们进行编译

 这就是打印的结果,为什么结果是这样呢?

因为我们内存设置1,是把数组中每一个字节的位置都设置成了1,所以对应的二进制位是01 01 01 01   对应的结果正确

 我们可以看一下内存

 这就是memset内存设置1的结果,内存中所有的字节位都变成了1.

接下来,我们讲几道作业题。



BC116小乐乐改数字

描述
小乐乐喜欢数字,尤其喜欢0和1。他现在得到了一个数,想把每位的数变成0或1。如果某一位是奇数,就把它变成1,如果是偶数,那么就把它变成0。请你回答他最后得到的数是多少。

输入描述:
输入包含一个整数n (0 ≤ n ≤ 109)

输出描述:
输出一个整数,即小乐乐修改后得到的数字。

示例1
输入:

222222
复制输出:

0
复制

示例2
输入:

123
复制输出:

101

 答案:

#include<stdio.h>
#include<math.h>
int main()
{
	int a = 0;
	int i = 0;
	int sum = 0;
	scanf("%d", &a);
	while (a)
	{
		int bit = a % 10;
		if (bit % 2 == 1)
		{
			sum +=1* pow(10, i);
		}
		else
		{
			sum += 0 * pow(10, i);
		}
		a /= 10;
		i++;
	}
	printf("%d", sum);
	return 0;
}

下一道题:

KiKi学习了循环,BoBo老师给他出了一系列打印图案的练习,该任务是打印用“*”组成的带空格直角三角形图案。

输入描述:
多组输入,一个整数(2~20),表示直角三角形直角边的长度,即“*”的数量,也表示输出行数。

输出描述:
针对每行输入,输出用“*”组成的对应长度的直角三角形,每个“*”后面有一个空格。

示例1
输入:
5
复制
输出:
        * 
      * * 
    * * * 
  * * * * 
* * * * *
复制
示例2
输入:
4
复制
输出:
      * 
    * * 
  * * * 
* * * *

答案

#include<stdio.h>
int main()
{
	int a = 0;
	int i = 0;
	int j = 0;
	while(scanf("%d", &a)==1)
	for (i = 0; i < a; i++)
	{
		for (j = 0; j < a; j++)
		{
			if (i + j < a-1)
			{
				printf("  ");
			}
			else
				printf("* ");
		}
		printf("\n");
	}
	return 0;

}

答案

#include<stdio.h>
int main()
{
    double a=0.0;
    int b=0;
    int c=0;
    int d=0;
    scanf("%lf",&a);
    scanf("%d",&b);
    scanf("%d",&c);
    scanf("%d",&d);
    if(b==11&&c==11)
    {
        if(d)
        {
            a=a*0.7-50;
            if(a<0)
            a=0;
        }
        else{
            a=0.7*a;
        }
    }
     else if(b==12&&c==12)
    {
        if(d)
        {
            a=a*0.8-50;
            if(a<0)
            a=0;
        }
        else{
            a=0.8*a;
        }
    }
    else{
        a=a;
    }
    printf("%.2lf",a);
    return 0;
}

 

整型提升:如果是无符号数,高位直接补0

如果是有符号数,高位补符号位

题目名称:
程序的执行结果为( )

int main()
{
  unsigned char a = 200;
  unsigned char b = 100;
  unsigned char c = 0;
  c = a + b;
  printf(“%d %d”, a+b,c);
  return 0;
}
题目内容:
A .300 300
B .44 44
C .300 44
D .44 300

这道题如何做,我们进行分析:

首先看a+b,这里涉及到整型计算,整型提升的概念:C语言中字节数少于整型字节数的数据类型在进行整型运算时,该类型的数据会被默认转为整型数据。

所以我们这里要整型提升:

200对应的二进位制为0000 0000 0000 0000 0000 0000 1100 1000

但是我们这里的a是无符号字符类型,只能保存四个字节,所以这里我们要发生截断,所以a对应的二进位制为1100 1000

100对应的二进位制是0000 0000 0000 0000 0000 0000 0110 0100

同理,我们发生截断,所以b对应的二进位制是0110 0100

因为一个表达式的操作数的大小达不到一个整型大小的时候需要进行整型提升:所以a+b这里要整型提升

a,b都是无符号数,无符号数整型提升,高位直接补0.

a整型提升后的结果是0000 0000 0000 0000 0000 0000 1100 1000

b整型提升后的结果是0000 0000 0000 0000 0000 0000 0110 0100

进行相加后的结果是0000 0000 0000 0000 0000 0001 0010   1100

然后我们用整型的方式打印0000 0000 0000 0000 0000 0001 0010   1100

对应的结果是

300.

因为c是无符号char型,所以这里发生截断,阶段后的结果是0010   1100,再发生整型提升,提升后的二进位制为0000 0000  0000 0000  0000 0000  0010  1100,对应的整型的结果是44

所以最后的结果是300 44,所以选c。

下一题:

unsigned int a= 0x1234; unsigned char b=*(unsigned char *)&a;
在32位大端模式处理器上变量b等于( )

题目内容:
A .0x00
B .0x12
C .0x34
D .0x1234

我们首先要了解一下大小端字节序

大端字节序:低权值位数据存放在高地址处

小端字节序:低权值位数据存放在低地址处。

0x1234对应的其实是0x00 00 12 34

 因为是大端存储,大段存储的低地址存放的是高位字节序,00就是高位字节序。

*(unsigned char *)&a:首先取出a的地址,然后强制类型转化为无符号char型指针,char型指针解引用只能访问一个字节,又是大段存储,所以解引用访问的字节是00.

所以对应的结果是0x00.

接下来是一道编程题目:


作业内容
5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:



A选手说:B第二,我第三;

B选手说:我第二,E第四;

C选手说:我第一,D第二;

D选手说:C最后,我第三;

E选手说:我第四,A第一;

比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。

我们进行分析:这道题是需要我们把实际问题转化为代码的形式。

int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	int d = 0;
	int e = 0;
	for (a = 1; a <= 5; a++)
	{
		for (b = 1; b <= 5; b++)
		{
			for (c = 1; c <= 5; c++)
			{
				for (d = 1; d <= 5; d++)
				{
					for (e = 1; e <= 5; e++)
					{
						if (((b == 2) + (a == 3)) == 1
							&& ((b == 2) + (e == 4)) == 1
							&& ((c == 1) + (d == 2)) == 1
							&& ((c == 5) + (d == 3)) == 1
							&& ((e == 4) + (a == 1)) == 1)
						{
							if (a*b*c*d*e == 120)
							{
								printf("%d %d %d %d %d ", a, b, c, d, e);
							}
						}
						
					}
				}
			}
		}
	}
	return 0;
}

下一道题和这一道比较类似

日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。



以下为4个嫌疑犯的供词:



A说:不是我。

B说:是C。

C说:是D。

D说:C在胡说

已知3个人说了真话,1个人说的是假话。



现在请根据这些信息,写一个程序来确定到底谁是凶手。

 答案

int main()
{
	int killer = 0;
	for (killer = 'a'; killer <= 'd'; killer++)
	{
		if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
		{
			printf("%c\n", killer);
		}
	}
	return 0;
}

作业内容
在屏幕上打印杨辉三角。



1

1 1

1 2 1

1 3 3 1

……

答案:

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int arr[10][10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		for (j = 0; j <= i; j++)
		{
			if (j == 0)
			{
				arr[i][j] = 1;
			}
			if (i = j)
			{
				arr[i][j] = 1;
			}
			if (i >= 2 && j >= 1)
			{
				arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
			}
		}
		printf("\n");
	}
	for (i = 0; i < 10; i++)
	{
		for (j = 0; j <= i; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

free能够释放指针,但并不是指针会被置为空指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值