提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
算数优先级
内存映像图
指针
指针常用的有:指针常量和常量指针,指针数组,数组指针,指针函数,函数指针 ,结构指针和联合指针,枚举指针
空指针 VS 野指针
空指针
NULL,定义在stdlib.h中定义,相当于0;地址为0,
int* p = NULL;
*P = 100; //违法
野指针
表示没有赋值的指针,其指针存放的地址为随机值,垃圾内容,会出现段错误:访问了不能访问的内存(可能是不一定存在的内存或系统保护的内存),
合法的使用内存(堆内存)
- 使用系统分配的内存 如:
int a; int* p = &a;
- 申请内存
申请内存使用malloc
函数 定义在stdlib.h
中
使用方法:void* malloc(需要的字节数)
比如:
char* str = (char*)malloc(32);
申请32字节空间,str指向了一块32字节的空间地址
使用后free(str);
释放内存 并str=NULL
避免出现野指针
指针运算
int a;
int* p = &a;
表示把指针变量p存放a的地址
int x = 5, y = 0;
int* p = &x;
//下面三条 不是同时运行
y = *p + 5; //输出10
y = ++ *p; //输出 6
y = *p++; //违法 p为野指针
y = *p + 5;
等价于 y = (*p)+ 5;
等价于 y = x + 5;
y = ++ *p;
等价于 y = ++ (*p);
等价于 y = ++ x;
y = *p++;
等价于 y = *(p++);
等价于没有赋值的指针
*p++
等价于 *(p++);
指针加一后解引用
(*p)++
等价于 (*p)++;
先解引用后指针加一
常量指针 VS 指针常量
const 修饰指针
常量指针
const int* p=&a; //常量指针
(*p)++ //违法 等价于 a++
p++ //可以 等价于p=&b
const 修饰 *p ,指针p指向的值(即a的值)不可以修改,指针p的指向(即指向的地址)可以修改
指针指向可以修改,指向的值不能修改
即 p=&b可以,*p=20不可以
指针常量
int* const p=&a; //指针常量
(*p)++ //可以 等价于 a++
p++ //违法 等价于p=&b
const 修饰 p ,指针p的指向(即指向的地址)不可以修改,指针p指向的值(即a的值)可以修改
指针指向不可以修改,指向的值可以修改
即 p=&b不可以,*p=20可以
另外一种
const int* const p=&a;
(*p)++ //违法
p++ //违法
指针的指向和指针指向的数据都不能修改
指针和数组
指针和数组在使用上基本没区别
int a[5] = { 1,2,3,4,5 };
int* p = a;
a[i]等价于* (p + i)
a[i]等价于* (a + i)
char* str = "hello";
*str 等价于 str[0] 等于h
/*******************************/
需要注意的时
char str[5] = "hello"; 初始化数组内容
char* p = "hello"; 字符串常量(有实际的地址)
str[0] = 'x'; 可以 str存在栈空间 可读可改
p[0] = 'x'; 违法 char* p = "hello";为字符串常量,存放在只读数据区
char* p = "hello"; 字符串常量(有实际的地址)
指针p指向了字符串常量hello
指针数组 VS 数组指针
指针数组
存放同类型指针的数组,本质是数组
类型 *数组名[数组长度]
char* arr[4] = {"hello", "world", "shannxi", "xian"};
arr为数组,有四个字符串常量元素,arr[0]中存放这hello的地址
相当于char *p = “hello”,char *p1 = “world”,char *p3 = “shannxi”, char *p4 = “xian”四个指针,
每个指针存放着一个字符串的首地址,arr[4]这个数组分别存放这四个指针,就形成了指针数组
数组指针
数组指针本质为一个指针 ,这个指针指向数组
类型 (*指针名)[数组长度]
int(*p)[5] ; //p为一个指针,指向一个数组,该数组有5个int类型的元素
p为一个指针,指向数组,数组有四个int类型元素占4*5=20个字节,所以p++,应该是加20个字节
int a[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &a; //数组指针p指向整个数组的首地址
for(int i = 0; i < 5; i++)
{
printf("%d ", *(*p + i));
//printf("%d\n", (*p)[i]);
int (*p)[5] = &a; 这里a数组第一个元素的地址,而&a是整个数组的首地址(二维数组尤为重要)
*(*p + i)中p指向数组a的地址,*p+i 是第i个元素的地址, *(*p + i) 表示第i个元素的值
二维数组
int a[3][5]
是一个三行五列的二维数组 ,其中
a[0]
表示首行首元素地址 4字节
a
表示数组首行地址 4×5=20字节
&a
表示数组的地址 3×(4×5)=60字节
不理解可以参考下面代码
int a[3][4] = { {1,2,3,4},{2,3,4,5},{4,5,6,7} };
int* p = NULL;
int(*q)[4] = a; //数组指针指向第一行
printf("a[0] %p\n", a[0]); //首行首元素地址
printf("a %p\n", a); //首行地址
printf("a[1][] %p\n", ++q); //第二行地址
printf("&a %p\n", &a); //整个数组地址
a[0]
表示首行首元素地址,当a[0]加一时地址为首行元素的第二个地址
a
表示数组首行地址 ,当a加一时为第二行的地址
&a
表示数组的地址 ,当&a加一时地址为加整个数组元素的字节数
q为数组指针,q加一相当于指向了第二行 用3E8减3D8为十六进制的10正好是十进制的16
指针函数 VS 函数指针
指针函数 int* p() 函数指针 int(*p)()
区别:
指针函数中p是函数,返回的是int* 整形指针
函数指针中p是指针,指向的是一个函数地址,返回值是int
指针函数 本质是一个函数,返回值是一个指针。通俗讲:返回值是指针的函数
注意:不能返回局部变量的地址! 函数运行结束局部变量会被释放
char* func(args, ...);
char* init() //指针函数
{
char* str = (char*)maslloc(128); //申请128字节的内存
return str;
}
char* s = init();
strcpy(s,"hello");
free(s); //释放内存
int* max(int* p1, int* p2)
{
if (*p1 > *p2)
return p1;
else
return p2;
}
int main(int argc, char* argv[])
{
int* p, a, b;
a = 1; b = 2;
p = max(&a, &b);
printf("%d\n", *p);
return 0;
}
函数指针 本质是一个指针,指向了一个函数,是指向函数的指针
void(*p)(args, ...); //函数指针
例如:
void func(){}; 定义一个无参无返回值的函数
void (*p)(); 定义一个无参无返回值的函数指针
p = func; p指针指向函数func的地址
p(); 等价于func();
int add(int x,int y){}; 定义一个返回int和形参为int的函数
int (*p)(int,int) = add; 定义一个返回int和形参为int的函数指针并指向add函数的地址
p(1,2); 等价于add(1,2);
可以使用typedef 简化代码,比如:
typedef int (*T)(int,int);
T p = add; 等价于int(*p)(int,int) = add;
回调函数
回调函数中常用函数指针
回调函数:把函数名作为另一个函数的参数,从而达到修改函数的功能
//函数功能:实现累加求和
int func_sum(int num)
{
return sum;
}
//这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数
int callback(int num, int (*p)(int))
{
return p(num);
}
int main(void)
{
int num = 0;
printf("please input number:");
scanf("%d", &num);
printf("the sum from 0 to %d is %d\n", num, callback(num, func_sum)); //此处直接调用回调函数,而不是直接调func_sum函数
return 0;
}