C基础梳理 2019.1.1 (calloc、realloc、sscanf、strstr、指针易错点、const使用、二级指针读取文件练习、位运算)

calloc

 

测试代码(free之前先判断,这一点挺好)

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

void test() {
	int *p = (int*)calloc(10, sizeof(int));
	for (int i = 0; i < 10; ++i) {
		p[i] = i + 1;
	}
	for (int i = 0; i < 10; ++i) {
		printf("%d", p[i]);
	}
	if (p != NULL) {
		free(p);
		p = NULL;
	}
}

运行结果

 

不同平台下 size_t 可能是不同的,不一定是unsigned int

 

 

 

realloc

作用

1、在原有空间的后面继续扩展内存

2、如果申请失败,则将源数据挪走到新申请的连续整块内存中。并将原数据占的内存清理

 

测试代码如下:

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

void test() {
	int *p = (int*)malloc(sizeof(int)*10);
	for (int i = 0; i < 10; ++i) {
		p[i] = i + 1;
	}
	for (int i = 0; i < 10; ++i) {
		printf("%d", p[i]);
	}
	printf("\n");

	printf("%d\n", p);
	p = (int*)realloc(p, sizeof(int) * 1000);
	printf("%d\n", p);

	for (int i = 0; i < 10; ++i) {
		printf("%d", p[i]);
	}

	if (p != nullptr) {
		free(p);
		p = nullptr;
	}
}

int main() {
	test();
	return 0;
}

运行结果

 

 

 

 

sscanf

 

 

 

1、跳过数据

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

//%*s或%*d  跳过数据
void test01() {
#if 0
	char *str = "12345abcde";
	char buf[1024] = { 0 };
	sscanf(str, "%*d%s", buf);
	printf("buf:%s\n", buf);
#else
	const char *str = "abcde 12345";
	char buf[1024] = { 0 };
	sscanf(str, "%*s%s", buf);
	printf("buf:%s\n", buf);
#endif
}

注意:如果else里面的str是“abcde12345”,则得到的结果是,因为要有边界

忽略字符串到空格或者\t

运行结果

 

 

2、%[width]s 读取指定宽度的数据

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

void test01() {

	char *str = "12345abcde";
	char buf[1024] = { 0 };
	sscanf(str, "%5s", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果

 

 

3、%[a-z] 匹配a到z中任意字符(尽可能多匹配)

注意:他会从第一个元素开始匹配,如果匹配不上,则直接退出,哪怕你后面的元素匹配的上。

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

void test01() {

	char *str = "abcde12345";
	char buf[1024] = { 0 };
	sscanf(str, "[a-z]", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果

如果把"abcde12345"改成"12345abcde",则运行结果会变成:

可以把条件改成%*d%[a-z],这样就能匹配到abcde了

 

 

4、%[aBc]  匹配a、B、c中的一员,贪婪性

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

void test01() {

	const char *str = "aABbcde";
	char buf[1024] = { 0 };
	sscanf(str, "%[aAb]", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果:

 

 

5、%[^c]   匹配非c的任意字符,贪婪性   遇到c就退出

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

void test01() {

	const char *str = "aABbcde";
	char buf[1024] = { 0 };
	sscanf(str, "%[^c]", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果:

 

 

6、%[^a-z] 表示取a-z之外的所有字符   遇到就退出

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

void test01() {

	const char *str = "12345aABbcde";
	char buf[1024] = { 0 };
	sscanf(str, "%[^a-z]", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果

 

 

实例:ip地址匹配

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

void test01() {

	const char *ip= "127.0.0.1";
	int num1 = 0, num2 = 0, num3 = 0, num4 = 0;
	sscanf(ip, "%d.%d.%d.%d", &num1 , &num2, &num3, &num4);
	printf("num1:%d\n", num1);
	printf("num2:%d\n", num2);
	printf("num3:%d\n", num3);
	printf("num4:%d\n", num4);

}

int main() {
	test01();
	return 0;
}

运行结果:

 

 

实例2

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

void test01() {

	const char *str="abcde#12uiop@0p1ju";
	char buf[1024]={0};
	sscanf(str, "%*[^#]#%[^@]", buf);
	printf("buf:%s\n", buf);

}

int main() {
	test01();
	return 0;
}

运行结果

 

 

 

 

查找子串 strstr

我面滴滴的时候被问过怎么写这个

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

char* myStrStr(const char* str, const char* sub){
	const char* father=str;
	const char* f=NULL;
	const char* son=sub;
	const char* s=NULL;	

	while(*father!='\0'){
		if(*father==*son){
			f=father;
			s=son;
			while(*f==*s){
				f++;
				s++;
			}
			if(*s=='\0'){
				return father;
			}
		}
		father++;
	}
	return NULL;

}

void test(){
	char *str="abcdefghi0";
	char *sub="ghi";
	char *res=myStrStr(str, sub);
	printf("res=%s\n",res);
}

int main() {
	test();
	return 0;
}

 

 

 

 

 

指针易错点

 

1、指针越界

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


void test() {
	const char buf[3] = "abc";
	printf("buf=%s\n", buf);
}

int main() {
	test();
	return 0;
}

 

 

 

2、指针叠加会改变指针指向

        char*p = (char*)malloc(64);
	++p;
	if (p != NULL) {
		free(p);
		p = NULL;
	}

 

 

3、返回局部变量地址

char* test() {
	char str[] = "abcdefg";
	return str;
}

 

 

4、同一块内存释放多次

 

 

 

const使用

在使用对象指针的时候,有可能意外的修改数据。使用const可以规避地址传递的副作用。

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

struct Person {
	char name[64];
	int age;
	int ID;
	double score;
};

void Print(const struct Person *person) {
	printf("name=%s age=%d ID=%d score=%f\n", 
		person->name, person->age, person->ID, person->score);
}

void test() {
	struct Person person = { "Trump", 30, 250, 59.9 };
	Print(&person);
}
int main() {
	test();
	return 0;
}

 

不过加了const也可以改

void Print(const struct Person *person) {

	//强制类型转换
	struct Person *p = (struct Person *)person;
	p->ID = 1299;
	printf("name=%s age=%d ID=%d score=%f\n", 
		person->name, person->age, person->ID, person->score);
}

 

 

 

 

二级指针练习文件读写

要求:文件有多少空间就申请多少空间

 

第一版本——读取文件行数,开辟相应大小的内存空间。将每一行都存入buf中

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

//获得文件行数
int getFileLines(FILE *file){
	if(file==NULL) return -1;

	//按行读取  fgets();
	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((file==NULL)||(contents==NULL)||(lines<=0))
		return;

	//创建缓冲区
	char buf[1024]={0};
	while(fgets(buf, 1024, file)!=NULL){
		printf("nuf=%s\n",buf);
		
		memset(buf, 0, 1024);
	}

}

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

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

	//开辟空间
	char **pContents=malloc(sizeof(char*)* lines);
	readFileData(file, lines, pContents);
}

int main() {
	test();	
	return 0;
}

运行结果:

 

 

 

第二版——将每一行数据读取出来并申请相应大小的内存。最后将数据打印出来

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

//获得文件行数
int getFileLines(FILE *file){
	if(file==NULL) return -1;

	//按行读取  fgets();
	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((file==NULL)||(contents==NULL)||(lines<=0))
		return;

	//创建缓冲区
	char buf[1024]={0};
	int index=0;
	while(fgets(buf, 1024, file)!=NULL){
		//printf("nuf=%s\n",buf);
		//获取当前行的长度  +1是为了'\0'
		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 test(){
	//打开文件
	FILE *file=fopen("./file.txt", "r");
	if(file==NULL){
		printf("打开文件失败");
		return;
	}

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

	//开辟空间
	char **pContents=malloc(sizeof(char*)* lines);
	//读取文件内容
	readFileData(file, lines, pContents);

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

	
}

int main() {
	test();	
	return 0;
}

运行结果:

 

 

第三版(完整版)——释放内存

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

//获得文件行数
int getFileLines(FILE *file){
	if(file==NULL) return -1;

	//按行读取  fgets();
	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((file==NULL)||(contents==NULL)||(lines<=0))
		return;

	//创建缓冲区
	char buf[1024]={0};
	int index=0;
	while(fgets(buf, 1024, file)!=NULL){
		//printf("nuf=%s\n",buf);
		//获取当前行的长度  +1是为了'\0'
		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;
		}
	}

	if(contents!=NULL){
		free(contents);
		contents=NULL;
	}

}

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

	//统计文件行数
	int 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();	
	return 0;
}

运行结果:

 

 

 

 

位运算

 

位逻辑运算符

 

按位取反~

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

void test() {
	//2 的二进制是010  按位取反为101  
	//负数使用补码存储 补码的计算方式是除了最高位,其余的按位取反然后再加1
	//因此101的补码为 110+1=111
	int number = 2;
	printf("~number:%d\n", ~number);
}
int main() {
	test();
	return 0;
}

运行结果:

 

位与(AND):&

可以判断指定位置是否为0

可用于判断某一位上是0还是1(计算快速幂的时候需要判断幂次的二进制形式的每一位都是什么)

也可以用这个判断一个数的奇偶性

注意:&的优先级小于“==”

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

void test() {
	int number = 332;
	if ((number & 1 )== 0) {
		printf("%d是偶数\n", number);
	}
	else {
		printf("%d是奇数\n", number);
	}
}
int main() {
	test();
	return 0;
}

number = number & 0;

等价于

number &=0;

 

位或(OR):|

可以判断指定位置是否为1

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

void test() {
	int num1 = 5;
	int num2 = 3;
	printf("num1 | num2 = %d\n", num1 | num2);
}
int main() {
	test();
	return 0;
}

运行结果:

 

 

位异或

同则 0 ,不同则 1

交换两个数字的值的例子

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

void test() {
	int num1 = 5;
	int num2 = 3;

	num1 = num1 ^ num2;
	num2 = num1 ^ num2;
	num1 = num1 ^ num2;
	printf("num1 =%d num2 = %d\n", num1 , num2);
}
int main() {
	test();
	return 0;
}

运行结果:

 

 

 

移位运算符

 

左移——相当于乘以2的n次幂

001<<1 变成 010

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值