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;
}