指针是什么
指针就是地址。说得再全面一点,指针是一个变量,且这个变量是专门用来存放地址的。
指针的在函数中的两个基本用法:
用法一:
-
当函数需返回多个值时,某些值则需要通过指针返回
-
传入的参数实际上是需要保存带回的结果的变量
例如:找一列数中的最大和最小值并输出
#include <stdio.h> void minmax(int a[],int len,int *min,int *max); int main(void) { int a[]={1,2,3,4,5,6,7,8,9,12,13,17,21,23,55}; int min,max; minmax(a,sizeof(a)/sizeof(a[0]),&min,&max); printf("max=%d,min=%d",max,min); return 0; } void minmax(int a[],int len,int *min,int *max) { int i; *min=*max=a[0]; for(i=0;i<len;i++) { if(a[i]<*min) *min=a[i]; if(a[i]>*max) *max=a[i]; } }
用法二:
-
函数返回运算的状态,结果通过指针返回
-
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错
例如:做两个整数的除法,若除法成功则函数返回1并输出,否则返回0
#include <stdio.h> int divide(int a,int b,int *result); int main() { int a=5,b=2; int c; if(divide(a,b,&c)) printf("%d/%d=%d",a,b,c); return 0; } int divide(int a,int b,int *result) { int ret =1; if(b==0) ret=0; else *result=a/b; return ret; }
指针与数组
-
函数参数表中的数组实际上就是指针
-
对其做sizeof(a)其实是做sizeof(int*)
-
但是可以用数组的运算符[]进行运算
例如以下函数原型为等价的:
-
int sum(int *ar,int n)
-
int sum(int*,int)
-
int sum(int ar[],int n)
-
int sum(int [],int)
-
数组变量是特殊的指针
-
数组变量本身表达地址,所以数组a[]的地址即为a,无需用&取地址
-
数组的单个单元表达的为变量,需要用&取地址,不过a[0]的地址就是a
-
[]可对数组也可对指针做,若一个数组对他做p[0]即相当于*p
-
*运算符可对指针做也可对数组做,例如对数组a[]做 *a=1000,即a[0]=1000
-
数组变量为常量指针,因此不能将一个数组的值赋给另一个数组,但可以将一个数组赋给一个指针
int b[]=a错误,但是int*p=a正确,这是因为int b[]就相当于int *const b
指针运算
加减法
加减整数
有以下程序:
#include <stdio.h> int main() { char ac[]={1,2,3,4,5,6}; char *p=ac; printf("p=%p\n",p); printf("p+1=%p\n",p+1); int ai[]={1,2,3,4,5,6}; int *q=ai; printf("q=%p\n",q); printf("q+1=%p\n",q+1); return 0; }
由此可看出,对指针+1是加sizeof(char)或者sizeof(int),即将指针移到下一个数组的单元格
因此*(p+n)=a[n],但如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
指针相互加减
#include <stdio.h> int main(void) { char ac[]={1,2,3,4,5,6}; char *p=ac; char *p1=&ac[5]; printf("p1-p=%d\n",p1-p); int ai[]={1,2,3,4,5,6}; int *q=ai; int *q1=&ai[5]; printf("q1-q=%d\n",q1-q); return 0; }
由此得,指针相互加减是指两指针的距离为多少;
*p++
取出p所指的那个数据,完事后把p移到下一个位置,常用于数组类的连续空间操作
有以下函数:
#include<stdio.h> int main() { char ac[]={1,2,3,4,5,6,7,8,-1};//此处-1作为跳出循环的条件 char *p=ac; for(p=ac;*p!=-1;) printf("%d\n",*p++); return 0; }
指针比较
-
<,<=,==,>,>,!=都可以对指针做
-
是比较它们在内存中的地址
-
数组中的单元的地址是线性递增的
指针的类型
例如:用p申请了一部分空间,但是在之后的代码中对p的值进行了更改,例如p++,则free(p)会导致错误,只能用p的初始值进行free
-
无论指向什么类型,所有指针的大小都是相同的,因为都是地址
-
指向不同类型的指针不能直接相互赋值
-
void指不知道什么类型的指针
-
动态内存分配
int * a=(int*)malloc(n *sizeof(int));
调用malloc,要求分配n个int大小的内存,malloc就会分配相应大小内存,由于malloc返回的是void*,因此,需要在前面强制类型转换为int *。用完后还要释放空间free
动态内存分配时需调用#include <stdlib.h>函数
例如用动态内存分配实现一个大小可变的数组:
#include <stdio.h> #include <stdlib.h> int main(void) { int number; int *a; int i; printf("输入数组大小:"); scanf("%d",&number); a=(int*)malloc(number*sizeof(int));//之后就可以当做数组用了 for(i=0;i<number;i++) { scanf("%d",a[i]); } free(a); return 0; }
malloc申请的空间是以字节为单位的
如果没空间了,即内存申请失败则malloc返回0,或者叫做NULL;
因此有以下代码可查看可申请多大空间:
#include <stdio.h> #include <stdlib.h> int main() { void *p; int cnt=0; while(p=malloc(100*1024*1024))//1024*1024为1M,每次申请100M空间,然后将申请到的空间交给p,同时p也作为一种条件 cnt++; printf("申请到了%d00Mb空间",cnt); free(p); return 0; }
关于free()
-
申请来的空间需要还给系统
-
只能还申请来的空间的首地址
结构类型
-
在函数内部申明的结构类型只能在函数内部使用
-
尽量在函数外部申明结构类型,这样可被多个函数使用
#include <stdio.h> struct date { int day; int month; int year; }; int main() { struct date today; today.day=23; today.month=5; today.year=2022; printf("Today's days is %d-%d-%d",today.year,today.month,today.day); return 0; }
申明结构的形式
第一种:
struct point{ int x; int y; }; struct point p1,p2;
p1和p2都是point里面有x和y的值
第二种
struct{ int x; int y; }p1,p2;
这个结构无名,p1和p2都是这个无名结构,其中有x和y
第三种
struct point{ int x; int y; }p1,p2;
p1和p2都是point里面有x和y的值
结构运算
-
强制类型转换:p1=(struct point){5,10}//相当于p1.x=5;p1.y=10;
-
p1=p2;//相当于p1.x=p2.x;p1.y=p2.y;
结构指针
-
和数组不同,结构变量的名字并不是结构变量的地址,需使用&运算符
-
struct date *pdate=&today;
结构与函数
-
整个结构可以作为函数的参数的值传入函数
-
这时候是在函数内新建一个结构变量,并复制调用者的结构的值
-
函数也可以返回一个结构
以下函数为输出今天的第二天的年月日:
#include <stdio.h> #include <stdbool.h> int numberofdays(struct date d); bool isleap(struct date d); struct date{ int month; int day; int year; }; int main(void) { struct date today,tomorrow; printf("This date is:"); scanf("%d %d %d",&today.month,&today.day,&today.year); if(today.day!=numberofdays(today)) { tomorrow.month=today.month; tomorrow.day=today.day+1; tomorrow.year=today.year; } else if(today.month==12) { tomorrow.month=1; tomorrow.day=1; tomorrow.year=today.year+1; } else { tomorrow.month=today.month+1; tomorrow.day=1; tomorrow.year=today.year; } printf("Tomorrow's date is %d-%d-%d",tomorrow.month,tomorrow.day,tomorrow.year); return 0; } int numberofdays(struct date d) { int day; const int dayspermonth[12]={31,28,31,30,31,30,31,31,30,31,30,31}; if(d.month==2&&isleap(d)) { day=29; } else day=dayspermonth[d.month-1]; return day; } bool isleap(struct date d) { bool leap=false; if((d.year %4==0&&d.year %100!=0)||d.year%400==0) leap=true; return leap; }
结构与指针
-
用->表示指针所指的结构变量中的成员
struct date{ int month; int day; int year; }myday; struct date *p=&myday; p->month=12;\\这个与(*p).month=12意思相同
指针函数
int *fun(int x,int y);
这个函数就是一个指针函数,其返回值是一个 int 类型的指针,是一个地址。和普通函数对比不过就是其返回了一个指针(即地址值)而已。
以下有一段指针函数的代码:
#include <stdio.h> struct point* getstruct(struct point *p); void output(struct point p); void print(const struct point *p); struct point{ int x; int y; }; int main(void) { struct point y={0,0}; getstruct(&y); output(*getstruct(&y)); print(getstruct(&y)); getstruct(&y)->x=0;//将y的x值更改为0 *getstruct(&y)=(struct point){1,2};//将结构y的x改为1,y改为2 return 0; } struct point* getstruct(struct point *p)//传进一个指针,对指针所指的东西进行一定的处理后再将这个指针返回出去 { printf("%d %d",p->x,p->y); scanf("%d",&p->x); scanf("%d",&p->y); printf("%d %d",p->x,p->y); return p; } void output(struct point p) { printf("%d %d",p.x,p.y); } void print(const struct point *p)//不用修改p的内容,因此有const,从而防止p被修改 { printf("%d %d",p->x,p->y); }
结构数组
输出这一秒的下一秒的代码:
struct date dates[100]; struct date dates[]={ {4,5,2005},{2,4,2005}; }
自定义数据类型
typedef:
typedef int length;
使得Length成为int类型的别名。这样Length 就可以代替Int出现在变量定义和参数声明的地方了:
Length a,b,len;
Length numbers[10];
typedef long int64_t; typedef struct ADate{ int month; int year; int day; }Date; int64_t i=100; Date d={9,1,2005};
typedef *char[10] Strings;//Strings是10个字符指针的数组
程序结构
全局变量
-
定义在函数外面的为全局变量
-
全局变量具有全局的生存期和作用域
-
它们与任何函数都无关
-
任何函数内部都可以使用它们
例如:
#include <stdio.h> int f(void); int gall=12; int main(void) { printf("in %s gall =%d\n",__func__,gall); f(); printf(" agn in %s gall =%d\n",__func__,gall); } int f(void) { printf("in %s gall =%d\n",__func__,gall); gall+=2; printf(" agn in %s gall =%d\n",__func__,gall); return gall; }
全局变量初始化
-
没有做初始化的全局变量会得到0值,而指针会得到NULL
-
只能用编译时刻已知的
若是全局变量,实行以下操作: int gall; int g2=gall; 这是不合法的。但若是: const int gall=12; int g2=gall; 则是合法操作,但是不建议此操作。
-
若函数内部存在与全局变量同名的变量(本地变量),则该全局变量会被暂时覆盖(仅存在于函数内部),出函数后全局变量不会被改变
静态本地变量
-
在本地变量定义时加上static修饰符就成为静态本地变量
-
当离开函数时,静态本地变量会继续存在并保持其值
-
静态本地变量只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
-
静态本地变量具有全局生存期和函数内的局部作用域
-
静态本地变量实际上是特殊的全局变量,它们位于相同的内存区域
#include <stdio.h> int f(void); int main(void) { f(); f(); f(); return 0; } int f(void) { static int all=1; printf("in %s gall =%d\n",__func__,all); all+=2; printf(" agn in %s gall =%d\n",__func__,all); return all; }
宏
-
#define 用来定义一个宏
-
#define <名字><值>
-
名字必须为一个单词,值可以为各种东西
-
编译器替换是做的是完全的文本替换,define定义的名字后面有什么就把程序中相应名字替换为后面的值
-
如果宏的值超过一行,最后一个行之前的行末需要加\
-
宏也可以只有名字没有值,这类宏用来进行条件编译
带参数的宏:
#define cube(x) ((x)* (x)*(x)):
这与C语言的函数相似,不同的是这其中的x没有类型
错误用法:
#define RATG1(x) (x*57.298)
#define RATG2(x) (x)*57.298
这两个错误的原因都是括号的用法错误
因此:
-
宏的参数出现的每个地方都要有括号
-
整个值要括号
因此改为:#define RATG(x) ((x)*57.298)
宏可以带多个参数:
#define MIN(a,b) ((a)>(b)?(b):(a))
宏中也可以嵌套多个宏
多个源代码文件:
-
一个.c文件是一个编译单元
-
把函数原型声明封装到一个头文件(就是以.h结尾的文件)中,在需要调用这个函数的源代码文件(.c文件)中#include“ ”这个头文件,就能让编译器知道这个函数原型
-
#include“ ”把那个文件的全部文本内容原封不动地插入到它所在的地方
-
在使用和定义这个函数的地方都应该#include“ ”这个头文件(.h)
-
在函数前面加上static就使得它成为只能在所在编译单元中被使用的函数
-
在全局变量前面加上static就使得它成为只能在所在编译单元中被使用的全局变量
-
在头文件中,对全局变量声明为:extern int gAll;从而可以在其他文件中直接使用gAll
标准的头文件结构:
#ifndef _LIST_HEAD_ #define _LIST_HEAD_ //头两行,注意:该头文件文件名要在此处为大写,比如头文件叫max.h,则在代码中为_MAX_H_ #endif//在最后一行 前两行和最后一行是这个头文件的标准结构,用来判断这个头文件是否被调用过,如果被调用,则不在重复调用,从而防止对其中一些内容进行重复调用
函数指针:
-
一个函数的函数名就是一个地址
-
例如一个函数为void f(int),则指针为void (*pf)(int)=f,pf就是这个函数的指针,类型为void()
函数指针的使用:
-
调用函数也可以:(*pf)(10),这与f(10)相同,意为调用f函数并传入参数10
第一种用法:
#include <stdio.h> void f(int i) { printf("in f(),%d",i); } void g(int i) { printf("in h(),%d",i); } void h(int i) { printf("in h(),%d",i); } int main(){ int i=0; void (*fa[])(int)={f,g,h};//建立一个函数指针的数组fa,fa[0]指向为f的地址,依次类推 scanf("%d",&i); if(i>=0&&i<sizeof(fa)/sizeof(fa[0])){ (*fa[i])(0); } return 0; }
第二种用法:
#include <stdio.h> int plus(int a,int b){ return a+b; } int minus(int a, int b){ return a-b; } void cal(int (*f)(int,int))//参数为一个函数指针 { print("%d",(*f)(2,3)); } int main(void){ cal(plus); cal(minus); return 0; }