概述
原本都是看博客零零散散学的指针,所以有很多细节问题没注意到。今天整理书本突然发现家里还有一本《C Primer Plus》就简单翻阅一遍,做个简单的查漏补缺以及整理
PS:本博客只是标记一些注意点,主要用于个人知识体系的完善,如需详细了解请自行购买《C Primer Plus》当然你也可以向我借阅这本书
正文
指针
指针的声明
int * pi;//pi是指向int类型变量的指针
char * pc;//pc是指向char类型变量的指针
float * pf;//pf是指向float类型变量的指针
// pc所指向的值(*pc)是char类型的
当我们描述一个指针类型的时候
int * pi;//pi 是 指向int类型变量的指针 正确
int * pi;//pi 是 int类型的指针 错误
指针本身是独立的类型,其本身的值在大多数系统中为一无符号类型整数,这与整数类型是是不同的,因此一些处理整数的操作不能用于指针(例如两个指针相乘)
指针用于函数通信
主要区分两种函数定义调用方式
int function1(int num)//计算或处理值
int function2 (int * ptr)//要改变主调函数中的变量
指针的打印
使用%p
print("val=%d address=%p",x,x_ptr);
printf("val=%d address=%p",x,&x);
指针与数组
指针与数组名
数组名是该数组首元素的地址
a==&a[0];//两个等效,都是常量
指针的赋值与修改
对于一个常量,他们在程序运行过程中不会改变,但是我们可以将他们赋值给指针变量,并修改指针变量的值
double bills[SIZE];
double * ptf;
ptf = bills;//把数组名赋值给ptf指针变量
指针的修改
试着打印一个数组的指针
printf("%p %p",ptr,ptr+1);
比较输出的两个数值,发现数值并非单纯+1。指针+1指的是增加一个存储单元,对于一个数组而言意味着下一个元素的地址,而不是下一个字节的地址
因此当我们声明指针的时候必须同时声明指向变量的类型,否则仅靠地址是无法知道+1后指向哪里的。
指针+1,指针的值递增它所指向类型的大小
数组下标与指针的对应关系
dates+2==&dates[2];
*(dates+2)==dates[2];
ar[i]==*(ar+i);
//区分下面两个
*(dates+2)//dates第三个元素的值
*dates+2//dates第一个元素+2
ar++;//不可以,数组名是常量
p_ar++;//可以p_ar是指针变量
函数,数组和指针
以下两种函数形式定义等价
int sum(int * ar ,int n)
{
}
int sum(int ar[],int n)
{
}
但是int ar[]只能用于声明形式参数
而指针ar指向的不仅仅是个int类型值,还是一个int类型数组的元素
当然我们也可以用数组首尾指针来代替n
ans=sum(arr,arr+SIZE);
int sum(int * start,int * end)
{
int total=0;
while(start<end)
{
total+=*start;
start++;
}
return total;
}
不要解引用没初始化的指针
int * pt;
*pt=5;//错误
double * pd;
*pd=2.4;//错误
指针和多维度数组
int zippo[4][2];
zippo//二维数组首元素(一维数组)地址
zippo+2//二维数组第三个元素(一维数组)的地址
*(zippo+2)//二维数组第三个元素(一维数组)的首元素(int类型的值)的地址
*(zippo+2)+1//二维数组第三个元素(一维数组)的第二个元素(int类型的值)的地址
*(*(zippo+2)+1)二维数组第三个元素(一维数组)的第二个元素(int类型的值)的值
zippo[0] //是该数组首元素zippo[0][0]的地址
*(zippo[0])//是该数组首元素zip[0][0]的值
指向多为数组的指针
//指向数组的指针
int (*pz)[2];//用这个,pz指向一个内含两个int类型值的数组
//内含指针的数组
int * pax[2];//pax是一个含内涵两个指针元素的数组,每个元素都指向int的指针
函数与多维数组
编译器会把数组表示法转化为指针表示法,所以会把ar[1]转为ar+1。指针的递增是同时知道地址和元素大小的,第二个[]的数值就是告诉元素大小
int sum2(int ar[][],int rows);//错误
int sum2(int ar[][4],int rows);//正确
int sum2(int ar[3][4],int rows);//正确,但会无视3
一般而言,声明一个指向N维数组的指针的时候只能省略最左边括号的值,
第一个括号的作用只是表明这是个指针,而其他方括号则用与描述指针所指向数据对象的类型
下面两个声明等价
int sum4(int ar[][12][30][30],int rows);
int sum4(int (*ar)[12][30][30],int rows);
指针与字符串
分配空间
char *name;
scanf("%s",name);//错误,name未初始化可能指向任何地方造成擦除数据
显式指明数组大小
char name[81];
另外一种方法是使用库函数动态分配大小
分配内存
我们已经使用过的分配内存方式
float x;//自动分配
char place[]="12345";//自动分配
int plates[100];//显式分配
malloc()和free()
double * ptd;//记录指向分配内存的位置
long * newmem;
ptd=(double *)malloc(30*sizeof(double));//最好写强制类型转换,初始化随机
newmem=(long *)calloc(100,sizeof(long));//把块的所有位初始化为0
free(ptd);//free参数为malloc()返回的地址
检测是否分配成功
if(ptd==NULL)//虽然一般不会
{
puts("Memory allocation failed.Goodbye.");
exit(EXIT_FAILURE);
}
结构体与指针
声明
struct guy * him;//声明guy类型结构的指针
him = &barney;//如果barney是guy类型结构体就可以这样指向它
him =&fellow[0];//指向结构体数组
指针访问成员
//如果him == &barney;
him->income等效barney.income;
//如果him==&fellow[0];
him->income等效fellow[0].income;
向函数传递结构
//为了简化,设s为funds结构体类型的变量,funds结构有first_member和second_member两个成员
//传递成员
double sum(double x,double y)
{
return x+y;
}
sum(s.first_member,s.second_member);
//传递结构的地址
double sum(const struct funds * money)//const是因为不用改变值,只是计算,这样可以保护原数据
{
return(money->first_member+money->second_member);
}
sum(&s);
//传递结构
double sum(struct funds money)
{
return(money.first_member+money.second_member);
}
sum(s);
传递结构与传递结构指针的优缺点
传递结构 | 传递结构指针 |
---|---|
老版本无法实现,浪费时间和空间 | 执行快,但没法保护数据(const可以帮忙限制) |
结构中的字符数组与字符指针
struct names
{
char first[LEN];
char second[LEN];
}
struct pnames
{
char * first;
char * second;
}
结构体中字符串推荐用字符串数组存,如果用指针的话因为是没有初始化的所以可能指向任意地址发生危险
函数和指针
声明
void ToUpper(char *);//把字符串中的字符转换成大写字符的函数
void (*pf)(char *);//pf是一个指向函数的指针
void *pf(char *);//pf是一个返回字符指针的函数
//技巧是把声明的函数名替换成(*pf)形式的表达式
void ToUpper(char *);
void ToLower(char *);
int round(char *);
void pf(char *);//可以指向void 指针名称(char*)类型的函数
pf=ToUpper;//ok
pf=ToLower;//ok
pf=round;//round与指针类型不匹配
pf=ToLower();//ToLower()不是地址
常见的用法(函数指针+数据指针完成多个相似函数的调用)
show(ToLower,str);
show(pf,str);
show(ToUpper,str);
void show(void (*fp)(char *),char *str);
{
(*fp)(str);//把所选函数作用与str
puts(str);
}
小结
数据类型 | 原来的 | 指针声明 |
---|---|---|
变量 | int a | int * p |
一维数组 | int a[100] | int * p |
二维数组 | int a[10][20] | int (*p)[20] |
结构体 | struct node a | node * p |
结构体数组 | struct node a[100] | node * p |
函数 | void fun(char *) | void (*p)(char *) |