02-c提高03day

目录

一、目的

1、想多学习C知识

二、参考

1、(01)某马C++24期完整版

三、操作:01 Calloc和Realloc

1、测试1:calloc申请内存

2、测试:realloc进行扩充内存大小

三、操作:02 sscanf

1、测试1:02 sscanf

2、测试:忽略字符串到空格或\t

3、测试:%[width]s 读指定宽度的数据;小写's'

4、测试:%[a-z] 匹配a到z中任意字符(尽可能多的匹配)

5、测试:%[aBc] 匹配a、B、c中一员,贪婪性

6、测试:%[^a]从开始位置匹配到非a的任意字符,贪婪性

7、测试:%[^a-z]表示读取除a-z以外的所有字符

8、测试:以“.”来分割字符数组

9、测试:一堆符号做处理

三、(暂时不理解)操作:03 查找子串

1、测试

三、操作:04 指针易错点

1、测试

三、操作:05 const使用

1、测试

三、操作:07 二级指针做函数参数_输入特性

1、测试

三、操作:08 二级指针练习

1、测试:失败:不知道怎么回事

三、操作:09 位运算

1、测试:按位取反

2、测试:位与 &

3、测试:位或 |

4、测试:位异或^

5、测试:左移运动符,左移几位就相当于乘以2的几次方


一、目的

1、想多学习C知识

 

二、参考

1、(01)某马C++24期完整版

 

三、操作:01 Calloc和Realloc

1、测试1:calloc申请内存

①运行结果:

  • 总结:calloc作用
  • 总结:int *p申请了10个连续int类型大小的内存,所以使用p[i]就可以定位到这个数组的具体某个元素
  • 总结:calloc和free也是对应的
  • 总结:int *p和char * p差不多,理解为数组,这样给数组分配了内存地址之后,for循环中给数组每个位置赋值,

 

①总结:calloc作用

函数名: calloc

函数原型:void* calloc(unsigned int num,unsigned int size);

功能:在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

 

2、测试:realloc进行扩充内存大小

①运行结果:

  • 总结:malloc:用法
  • 总结:realloc:用法
  • 总结:因为realloc多分配出来的内存地址没有初始化,所以都是乱码,并且都是一样大。
  • 总结:int *p和char * p差不多,理解为数组,这样给数组分配了内存地址之后,for循环中给数组每个位置赋值,
  • 总结:malloc之后,使用realloc后,p的地址变了

 

①realloc

功能

先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

返回值

如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

注意

当内存不再使用时,应使用free()函数将内存块释放。

 

三、操作:02 sscanf

1、测试1:02 sscanf

①运行效果:

  • 总结:sscanf用法
  • 总结: * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
  • 总结:#if 0 #else #endif
  • 总结:sscanf中将第一个参数里面通过第二个参数的处理赋值给第三个参数

 

①sscanf:用法

1、参考:

https://baike.baidu.com/item/sscanf/10551550?fr=aladdin

1、一般用法

1

2

3

char buf[512] = ;

sscanf("123456 ""%s", buf);

printf("%s\n", buf);

结果为:123456
2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。

1

2

sscanf("123456 ""%4s", buf);

printf("%s\n", buf);

结果为:1234
3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。

1

2

sscanf("123456 abcdedf""%[^ ]", buf);

printf("%s\n", buf);

结果为:123456
4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。

1

2

sscanf("123456abcdedfBCDEF""%[1-9a-z]", buf);

printf("%s\n", buf);

结果为:123456abcdedf
5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。

1

2

sscanf("123456abcdedfBCDEF""%[^A-Z]", buf);

printf("%s\n", buf);

结果为:123456abcdedf
  6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中

1

2

sscanf("iios/12DDWDFF@122""%*[^/]/%[^@]", buf);

printf("%s\n", buf);

结果为:12DDWDFF
  7、给定一个字符串"hello, world",仅保留"world"。(注意:“,”之后有一空格)

1

2

sscanf("hello, world""%*s%s", buf);

printf("%s\n", buf);

结果为:world
  P.S. %*s表示第一个匹配到的%s被过滤掉,即hello被过滤了,
  如果没有空格则结果为NULL。 [2] 

 

2、测试:忽略字符串到空格或\t

①运行效果:

  • 总结:sscanf用法
  • 总结: * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
  • 总结:#if 0 #else #endif
  • 总结:sscanf中将第一个参数里面通过第二个参数的处理赋值给第三个参数
  • 总结:忽略字符串到空格或\t
  • 总结:好像\t是table的意思?
  • 总结:如\0,\t,\n等,就称为转义字符,\n相当于在程序中按了一下“Enter”回车键比较,\t就相当于你在编程的时候按一下“Table”键

 

3、测试:%[width]s 读指定宽度的数据;小写's'

①运行结果:

  • 总结:注意:区分大小写,小写才有效,大写一直是第一个元素

 

4、测试:%[a-z] 匹配a到z中任意字符(尽可能多的匹配)

①运行结果:

  • 总结:只能小写[a-z]
  • 总结: * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
  • 总结:

 

①运行结果

  • 总结:sscanf是从第一个字符开始取值的,不能断掉,如果断了,后面也就不取值了,

 

5、测试:%[aBc] 匹配a、B、c中一员,贪婪性

①运行结果:

  • 总结:%[aAb]能匹配几个就赋值几个
  • 总结:

①运行结果

  • 总结:必须要str第一个字符开始,否则就获取不到
  • 总结:断了就后面都不要了

 

6、测试:%[^a]从开始位置匹配到非a的任意字符,贪婪性

①运行结果:

  • 总结:%[^a]匹配到了字符a就停下来,并且后面的字符都不要了

 

7、测试:%[^a-z]表示读取除a-z以外的所有字符

①运行结果:

  • 总结:记住写法%[^0-9]。
  • 总结:排除后,后面的字符都不要了。

①运行结果:

  • 总结:到了[^0-9]以后的字符都不要了

 

8、测试:以“.”来分割字符数组

①运行结果:

  • 总结:将*ip中以"."分割开了,并且分别赋值给了num1。
  • 总结:赋值时候使用的是&。

①运行结果:如果不加&会报错

  • 总结:需要使用&符号,否则会报错。
  • 总结:&代表对这个地址上面的变量进行处理。

 

9、测试:一堆符号做处理

①运行结果:

  • 总结:老师做个测试,记住各种使用的方法。

 

三、(暂时不理解)操作:03 查找子串

1、测试

①运行结果:

  • 总结:将所有的char* a看作是string str_a,这样++a就是++指针地址,也就是string移动到下一个字符
  • 疑问:为什么输出是gf
  • 总结:要使用const,不会对原本的数据进行操作。

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

//查询子串第一次出现的位置
char *myStrStr(const char *str,const char *substr)
{
	const char *mystr = str;
	const char *mysub = substr;
	while (*mystr!='\0')
	{
		if (*mystr!=*mysub)
		{
			++mystr;
			continue;
		}

		//临时指针变量
		const char *temp_mystr = mystr;
		const char *temp_mysub = mysub;
		while (*temp_mysub!='\0')
		{
			if (*temp_mystr!=*temp_mysub)
			{
				++mystr;
				break;
			}

			++temp_mysub;
			++temp_mystr;
		}

		//说明匹配成功
		if (*temp_mysub=='\0')
		{
			return (char *)mystr;
		}
	}

}

void test()
{
	char *str = "abcdefg";
	char *sub = "f";
	char *pos = myStrStr(str, sub);
	printf("pos=%s\n", pos);
}

int main()
{
	test();
	system("pause");
	return EXIT_SUCCESS;
}

 

三、操作:04 指针易错点

1、测试

①运行结果:报错

  • 总结:不能++p
  • 总结:当你操作某块内存的时候,这块内存一定要是合法的(自己申请的,在使用的时候还没有被释放);因为p++内存不是我申请的不能操作

①运行结果:

  • 总结:不能p++
  • 当你操作某块内存的时候,这块内存一定要是合法的(自己申请的,在使用的时候还没有被释放)因为p++内存不是我申请的不能操作

①运行结果:

  • 总结:当你操作某块内存的时候,这块内存一定要是合法的(自己申请的,在使用的时候还没有被释放)因为p++内存不是我申请的不能操作

①运行结果

  • 总结:因为p是我创建的,但是p++、++p不是我创建的,所以对其访问会报错
  • 总结:因为char *p等于string str,所以当p[index+1]就是访问字符串里面的字符,所以不会报错

 

三、操作:05 const使用

1、测试

①运行结果:

  • 总结:形参设置为const,并且新建一个Person对象来接受这个,这样就不会对原数据造成改变
  • 总结:规避地址传递的副作用(在使用对象指针的情况下,有可能意外修改数据)
  • 总结:因为c语言中没有类,所以使用结构体,struct

 

三、操作:07 二级指针做函数参数_输入特性

1、测试

①运行结果:

  • 总结:堆上分配数据空间
  • 总结:栈上分配数据空间
  • 总结:释放数组内存
  • 总结:malloc时候是分配内存空间,其实也就是给指针来分配内存空间。那么(int*)*10分配空间时候,就是给int **p分配空间。

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

void printArray(int **arr, int len)
{
	for (int i = 0; i < len;++i)
	{
		printf("%d\n", *arr[i]);
	}
}

void test01()
{
	//堆上分配数据空间
	int **pArray = malloc(sizeof(int*) * 6);
	//栈上分配数据空间
	int a1 = 100;
	int a2 = 200;
	int a3 = 300;
	int a4 = 400;
	int a5 = 500;
	int a6 = 600;
#if 1
	pArray[0]=&a1;
	pArray[1] = &a2;
	pArray[2] = &a3;
	pArray[3] = &a4;
	pArray[4] = &a5;
	pArray[5] = &a6;
#else
	*(pArray + 0) = &a1;
	*(pArray + 1) = &a2;
	*(pArray + 2) = &a3;
	*(pArray + 3) = &a4;
	*(pArray + 4) = &a5;
	*(pArray + 5) = &a6;
#endif
	printArray(pArray, 6);
	//释放数组内存
	if (pArray!=NULL)
	{
		free(pArray);
		pArray = NULL;
	}

}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

 

三、操作:08 二级指针练习

1、测试:失败:不知道怎么回事

①运行效果

  • 总结:FILE、strlen、strcpy、fgets、fseek、memset、fopen要知道

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

//获得文件行数
int getFileLines(FILE *file)
{

	if (NULL == file)
	{
		return -1;
	}

	char buf[1024] = { 0 };

	int lines = 0;

	while (fgets(buf, 1024, file) != NULL)
	{
		++lines;
	}

	//恢复文件指针指向文件起始位置
	fseek(file, 0, SEEK_SET);



	return lines;
}


//读取文件数据
void readFileData(FILE *file, int lines, char ** contents)
{
	if (NULL == file)
	{
		return;
	}

	if (NULL == contents)
	{
		return;
	}

	if (lines <= 0)
	{
		return;
	}

	//创建缓冲区
	char buf[1024] = { 0 };
	int index = 0;
	while (fgets(buf, 1024, file) != NULL)
	{
		//printf("buf:%s", buf);
		int curLineLen = strlen(buf) + 1;
		//给当前行分配内存
		char *lineContent = malloc(sizeof(char)* curLineLen);
		//将行数据拷贝到空间中
		strcpy(lineContent, buf);

		contents[index++] = lineContent;

		memset(buf, 0, 1024);
	}

}

void showFileContents(char **contents, int lines)
{
	for (int i = 0; i < lines; ++i)
	{
		printf("%d行:%s", i + 1, contents[i]);
	}
}

//释放文件数据内存
void freeFileSpace(char **contents, int lines)
{
	for (int i = 0; i < lines; ++i)
	{
		if (contents[i] != NULL)
		{
			free(contents[i]);
			contents[i] = NULL;
		}
	}

	free(contents);
	contents = NULL;
}

void test()
{

	//打开文件
	FILE *file = fopen("../text.txt", "r");
	if (NULL == file)
	{
		printf("打开文件失败!\n");
		return;
	}

	//统计文件行数
	int lines = 10;
	lines = getFileLines(file);
	printf("lines:%d\n", lines);

	char **pContents = malloc(sizeof(char *)* lines);

	//读取文件内容
	readFileData(file, lines, pContents);

	//关闭文件
	fclose(file);
	file = NULL;

	//打印文件内容
	showFileContents(pContents, lines);

	//释放文件数据
	freeFileSpace(pContents, lines);
}


int main(){

	test();

	system("pause");
	return EXIT_SUCCESS;
}

①fopen

fopen函数是打开一个文件,其调用的一般形式为:

文件指针名=fopen(文件名,使用文件方式);

“文件指针名”必须是被声明为FILE 类型的指针变量;

“文件名”是被打开文件的文件名;

“使用文件方式”是指文件的类型和操作要求;

“文件名”是C风格字符串。

①fgets

fgets函数功能为从指定的流中读取数据,每次读取一行。其原型为:char *fgets(char *str, int n, FILE *stream);从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 [1] 

①fseek

int fseek(FILE *stream, long offset, int fromwhere);函数设置文件指针stream的位置。

如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset取值大于等于2*1024*1024*1024,即long的正数范围2G),则不改变stream指向的位置,函数返回一个非0值。

fseek函数和lseek函数类似,但lseek返回的是一个off_t数值,而fseek返回的是一个整型。

 

三、操作:09 位运算

1、测试:按位取反

①参考:C语言取反运算~

https://blog.csdn.net/weixin_33858336/article/details/91735005

  • 总结:int是4个字符,就是2的4次方,是32位。

十进制数字 7 的二进制码 

00000000 00000000 00000000 00000111
按位取反运算 ~7
11111111 11111111 11111111 11111000 
所以  printf("%x\n",~7);  //十六进制输出:fffffff8
11111111 11111111 11111111 11111000  的最高八位符号位(11111111,表示负数),有符号整数在计算机中采用补码存储,即 该二进制数为所求整数的补码。

负数的补码 = 原码 取反 + 1;

所以   原码 = 补码 - 1  取反 

所以有:

11111111 11111111 11111111 11111000(补码)

11111111 11111111 11111111 11110111( -1 )

11111111 00000000 00000000 00001000(取反 得到原码 -8)

所以  printf("%d\n",~7);   //十进制输出:-8

 

①运行结果:

  • 总结:2的二进制是010(前面要加个0,为了后面的补码),补码之后就是101,负数之后就是1101 ;然后负数使用补码存储,就是101;转为十进制就是
  • 总结:  -1的二进制是110
  • 疑问:为什么等于-3?

  • 总结:int是4个字符,就是2的4次方,是32位。
  • 总结:负数的补码 = 原码 取反 + 1
  • 总结: 取反后的原码 = (非负号的二进制)补码 - 1
  • 总结:前面8位是负号的意思,不用进行补码

 

①运行结果

  • 总结:0的二进制是 0,那么取反就是1

 

①运行结果

  • 总结:二进制、再取反

①总结

  • ~1010的反码是0101
    而负数在计算机中的表示是用补码,-11求补码过程:1011取反->0100加1->0101
    即-11等价于~10
    括号中的是0101
    补充说明:是这样的,1010在32位计算机中的存储实际上是00001010,取反后是11110101,在计算机中首位是0表示正数,是1表示负数,即11110101表示的是一个负数,即要由11110101求这个负数,即求补码的逆,步骤:先减1得11110100,再取反,取反时符号位不变,得10001011,即-11。用4位表示的话可以填0101,或者是8位的11110101

 

2、测试:位与 &

①运行结果

总结:

 

3、测试:位或 |

①运行结果:

  • 总结:倒叙排列法来十进制转换为二进制
  • 总结:|符号,当二进制俩个对应的数字相同就是1,不同取大的那个。

 

4、测试:位异或^

①运行结果

  • 总结:^符号是:位异或^
  • 总结:2进制时候,相同的就是0,不同的就是1

 

5、测试:左移运动符,左移几位就相当于乘以2的几次方

①运行结果

  • 总结:左移运动符,左移几位就相当于乘以2的几次方
  • 总结:右移,就是负号

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值