一、指针的定义
1.int* p = &a;
指向改变: p = &b;
指向内容改变:*p = 123;
2.指针运算
指针和指针相加是非法。
指针和指针相减是可以的;
double a[2] = {0}; double* p=&arr[0]; double*q = &arr[1]
q指向a[1], 计算q-p =1;表示相差几个double
指针加常量是可以的 p+1是可以的 p+2越界是错误的;
指针可以++ ,--
3.解引用
得到指针所指向的内存空间的值。
在指针前加*,例如 int a = 10; int* p = &a; *p=110;
给指针赋值 指针指向发生改变。(非常重要)
4.野指针
野指针:不指向某一个固定位置的指针。要防止野指针出现
空指针:指向NULL的指针。当指针不知道指向哪里时,则赋值为NULL;
段错误:核心已转储,操作了一块不允许操作的内存控件,数组越界,操作了野指针,用空指针解引用。
void指针:通用型指针,可以保存任何类型的地址。使用前需要进行强制类型转换 。(int*)p;
5.常指针(面试题):只有const挨着指针的时候才是修饰指针,指针指向不可以改变。
指针常量:
const int *p = &a; p=&b 允许;但*p= 22是错误的; b=22可以的;
常量指针:
int * const p = &a; p=&b不允许;但*p= 22;
const int* const p=&a; 指针的指向和所指向的内存空间都不允许修改
补充:如果定义一个const变量,用指针指向变量后,可以通过解引用修改
const int a = 123;
int *p = &a;
*p=222; 允许;
补充: 直接用指针给指针赋值是可以的
int a = 123;
int *p = &a;
int *q = p; 是可以的,可以用指针直接给指针赋 值。
二、二级指针
1.二级指针:保存的是一级指针的地址;同样是8个字节。
一级指针:保存的是变量的地址。
2.格式:数据类型** 二级指针变量名 = &一级指针变量名;
案例: int a = 123; int* p = &a; int** pp= &p;
命名规则: pp_num; ppNum; PpNum;
3.使用案例:
int main(int argc,char *argv[])
{
int a = 123;
int b = 234;
int* p = &a;
int * q = &b;
printf("*p %d\n",*p);
int ** pp = &p;//二级指针不可以是野指针
*pp = &b; //pp=&&a 错误
printf("*p =%d,**pp =%d\n",*p,**pp);
pp = &q; //pp指向q,
printf("*p =%d,**pp =%d\n",*p,**pp);
return 0;
}
三:动态内存分配
1. 总共分为:代码区,数据区,堆区,栈区
根据地址从小到大分布如下:
代码区:存放代码,不可以修改
数据区: 静态常量区 :静态变量和常量
全局变量区:已初始化的全局变量
bss段:未初始化的全局变量
堆区:动态内存申请,自己申请自己释放,先从低地址分配,在分配高地址。
栈区:存放局部变量,自动申请自动释放,先从高地址分配,分配低地址空间,堆区和栈区无明显的分割线。
2. 动态内存申请
void *malloc(size_t size);
作用:在堆空间中申请内存。
参数: size 需要申请的内存大小
头文件: #include <stdlib.h>
返回值:申请到的内存空间的首地址。
int* p = (int*)malloc(8);
注意:malloc申请的内存空间没有初始化时,存放的是乱值。
malloc的返回值需要强制类型转换才能使用。
malloc申请的内存空间,用完后需要释放。
案例:用malloc申请一块内存,存放3个学生的信息,学生(id,age);代码如下:
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student
{
int id;
int age;
}stu;
int main(int argc,char *argv[])
{
//连续内存空间可以使用下标操作
// 结构体数组 stu arr[3]; p=&arr[0];
stu* p = (stu*)malloc(3*sizeof(stu));
if(NULL == p)
{
perror("malloc");//打印错误信息
exit(0); //结束程序
}
memset(p,0,sizeof(stu)*3);//malloc申请的空间是乱值。
for(int i = 0; i < 3;i++)
{
printf("请输入学生id和age\n");
scanf("%d %d",&p[i].id,&p[i].age);
}
for(int j = 0;j < 3;j++)
{
printf("第%d个学生的id是%d age是%d\n",j+1,p[j].id,p[j].age);
}
//相同数据类型连续的集合
int* pa = (int*)malloc(10*sizeof(int));
printf("%p,%p\n",pa+1,pa);//相差4字节
//如下会出现野指针
//int a = 123;
//pa = &a; malloc申请的内存pa不再指向,导致内存泄露
return 0;
}
备注: 对于连续的内存空间可以使用下标操作(重点)
int* p = (int*)malloc(5* sizeof(int));
p[0] = 123;
接受malloc返回值的变量指针,不允许被修改,不可以指向其他位置。如果修改会导致malloc申请的内存没有指针指向,内存泄漏。
void free(void *ptr);
作用:释放动态申请的内存空间,如果不释放内存泄漏
参数:malloc返回的指针
返回值: 无
void *calloc(size_t nmemb, size_t size);
作用:动态申请内存。申请的内存默认初始化成0.
参数:nmemb申请的内存块的个数
size 每一块内存的大小
返回值:申请的内存空间的首地址。
void *realloc(void *ptr, size_t size);
作用:重新申请内存空间。重新分配内存空间
参数: ptr 从ptr地址开始申请,一般malloc的返回值。
size 一共申请size大小。
返回值:重新申请的内存空间首地址。
注意:realloc申请的内存不一定是从ptr开始,当ptr开始的内存不足以有size个的时候,会先释放当前内存后,重新在另外一个区域申请。
四、指针和函数的关系
1.函数:存在代码区(不允许修改)
2.函数名: 就是函数的首地址。 void fun(int x);
3.练习: 请写一个函数,修改两个参数的值。形参和实参都需要修改。
void chage(int *a ,int * b);
4.注意:不可以返回局部变量的地址。
malloc申请的内存空间函数结束依然存在。只有free释放之后才不会存在。
作业:函数中的可变长参数学习 例如 void fun(int a,...);
gdb使用:给大家一个工程,请查看其中变量的值,和调用顺序。
预习:fopen fread fwrite fgets fputs fgetc fputc fclose fseek rewind ftell 的使用。
1.fopen:(打开文件)
头文件:#include<stdio.h>
原型:FILE *fopen(const char *path, const char *mode);
返回值是一个FILE型的指针(文件指针)
**path:**文件名
mode: 权限(文件使用方式) 比如可读,可写,可读可写…
r只读方式打开一个文本文件
rb 只读方式打开一个二进制文件
w 只写方式打开一个文本文件
wb 只写方式打开一个二进制文件
a 追加方式打开一个文本文件
ab 追加方式打开一个二进制文件
r+ 可读可写方式打开一个文本文件
rb+ 可读可写方式打开一个二进制文件
w+ 可读可写方式创建一个文本文件
wb+ 可读可写方式生成一个二进制文件
a+ 可读可写追加方式打开一个文本文件
ab+ 可读可写方式追加一个二进制文件
2.fwrite:(写入文件)
头文件:#include <stdio.h>
原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
ptr:写入的内容 (指针)
**size:**写入多大
**nmemb:**写多少次
**stream:**文件指针
成功写入则返回实际写入的数据个数 就是返回写入次数(nmemb的值)
注意:写完数据后要调用fclose()关闭流,不关闭流的情况下,每次读或写数据后,文件指针都会指向下一个待写或者读数据位置的指针。
3.fseek:(移动光标)
头文件:#include <stdio.h>
原型:int fseek(FILE *stream, long offset, int whence);
stream:文件指针
offset:偏移量(正数表示正向偏移,负数表示负向偏移)
whence:光标位置
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
成功,返回0,失败返回非0值,并设置error的值,可以用perror()函数输出错误。
4.fread:
头文件:#include<stdio.h>
原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
**ptr:**指向要读取的数组中首个对象的指针
**size:**读取多大
**nmemb:**读取多少次
**stream:**文件指针
返回值 是读取了多少次的次数。
5.fclose(FLILE* mm)
关闭文件直接 fclose(文件指针);
char *fgets( char *str, int num, FILE *stream );
函数fgets()从给出的文件流中读取[num - 1]个字符并且把它们转储到str(字符串)中. fgets()在到达行末时停止,在这种情况下,str(字符串)将会被一个新行符结束. 如果fgets()达到[num - 1]个字符或者遇到EOF, str(字符串)将会以null结束.fgets()成功时返回str(字符串),失败时返回NULL.
int fgetc(FILE *stream);
函数说明:从stream文件指针所指向的文件读取1个字符,通过函数的返回值返回。例如:
char c = fgetc(fp);
就是从fp指向的文件,读取1个字符的数据,通过函数返回值,设置给字符变量c。
int fputc(int c, FILE *stream);
函数说明:把参数c表示的字符,写入stream文件指针所指向的文件。
char *fgets(char *buf, int size, FILE *stream);
函数说明:从stream函数指针所指向的文件中,读取size-1个字节到buf缓存,不是读取size个字节,因为,buf[ ]缓存的第size个字节存放字符串结束符,形成一个有效的字符串。
当读取到换行符时,或者到达文件末尾时,结束文件的读取操作。
返回值:读取数据成功,返回存储数据的缓存地址,就是参数buf的地址。如果读取数据失败,返回NULL。例如,读取文件完,没有数据可以再读取,则返回NULL。
int fputs(const char *buf, FILE *stream);
函数说明:把参数buf指针指向的字符串,写入到stream指向的文件。参数buf是一个字符串指针,必须指向一个合法的字符串。合法的字符串是以字符串结束符'\0'结束,表示一个字符串的结尾。
返回值:写入数据成功,返回成功写入的字符个数。失败返回EOF标记。
ftell 函数的原型为:lng ftell(FILE *fp);
该函数用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁前后移动,程序不容易确定文件的当前位置。在使用 fseek 函数后,再调用函数 ftell 就能非常容易地确定文件的当前位置
rewind是C 程序中的库函数。
功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头。
注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
自己的方法:
fprintf();
fscanf();
void write_student_information(int a) //向文件中写入信息
{
int i,k;
FILE* fp;
fp=fopen("gw.txt","r+");
if(fp==NULL)
{
printf("打开文件失败!\n");
return;
}
// fprintf(fp,"学号\t 姓名\t 性别\t 年龄\t 语文成绩\t 数学成绩\t 英语成绩\t 总成绩\n");
for(i=0;i<a;i++)
{
fprintf(fp,"%d\t\t %s\t %s\t\t %d\t\t %d\t\t\t %d\t\t %d\t\t %d\n",W[i].id,W[i].name,W[i].sex,W[i].age,W[i].chinese,W[i].math,W[i].english,W[i].sum);
}
fclose(fp);
}
void read_student_information() //从文件中读取内容
{
int n,i=0;
FILE* fp;
fp=fopen("gw.txt","r+");
if(fp==NULL)
{
printf("打开文件失败!\n");
return;
}
while(fscanf(fp,"%d %s %s %d %d %d %d %d",&W[i].id,W[i].name,W[i].sex,&W[i].age,&W[i].chinese,&W[i].math,&W[i].english,&W[i].sum)!=EOF)
{
i++;
}
vacancy=i;
fclose(fp);
}