C语言第七天


申请内存:malloc
释放内存:free
悬空指针:释放后,没有被及时置为NULL的指针。
野指针:没有初始化的指针

●关于程序运行时的内存区域:

静态(全局)数据区:全局变量,static类型的局部变量
常量数据区:存储的是字符串
代码区:代码
栈区:局部变量,函数的参数
堆区:malloc出来的区域
在这里插入图片描述

int g_Num = 0;
void Fun(int nParam)
{
	printf("参数地址:%p\n", &nParam);
	static int nNum = 0;
	printf("静态局部:%p\n", &nNum);
}
int main()
{
	int nTest1 = 0;
	int nTest2 = 0;
	printf("局部变量:%p\n", &nTest1);
	printf("局部变量:%p\n", &nTest2);
	Fun(10);
	printf("全局变量:%p\n", &g_Num);

	int *p1 = (int *)malloc(10);
	int *p2 = (int *)malloc(10);
	printf("堆:%p\n", p1);
	printf("堆:%p\n", p2);

	printf("常量区:%p\n", "hello world");
	printf("常量区:%p\n", "hello 15pb");

	return 0;
}

●指针的进阶

指针的算数运算:

1 指针+整数 或者 指针-整数

struct _TEST
{
	int nNum;
	int hNum;
	int cCh;
};


int main()
{
	int *p1 = NULL;
	double * p2 = NULL;
	char *p3 = NULL;
	short * p4 = NULL;
	_TEST *p5 = NULL;
	p1 = (int*)100;
	p2 = (double*)100;
	p3 = (char*)100;
	p4 = (short*)100;
	p5 = (_TEST*)100;
	printf("int*\n");
	printf("%d\n", p1);
	printf("%d\n", p1+1);
	printf("double*\n");
	printf("%d\n", p2);
	printf("%d\n", p2 + 1);
	printf("char*\n");
	printf("%d\n", p3);
	printf("%d\n", p3 + 1);
	printf("short*\n");
	printf("%d\n", p4);
	printf("%d\n", p4 + 1);
	printf("_TEST*\n");
	printf("%d\n", p5);
	printf("%d\n", p5 + 1);
	return 0;
}

在这里插入图片描述

一个整型指针只能指向一个整型变量吗????

	//内存没有类型的
	char nNum = 'm';
	int * p1 = (int*)&nNum;
	char* p2 = (char *)&nNum;
	*p1 = 200;
	*p2 = 'a';

在这里插入图片描述
修改之后,通过int*修改一个字符型变量,也是可以的。但是这样是非常危险的。
此时就已经溢出了。
在这里插入图片描述

指针和一维数组的关系:

int main()
{
	int nNum = 0;
	short hNum = 100;
	nNum = hNum;//为什么int和short能够相互赋值
	            //因为类型比较类似

	int* p = NULL;
	//p = nNum ;//int类型为什么不能赋值给int*??
	          //因为类型相差太大
	int Arr[10] = {3,2,1,4,6,8,5,3,2,1};

	p = Arr;//数组名字,就是数组的起始地址
			//数组名的类型和一级指针类型相似
	
			//指针+n 相当于  指针+n*sizeof(类型)
	//为了便于访问每一个数据
	for (int i = 0; i <10; i++)
	{
		printf("%p:%d ",p+i, *(p + i));
	}
	printf("\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%p:%d ", p + i, p[i]);
	}
	printf("\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%p:%d ", Arr + i, Arr[i]);
	}
	printf("\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%p:%d ", Arr + i, *(Arr+i));
	}
	printf("\n");

	return 0;
}

指针和数组有什么区别呢????

1 指针是一个变量,数组是一个常量

int main()
{
	int* p = NULL;
	//p = nNum ;//int类型为什么不能赋值给int*??
	          //因为类型相差太大
	int Arr[10] = {3,2,1,4,6,8,5,3,2,1};
	p = Arr;//数组名字,就是数组的起始地址
			//数组名的类型和一级指针类型相似
	printf("%d", *p);
	p = p+1;
	printf("%d", *p);
    
	printf("%d", *Arr);
	//Arr = Arr + 1;数组名是数组的地址,是一个常量
	return 0;
}

2 指针取地址,能够得到指针变量的地址,数组取地址,数值不变,类型改变

int main()
{
	int* p = NULL;
	//p = nNum ;//int类型为什么不能赋值给int*??
	          //因为类型相差太大
	int Arr[10] = {3,2,1,4,6,8,5,3,2,1};

	p = Arr;//数组名字,就是数组的起始地址
			//数组名的类型和一级指针类型相似
	printf("%p \n", p);
	printf("%p \n", &p);//二级指针 int **p; 二级指针不展开讲
                        //存储指针变量地址的变量
	printf("%p \n", Arr);
	printf("%p \n", &Arr);//变成了数组指针类型

	return 0;
}

在这里插入图片描述
理解了上面的知识之后,我们需要了解一下,这些都有什么用???

应用1:一维数组的参数传递

//void fun(int arr[])
//void fun(int* arr)
void fun(int arr[10])
{
	printf("%d\n", sizeof(arr));//输出4,再次证明这是个指针
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *arr);
		arr++;
	}
}

//在函数内部可以对数组中的元素进行修改
void fun2(int arr[10])
{
	
	for (int i = 0; i < 10; i++)
	{
		arr[i] = arr[i] * 2;
	}
}


int main()
{
	int nArr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d\n", sizeof(nArr));
	fun2(nArr);
	return 0;
}

应用2:堆空间的使用

int main()
{
	int n = 10;
	int *p = (int*)malloc(n * sizeof(int));

	for (int i = 0; i < n; i++)
	{
		scanf_s("%d", &p[i]);
		//scanf_s("%d", p+i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d", p[i]);
	}
	return 0;
}

注意点1:在使用函数时,需要注意的陷阱

Int main()
{
Printf("hello world");

Printf("%p", "hello world");

Char *a = NULL;
Char *p = (char *)malloc(20);
Char buf[10];
//When we look at a function, the argument to the function is a pointer
/ / We should understand that the function does not need a pointer type
// is the address of a valid space.
Strcpy_s(a, 10, "hello");
Strcpy_s(p, 10, "hello");
Strcpy_s(buf, 10, "hello");
Return 0;
}

指针和二维数组:

int main()
{
	int  Arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };


	//int *p = Arr;这个是不行的。
    
	//我们需要用数组指针来存储二维数组的地址
	//这个数组指针在定义的时候,指明了一排4个元素
	int(*p)[4] = Arr;
	//这个数组指针,指明了一排5个元素
	//不能存储一排4个元素的二维数组首地址
	//int(*p)[5] = Arr;
	return 0;
}

●二维数组和数组指针的用法:

二维数或者数组指针+1 是加了一排
二维数组或者数组指针 1 解引用 2 做一次下标运算 会得到一维数组类型 再解引用或者下标运算就会得到数据。

int main()
{
	int  Arr[3][4] = { 
		1,2,3,4,
		5,6,7,8,
		9,10,11,12 };

	int(*p)[4] = Arr;

	printf("%d", Arr[1][1]);

	printf("%d", p[1][1]);


	printf("%d", *(*(Arr+1)+1));
	printf("%d", *(Arr[1] + 1));
	printf("%d", (*(Arr+1))[1]);


	printf("%d", *(*(p + 1) + 1));
	printf("%d", *(p[1] + 1));
	printf("%d", (*(p + 1))[1]);

	printf("\n");

	printf("%p ", Arr);
	printf("%p ", Arr + 1);
	printf("\n");
	printf("%p ", *Arr);
	printf("%p ", *Arr + 1);
	printf("%p ", *(*Arr + 1));
	printf("\n");
	printf("%p ", Arr[0]);
	printf("%p ", Arr[0] + 1);
	return 0;
}

应用1:

1 二维作为参数传递的时候

//      int Arr[][4]
//      int (*Arr)[4]
void Fun(int Arr[3][4])
{
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d",Arr[i][j]);//*(*(Arr+i)+j)
		}
	}
}



int main()
{
	int  Arr[3][4] = { 
		1,2,3,4,
		5,6,7,8,
		9,10,11,12 };

	Fun(Arr);

	return 0;
}

2 堆空间的使用

int main()
{
	int(*p)[10] = (int(*)[10]) malloc(40 * sizeof(int));

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			p[i][j] = i + j;
		}
	}

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			printf("%-4d",p[i][j]) ;
		}
		printf("\n");
	}
	return 0;
}

关于数组取地址的问题

int main()
{
	int nArr[10] = { 1,2,3,4,5 };

	printf("%d\n", nArr);
	printf("%d\n", nArr+1);
	printf("%d\n", &nArr);
	printf("%d\n", &nArr+1);
	return 0;
}

在这里插入图片描述

数组指针和指针数组的问题:

口诀:数组指针是指针,指针数组是数组。
整型:

int main()
{
	int(*p)[10] = (int(*)[10]) malloc(40 * sizeof(int));



	int m = 0;
	int n = 10;
	int l = 20;

	int* Arr[10] = { &m,&n,&l };

	printf("%d ", *Arr[0]);

	printf("%d ", *Arr[1]);

	printf("%d ", *Arr[2]);
	return 0;
}

字符类型:
在这里插入图片描述

int main()
{
	const char *p = "hello world";

	char buf1[20] = { "hello wrold" };




	const char* Arr[3] = {
	"hello world",
	"hello 15pb",
	"hello xiaoming"
	};

	char buf[3][20] = {
		"hello world",
		"hello 15pb",
		"hello xiaoming"
	};


	printf("%s\n", Arr[0]);
	printf("%s\n", buf[0]);


	printf("%c\n", Arr[0][1]);
	printf("%c\n", buf[0][1]);

	return 0;
}

●结构体指针

typedef struct _INFO
{
	int x;
	int y;
}INFO,*PINFO;

int main()
{
	int Arr[3] = { 100,200,400 };
	_INFO obj = {10,20};
	printf("%d %d", obj.x, obj.y);



	_INFO* p = (_INFO*)&Arr;
	//PINFO p
	printf("%d %d", p->x, p->y);

	return 0;
}

●文件操作:

fputc fgetc

int main()
{
	//在文件打开了之后,有一个文件读写位置
	//w  开头  
	//r  开头
	//a  结尾



	//FILE* pFile = NULL;
	1 打开文件 
	为什么要传指针的地址,因为函数中要修改pFile的内容
	//fopen_s(&pFile,"D:\\123.txt", "a");

	//
	2 写文件,打开一次,读一次
	//fputc('a', pFile);
	//fputc('b', pFile);
	//fputc('c', pFile);
	3 关闭文件
	//fclose(pFile);
	FILE* pFile = NULL;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile, "D:\\123.txt", "r");


	//2 读文件,打开一次,读一次
	
	char cCh = 0;
	while (cCh!=EOF)
	{
		cCh = fgetc(pFile);//每读取一次,位置都会加1
		                   //从而保证下次读取读下一个字符 
		printf("%c", cCh);
	}
	//3 关闭文件
	fclose(pFile);
	return 0;
}

关于文件读写位置

int main()
{


	FILE* pFile = NULL;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile,"D:\\123.txt", "a+");

	
	//2 写文件,打开一次,读一次
	fputc('a', pFile);
	fputc('b', pFile);
	fputc('c', pFile);

	fseek(pFile, 0, SEEK_SET);//当我们写入文件之后,直接读取
	                          //是读不到数据的
	                          //因为读写位置,在刚写完的位置之后
	                          //所以我们需要用这个函数调整位置
	char cCh = 0;
	while (cCh != EOF)
	{
		cCh = fgetc(pFile);//每读取一次,位置都会加1
						   //从而保证下次读取读下一个字符 
		printf("%c", cCh);
	}

	fclose(pFile);
	return 0;
}

关于加’b’和不加’b’的问题。(二进制和非二进制问题)

int main()
{


	FILE* pFile = NULL;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile,"D:\\123.txt", "a+");

	
	//2 写文件,打开一次,读一次
	fputc('a', pFile);
	fputc('b', pFile);
	fputc('\n', pFile);
	fputc('c', pFile);
	//3 关闭文件
	fclose(pFile);
	return 0;
}

在这里插入图片描述
加了b,就不会转换。

int main()
{


	FILE* pFile = NULL;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile,"D:\\123.txt", "a+b");

	
	//2 写文件,打开一次,读一次
	fputc('a', pFile);
	fputc('b', pFile);
	fputc('\n', pFile);
	fputc('c', pFile);
	//3 关闭文件
	fclose(pFile);
	return 0;
}

在这里插入图片描述

fputs fgets

int main()
{


	//FILE* pFile = NULL;
	1 打开文件 
	为什么要传指针的地址,因为函数中要修改pFile的内容
	//fopen_s(&pFile,"D:\\123.txt", "w+b");

	//
	2 写文件,打开一次,读一次
	//fputs("hello world", pFile);
	3 关闭文件
	//fclose(pFile);



	FILE* pFile = NULL;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile, "D:\\123.txt", "r+b");


	//2 写文件,打开一次,读一次
	char buf[200] = {0};
	fgets(buf,200, pFile);
	//3 关闭文件
	fclose(pFile);
	return 0;
}

fprintf fscanf

int main()
{


	//FILE* pFile = NULL;
	1 打开文件 
	为什么要传指针的地址,因为函数中要修改pFile的内容
	//fopen_s(&pFile,"D:\\123.txt", "w+b");

	//
	2 写文件,打开一次,读一次
	//fprintf(pFile, "%d %d %d", 100, 200, 300);
	3 关闭文件
	//fclose(pFile);



	FILE* pFile = NULL;
	int a=0, b=0, c = 0;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile, "D:\\123.txt", "r+b");


	//2 写文件,打开一次,读一次
	char buf[200] = {0};
	fscanf_s(pFile, "%d %d %d", &a, &b, &c);
	//3 关闭文件
	fclose(pFile);
	return 0;
}

fwrite fread

操作的是内存

int main()
{

	int Arr[5] = { 100,200,300,400,500 };
	//FILE* pFile = NULL;

	1 打开文件 
	为什么要传指针的地址,因为函数中要修改pFile的内容
	//fopen_s(&pFile,"D:\\123.txt", "w+b");

	//
	2 写文件,打开一次,读一次
	//fwrite(
	//	Arr,//写入内存的起始位置
	//	4,  //写入一个元素的大小
	//	5,  //写入多少个元素
	//	pFile
	//);
	3 关闭文件
	//fclose(pFile);



	FILE* pFile = NULL;
	int a=0, b=0, c = 0;
	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile, "D:\\123.txt", "r+b");


	//2 写文件,打开一次,读一次
	int nArr2[5] = { 0 };
	fread(nArr2, 4, 5, pFile);
	
	//3 关闭文件
	fclose(pFile);
	return 0;
}

小技巧:获取文件的大小

int main()
{

	FILE* pFile = NULL;

	//1 打开文件 
	//为什么要传指针的地址,因为函数中要修改pFile的内容
	fopen_s(&pFile,"D:\\123.txt", "r+b");

	
	//2 获取文件大小

	fseek(
		pFile,//文件流
		0,    //相对于第三个参数的偏移
		SEEK_END
	);
	int n = ftell(pFile);//告知文件当前的读写位置
	
	//3 关闭文件
	fclose(pFile);
	printf("文件大小为%d字节", n);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值