1.函数和指针的关系
int a = 123;
int b = 234;
int *pa = &a;
int * pb = &b;
请用一个函数,修改pa和pb的指向,实参和形参同时修改(day07/01fun.c))。
备注:在函数中,如果想修改形参一级指针指向,需要传入二级指针。
注意: 不可以返回局部变量的地址。()
2.malloc和函数的关系
案例:
int* funmalloc()
{
// int a = 123;
int* p =(int*)malloc(sizeof(int));
// p = &a;
*p = 123;
// return p;
}
注意: 在函数中动态申请的内存,需要在函数内部是否,要么需要返回首地址给调用者,否则会出现内存泄漏。
注意:
/* 错误 p所指向的内存空间不允许修改,*p指向一级指针,所以不可以修改一级指针指向(查看大门01fun.c)查看图片(函数和malloc)
void funconst( const int** p)
{
*p = (int*)malloc(sizeof(int));
}*/
3.函数指针和指针函数
指针函数:是返回值是指针的函数 int* fun(int a){ }
函数指针:是一个指针,指向某一个函数。
函数指针格式: int (*fun)(int a); fun是指针,指向参数是int 返回值是int的函数,指针的类型 int (*) (int a); fun指向的类型 int p(int a);
使用:函数指针名(参数); 例如void (*pfun)(int a) = fun; pfun(10);
命别名: typedef int (*pf)(int a,int b) ;其中pf是指针类型。
定义了一个函数指针类型为int (*)(int ,int)
定义指针变量: 函数指针类型 指针变量= 函数名
注意:给指针变量赋值,需要匹配相同的指针类型,则返回返回值和函数的参数要和指针类型相匹配,否则是类型不匹配错误。
一般情况下 函数指针名都会用callback
当数组作为函数参数时,会隐式转换成指针使用。
回调函数:把函数指针当做函数参数,系统回调函数是系统自动调用函数。
案例如下:用一个函数计算两个数的加减乘除。
#include<stdio.h>
void fun(int a){printf("a = %d\n",a);}
//定义一个函数指针类型
typedef int (*pf)(int a,int b) ; 其中 pf是类型,不是变量名
//可以这么理解typedef int(*)(int a) pf; pf是别名,是类型,这个格式会语法错误。
void fun(int a){ printf("a = %d\n",a);}
int add(int a,int b){ printf("add = %d\n",a);}
int sub(int a,int b){ return a-b;}
int jisuan(int a,int b,pf function)//pf function = sub
{
return function(a,b);
//function只是一个变量,实参是什么则funciotn是什么。
}
int main(int argc,char *argv[]){
//pfun是指针名,是变量,不是类型
void (*pfun)(int a) = fun;
pfun(10);//实际调用的是fun函数,打印a=10;
pf pmyfun = add;//pmyfun指向add函数,
pmyfun(1,3);
printf("a-b=%d\n",jisuan(2,4,sub));
return 0;
}
二、数组和指针的关系
1.数组名: 数组名就是数组的首地址。数组名是const指针,不可以指向其他的位置,只可以指向数组的首地址。int arr[10] = {0},arr 和&arr[0]相同。arr++是错误的。
练习:定义一个数组,10个指向int类型的指针。int* arr[10];
2.指针数组: 是数组,在数组中存放 指针 int* arr[10];
3.数组指针:是指针,指向某一个数组。
意义: int (*arr)[10]; 一个指针,指向一个数组,数组大小是10个元素; int (*arr)[10] = &arr;
定义一个结构体数组指针。指向3个元素的数组。结构体(id ,age)(代码:day07/04arrPointTest.c)
4.指针数组指针:是一个指针,指向一个数组,数组中存放的是指针。
int* (*arr)[10]; stu* (*arr)[10] ;
自学: 定义一个数组指针,数组中粗放的是10个元素的数组指针。
5.二维数组和指针关系
二维数组名是指针,指向第一行的第一个元素。
二维数组名加1实际加了一行元素
二维数组名[下标] 表示某一行的首地址。类型是一维数组,加一实际加了一行。
二维数组名[行标][列标];某一行行的某一个元素,加一实际加了一个元素的值。
案例:
#include<stdio.h>
typedef struct student
{
int id;
int age;
}stu;
int main(int argc,char *argv[])
{
//定义结构体数组
stu arr[3]={{1,2},{2,3},{3,4}};
//strcpy 给字符串赋值才用
//定义一个结构体数组指针。
stu (*parr)[3] = &arr;
//如下代码,理解方式如下
//parr = &arr; *parr = arr =&arr[0]
//**parr=arr[0];
(**parr).id = 123; //arr[0].id
(*parr)[1].id = 234;//arr[1].id
printf("parr = %p\nparr+1=%p\n",parr,parr+1);//加24,指针加一加的是一个类型大小。
stu doubleArr[2][3] = {0};
parr = doubleArr;
printf("parr = %p\n",parr);
printf("parr+1 = %p\n",parr+1);
printf("doubleArr = %p\n",doubleArr);
printf("doubleArr+1 = %p\n",doubleArr+1); :
printf("doubleArr[0] = %p\n",&doubleArr[0]);
printf("doubleArr[0] +1 = %p\n",&doubleArr[0]+1); 加一行
printf("doubleArr[0][0]+1 = %p\n",&doubleArr[0][0]+1); 加一个
printf("doubleArr[1] = %p\n",&doubleArr[1]);
printf("doubleArr[1][0] = %p\n",&doubleArr[1][0]);
return 0;
}
三、指针和结构体关系
1.定义结构体 :struct 结构体名{ };
2.定义结构体指针:struct student* pa;
3.案例:
4.使用:通过结构体指针操作结构体中的成员变量,需要用到->
5.例子:
#include<stdio.h>
struct student
{
int id;
int age;
char name[10];
};
int main(int argc,char *argv[])
{
struct student danny;
struct student huqing;
struct student* pd = &danny;
danny.id = 123;
pd->age = 12;
//pd.id = 123; 错误
pd=&huaqing;
pd->id = 12;
printf("%d\n",huaqing.id);
printf("%d\n",pd->id);
return 0;
}
案例:定义一个结构体指针数组,有3个学生信息(int id;int age;char name[10];)请通过scanf输入的方式对结构体数组进行赋值。
#include<stdio.h>
#include <stdlib.h>
typedef struct student{ int id; int age; char name[10];}stu,*pstu;
//这么理解typedef struct student* pstu;
//这么理解typedef struct student stu;
int main(int argc,char *argv[]){
pstu pa[3] = {0};//等价于struct student* pstu
pa[0] = (pstu)malloc(sizeof(stu)*3);
printf("请输入学生信息(id,age,name)");
scanf("%d %d %s",&(pa[0]->id),&(pa[0]->age),pa[0]->name);
printf("%d\t%d\t%s\n",pa[0]->id,pa[0]->age,pa[0]->name);
return 0;
}
四、指针和字符串的关系
1.字符串的保存方式
字符串数组: char arr[10] = "abc";
字符串指针: char * p = "bac";
2.字符指针指向的是字符串的首地址,第一个字符的地址
3.相同的字符串常量在内存中只会存储一份,但凡有字母不一样则是不同的字符串。
五、文件操作
1. 文件存在磁盘中,是长期存储的。
2.使用:一般用于写日志文件,日志是程序运行中的各种信息,当程序出现bug时定位问题的位置。一般日志文件都是.log结束,和txt文件一样。
3.文件可以长期保存信息,比如passwd文件。
文件:存储在外部介质上的数据的集合,操作系统管理数据的最小单位。长期存储,不会因为断电而丢失,分为文本文件和二进制文件。
4.读文件:从磁盘上的文件中读取到计算机内存中
写文件:从计算机内存中的内容写入到文件中。
5.文件操作四步骤(文件FILE,和文件操作相关的函数是以f开头)
1> 打开文件:fopen
FILE *fopen(const char *path, const char *mode);
作用:打开文件或者创建文件
参数: path 需要打开或者创建的文件路径,默认是当前目录下,也是字符串
mode:打开或者创建的模式,是字符串
返回值:FILE的指针,文件指针,保存的是文件的基本信息(文件大小等)后续所有的函数操作都需要用到文件指针FILE*
模式如下:
r 只读的方式打开文件.文件指针在文件的开始位置,如果文件不存在则打开失败
r+ 以读写的方式打开文件,指针在文件开始的位置.如果文件不存在则打开失败
w 以写的方式打开文件,文件的长度初始化成0,所有内容会清空.如果文件不存在则创建.
w+ 读写的文件打开文件,如果文件存在则清空,不存在则创建
a 追加(只写)的方式打开文件,文件不存在则创建,光标在文件末尾;
a+ 读写的追加方式打开文件,光标在文件末尾,如果文件不存在则创建,存在不会清空。
读写文件:fgets fputs fgetc fputs fscanf fprintf fread fwrite
文件偏移:ftell fseek rewind
文件关闭:fclose
int fclose(FILE *stream);
作用:关闭文件
参数:stream是文件指针,是fopen的返回值。
作业:
void strcat1(char *a ,const char *b)
{
int n,m,i,j;
n=strlen(a);
m=strlen(b);
for(i=n,j=0;i<n+m;i++,j++)
{
a[i]=b[j];
}
a[i]='\0';
}
int main(int argc,char *argv[])
{
char W[100];
char M[50];
printf("请输入两个字符串!\n");
scanf("%s %s",W,M);
strcat1(W,M);
printf("之后:%s\n",W);
return0;
}