指针是什么
指针就是地址。说得再全面一点,指针是一个变量,且这个变量是专门用来存放地址的。
指针的在函数中的两个基本用法:
用法一:
-
当函数需返回多个值时,某些值则需要通过指针返回
-
传入的参数实际上是需要保存带回的结果的变量
例如:找一列数中的最大和最小值并输出
#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;
}
本文详细介绍了C语言中的指针概念,包括指针的定义、用法、运算和在函数中的应用。指针作为变量存储地址,可用于返回函数结果、动态内存分配以及结构体操作。文中通过实例展示了指针在数组、结构体、函数参数和函数返回值中的使用,还讲解了全局变量、静态变量、宏定义以及函数指针等高级主题。
505

被折叠的 条评论
为什么被折叠?



