当前位置 博文首页 > CW_qian的博客:7月30日笔记C语言基础指针1
目录
1.什么是指针(存放地址的变量)?
?????指针是一个变量,是用于存放地址的变量,我们称之为指针?? ?cpu往往对地址处理的效率较高,仅次于汇编,能不能通过指针来操作?? ?对应存放地址的变量呢?可以通过指针操作?? ?2.如何获取对应数据存放的内存所对应的地址,是通过&(取地址符)获取对应变量的地址
?? ?int a;//&a 获取a的地址?? ?printf("%p\n",&a);?? ?3.指针的定义
4.地址偏移量
字节序?? ?1.大端模式:高位数据存放在内存的低地址端,低位数据存放在内存的高地址端?? ?2.小端模式:高位数据存放在内存的高地址端,低位数据存放在内存的低地址端
5.函数传递地址
6.数组与指针?? ?数名有两个含义?? ??? ?1.第一个含义,表示整个数组?? ??? ?2.第二个含义,表示首元素地址?? ?// 例子?? ?
6.1指针转数组
7.char型指针?? ?因为我们对字符串的处理尤为重要,所以有时候需要用char *型指针对它进行处理
8.字符串常量与指针?? ?字符串常量在内存中实际就是一个匿名数组?? ?匿名数组满足数组的两个条件?? ??? ?1.第一个含义,表示整个数组?? ??? ?2.第二个含义,表示首元素地址
9.指针函数?? ?函数的返回值为指针称为指针函数
10.函数指针?? ?指向函数的指针变量称之为函数指针?? ?特点:函数指针和普通指针本质上是没有任何区别?? ?但是???? ??? ?定义:?? ??? ?// 函数指针,注意*p需要()括起来,否则就是普通的函数了?? ??? ?int (*p)(int a,int b)?
11.回调函数(必须要掌握)?? ?调用一个函数,给这个函数传递函数指针,被调用的这个?? ?函数通过这个函数指针进行调用其它函数,我们把这种方式?? ?称为回调函数?? ?demo
12.指针数组?? ?用于存放指针的数组称为指针数组?? ?指针一般是用于指向字符串的首地址?? ?// 二维数组?? ?char buf[2][4] = {"abc","def"};?? ??? ?// 指针数组 ? ??? ?char *buf[2];?? ??? ?demo?
13.数组指针?? ?用于存放数组的地址的变量称为数组指针?? ?又称为数组名的一个指针,及指向数组首元素的地址?? ?// 一维数组?? ?char buf[] = "123";?? ?// 一维数组指针?? ?char *p = buf;?? ??? ?//二维数组?? ?char buf1[2][3] = {"ab","cd"};?? ?// 注意(*p)一定要加括号,否者就是指针数组?? ?char (*p)[3] ? <===> char [3] (*p)
??段错误总结:
????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字节
????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字节 ?强制类型转换会发生改变。
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值。
?? ?
???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) 仅限面试用
? ?总结:数组最后编译器会自动转为指针操作,数组运算其实就是指针运算。
?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()功能,将这个函数封装起来方便以后调用
?? ?主函数调用此函数实现
?? ?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存放的是这个空间的地址?? ?
?? ?
?? ?//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]);
?? ?//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;
?? ?}
?? ?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;
?? ?}
?? ?
?? ?#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;
?? ?}
?? ?
?? ??#include <stdio.h>
?? ?#include <stdlib.h>?? ?int func(char *buf[])
?? ?{
?? ??? ?printf("%s\n",buf[1]);
?? ??? ?
?? ??? ?return 0;