目录
int a;//&a 获取a的地址 printf("%p\n",&a); 3.指针的定义
字节序 1.大端模式:高位数据存放在内存的低地址端,低位数据存放在内存的高地址端 2.小端模式:高位数据存放在内存的高地址端,低位数据存放在内存的低地址端
6.数组与指针 数名有两个含义 1.第一个含义,表示整个数组 2.第二个含义,表示首元素地址 // 例子
7.char型指针 因为我们对字符串的处理尤为重要,所以有时候需要用char *型指针对它进行处理
8.字符串常量与指针 字符串常量在内存中实际就是一个匿名数组 匿名数组满足数组的两个条件 1.第一个含义,表示整个数组 2.第二个含义,表示首元素地址
11.回调函数(必须要掌握) 调用一个函数,给这个函数传递函数指针,被调用的这个 函数通过这个函数指针进行调用其它函数,我们把这种方式 称为回调函数 demo
1.什么是指针(存放地址的变量)?
指针是一个变量,是用于存放地址的变量,我们称之为指针
cpu往往对地址处理的效率较高,仅次于汇编,能不能通过指针来操作
对应存放地址的变量呢?可以通过指针操作
2.如何获取对应数据存放的内存所对应的地址,是通过&(取地址符)获取对应变量的地址
int a;//&a 获取a的地址
printf("%p\n",&a);
3.指针的定义
int a = 1;
printf("%d\n",1);
printf("%p\n",&a);
// 找一个变量存放地址,定义一个int *类型的便变量存放地址我们把 int * 这种类型称为指针
int *p = &a;
printf("%d\n",a);
int b = *p; // *p表示获取地址a里面的数据,把这种方式称为解引用
printf("%d\n",*p); // 解引用获取p变量保存的地址里面的数据
printf("%d\n",b);
注意:
1. int *p; 是定义一个指针变量,int *p表示指针类型
p = &a; p指向a ,实际就是p变量存放a的地址
2. int b = *p;//*p表示为解引用,获取p变量所保存的地址里面的内容
short int a = 10;
short int *ps = &a;
printf("%hd\n",*ps); //
printf("%ld\n",sizeof(ps));
int b = 20;
int *pb = &b;
printf("%d\n",*pb);
printf("%ld\n",sizeof(pb));
long int c = 20;
long int *pl = &c;
printf("%ld\n",*pl);
printf("%ld\n",sizeof(pl));
float d = 3.14;
float *pd = &d;
printf("%f\n",*pd);
printf("%ld\n",sizeof(pd));
double e = 3.14;
double *pe = &e;
printf("%lf\n",*pe);
printf("%ld\n",sizeof(pe));
long double f = 3.14;
long double *pf = &f;
printf("%Lf\n",*pf);
printf("%ld\n",sizeof(pf));
总结 :
注意所有的类型的指针大小都是8字节(64位系统),32位系统一般为4字节
但是指针所存放的地址对应的空间的大小是与对应的数据类型相关 比如 int 4字节 char 1字节
4.地址偏移量
int a[2] = {1,2};
char *p = (char *)&a[0]; // 将p指向a0地址,因为a为数组,所以a[0]的地址称为基地址
printf("%d,%d\n",*p,*(p+1));// 强制类型转换偏移1字节int *q = &a[0];
printf("%p,%p\n",q,(q+1)); // 偏移4字节
char b[2] = {'a','b'};
char *q = &b[0]; // 将p指向a0地址,因为a为数组,所以a[0]的地址称为基地址
printf("%c,%c\n",*q,*(q+1));
int a = 0x11223344;
int (*p) = &a;
printf("p = %p\n",p); // 基地址
printf("&a = %p\n",&a);
printf("p+1 = %p\n",p+1); // 偏移后的地址 偏移4字节
char c = 'b';
char (*q) = &c;
printf("q1 = %p\n",q); // 基地址
printf("q+1 = %p\n",q+1); // 偏移后的地址
总结 :
地址偏移量是与数据类型的大小一致,比如int类型,它们之间的地址间隔是4字节, char 类型是1字节 强制类型转换会发生改变。
字节序
1.大端模式:高位数据存放在内存的低地址端,低位数据存放在内存的高地址端
2.小端模式:高位数据存放在内存的高地址端,低位数据存放在内存的低地址端
5.函数传递地址
void func(int *a)
{
int b = *a;
printf("%d\n",b);
}
int main()
{
int a = 1;
func(&a)
}
练习:
传递两个地址给子函数,子函数交换两个数值后,主函数将结果打印出来
// 值传参
void func1(int a,int b)
{
int temp = a;
a = b;
b = temp;
printf("%d,%d\n",a,b);
}
// 地址传参
void func2(int *a,int *b)
{
int temp = *a; // 解引用,获取a变量里面存放的地址里面的内容
*a = *b;
*b = temp;
}
void main()
{
int a =1,b=2;
int c = 3,d = 4;
func1(a,b);
func2(&c,&d);
printf("%d,%d\n",a,b); // 不能改变
printf("%d,%d\n",c,d); // 可以改变
}
总结:
地址传参可以修改对应传递的数值,基本所有接口(函数)调用都是使用地址传递,
所以一般接口的return都是返回对应的执行状态(成功与失败),类型为bool值。
6.数组与指针
数名有两个含义
1.第一个含义,表示整个数组
2.第二个含义,表示首元素地址
// 例子
void func(int *p)
{
printf("%d\n",*p);
printf("%d\n",*(p+1));
printf("%d\n",*(p+2));
}int main()
{
int a[3] = {1,2,3};
// 在此情形下表示整个数组
printf("%ld\n",sizeof(a));
//int *p = &a; // &a : 写法和编译器编译的时候会有出入,警告,不会报错,慎用,基本不用
printf("p = %d\n",*(p+1));
printf("p addr = %p\n",p);
// 其它情形下数组都视为首元素地址,常用
printf("%p\n",a);
printf("%p\n",&a[0]);
int *q = a;
printf("q = %d\n",*(q+1));
func(a);
数组下标
int a[3] = {1,2,3};
int b = a[0];
int c1 = *(a+0); // a[0]
int c2 = *(a+1); // a[1]
int c3 = *(a+2); // a[2]
int d1 = *(0+a); // a[0]
int d2 = *(1+a); // a[1]
int d3 = *(2+a); // a[2]
printf("2[a] = %d\n",2[a]); // *(2+a) 仅限面试用
总结:数组最后编译器会自动转为指针操作,数组运算其实就是指针运算。
6.1指针转数组
int b[10];
// 指针没有让它指向对应的空间,会出现段错误
int *a = b;
a[0] = 1;
printf("%d\n",a[0]);
总结 : 1.指针一定要指向一块合法的空间,否则出现段错误,没有空间自行分配
2.可以将指针转换成数组使用,如上所示
char buf[10] = "abc";
int len = strlen(buf);
int mc_Strlen(char *str)
{
....
return count;
}
int main()
{
MyStrLen(buf);
}
编译一个程序实现strlen()功能,将这个函数封装起来方便以后调用
主函数调用此函数实现
7.char型指针
因为我们对字符串的处理尤为重要,所以有时候需要用char *型指针对它进行处理
char buf[4] = "abc";
char *p = buf;
char *q = "def";
char *p1; // 段错误,因为p1没有给它指向对应的空间
strcpy(p1,"hello");
printf("%s,%s,%s\n",buf,p,q); // %s不需要解引用会自动解析地址里面的内容,只要地址空间连续,直到遇到'\0'结束
注意 : p指针所指向的空间一定要能存放对应的内容,因为对这个指针操作就相当于间接的对这个空间操作,因为这个指针p存放的是这个空间的地址
8.字符串常量与指针
字符串常量在内存中实际就是一个匿名数组
匿名数组满足数组的两个条件
1.第一个含义,表示整个数组
2.第二个含义,表示首元素地址
//demo
char buf[] = "abcd";
printf("%c\n",buf[1]);
printf("%c\n","abcd"[1]);
char *p = "abcd"; // 将p指向一块匿名数组的一个首地址
printf("%p,%p\n","abcd",&"abcd"[0]);
9.指针函数
函数的返回值为指针称为指针函数
//demo
// 指针函数
char *func(char *buf)
{
return buf;
}// 普通函数调用
int func1(int a)
{ // 因为变量的声明周期是属于这个函数,函数结束空间释放
// 所以返回地址会出现段错误
int b = a+1;
return b;
}int main()
{
char *str = func("abc");
printf("%s\n",str);
int a = 123;
int b = func1(a);
printf("b = %d\n",b);
return 0;
}
10.函数指针
指向函数的指针变量称之为函数指针
特点:函数指针和普通指针本质上是没有任何区别
但是??
定义:
// 函数指针,注意*p需要()括起来,否则就是普通的函数了
int (*p)(int a,int b)
demo1:
int max(int a,int b)
{
return a>b?a:b;
}// main其实就是程序的入口地址
int main()
{
// 定义一个指针,*不能去掉
int (*p)(int a,int b);
// p指向的函数名与定义的函数名一致
// 函数名就是这个函数的一个首地址
//p = &max; // 将max的地址给p
p = max;
int a = 1,b = 2;
// 通过指针调用max函数
//int ret = (*p)(a,b);
int ret = p(a,b);
printf("%d\n",ret);
return 0;
}
demo2:
// 通过typedef,给函数指针定义一个类型,类型的名字叫p
// 这时候p就是一个变量,和int char.. 相似
// 增加函数指针的易用性
// typedef给这个函数指针取别名相当于给它生成一个数据类型 ,和 int char 相似
typedef int (*p)(int a,int b);
int max(int a,int b)
{
return a>b?a:b;
}
int max1(int a,int b)
{
return a>b?a:b;
}
int main()
{
// 函数注册
p q = max;
p q1 = max1;
int a = 1,b = 2;
// 通过指针调用max函数int ret = q1(a,b);
printf("%d\n",ret);
return 0;
}
11.回调函数(必须要掌握)
调用一个函数,给这个函数传递函数指针,被调用的这个
函数通过这个函数指针进行调用其它函数,我们把这种方式
称为回调函数
demo
#include <stdio.h>
#include <unistd.h>
// 客户
void printPhone(int len)
{
printf("手机好漂亮---%d\n",len);
}// 手机店,回调函数,参数是一个函数指针类型
void callback(int times,void (*print)(int len))
{
for(int i = 0;i < times;i++)
{
print(i+1);
sleep(1);
}
}int main()
{
// 执行回调函数
callback(5,printPhone);
return 0;
}
12.指针数组
用于存放指针的数组称为指针数组
指针一般是用于指向字符串的首地址
// 二维数组
char buf[2][4] = {"abc","def"};
// 指针数组
char *buf[2];
demo
#include <stdio.h>
#include <stdlib.h>int func(char *buf[])
{
printf("%s\n",buf[1]);
return 0;
}int func2(char **buf)
{
//printf("%s\n",buf[1]);
printf("%s\n",*(buf+1));
return 0;
}// 外部参数传递,程序外面传进来的参数
// 例如 ./a.out abc def 传递了三个参数 分别为"./a.out","abc","def"
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("请输入一个内容\n");
return 0;
}
printf("argv = %d\n",argc);
for(int i = 0;i < argc;i++)
printf("argv[%d] = %s\n",i,argv[i]);
// atoi将字符串转整数
/*
#include <stdlib.h>
int atoi(const char *nptr);
参数 nptr: 需要转为整数的内容
*/
printf("%d\n",atoi(argv[1]));// 定义一个指针数组
char *buf[2] = {"abc","def"};
printf("%c\n",buf[0][0]);
printf("%c\n",**buf);
func(buf);
func2(buf);
return 0;
}
13.数组指针
用于存放数组的地址的变量称为数组指针
又称为数组名的一个指针,及指向数组首元素的地址
// 一维数组
char buf[] = "123";
// 一维数组指针
char *p = buf;
//二维数组
char buf1[2][3] = {"ab","cd"};
// 注意(*p)一定要加括号,否者就是指针数组
char (*p)[3] <===> char [3] (*p)
段错误总结:
1.内存没有分配
2.内存溢出
3.分配了内存被释放了
一般遇到的段错误问题是,用一个指针直线给他赋值,没有让这个指针合理的指向空间导致的。