黑马程序员_IOS开发_Objective-C学习笔记_指针复习

1.指针

上一篇我在分析我自己写的程序错误里面发现是指针的问题,我觉得有必要在复习一下指针,毕竟指针是C语言系列的难点。

指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移对指针的概念和使用越加模糊,感觉指针难以掌握,今天我就再回顾下指针的概念和使用。

2.什么是指针

指针的本质是存放变量地址的变量,简单的说变量p中存储的是变量a的地址,那么p就可以称为是变量a的指针,或者说p指向a。当我们访问a变量的时候其实是程序先根据a取得a对应的地址,再到这个地址对应的存储空间中拿到a的值,这种方式我们称之为“直接引用”;而当我们通过p取得a的时候首先要先根据p转换成p对应的存储地址,再根据这个地址到其对应的存储空间中拿到存储内容,它的内容其实就是a的地址,然后根据这个地址到对应的存储空间中取得对应的内容,这个内容就是a的值,这种通过p找到a对应地址再取值的方式成为“间接引用”。

2.1示例代码如下:

int a =10;//a的地址是 ffeedd01
int *p ;
*p=a;//此时p的值就为a的地址 ffeedd01

2.2指针的赋值

#include <stdio.h>

int main(int argc, const char * argv[]) {

    int a=10;
    int *p;
    p=&a; //也可以直接给指针变量赋值:int *p=&a;
    printf("a=%d,p=%d\n",a,*p); //结果:a=10,p=10

    *p=20;
    printf("a=%d,*p=%d\n",a,*p); //结果:a=20,p=20

    int b=8;
    char c= 1;
    int *q=&c;
    printf("c=%d,q=%d\n", c, *q); //结果:c=1,q=2049,为什么q的值不是1呢?

    return 0;
}

注意:

*int *p;中的*只是表示p变量是一个指针变量;而打印*p的时候,*p中的*是操作符,表示p指针指向的变量的存储空间(当前存储就是10),同时我们也看到了*p==a;修改了*p也就是修改了p指向的存储空间的内容,也就修改了a,所以第二次打印a=20;

*指针所指向的类型必须和定义指针时声明的类型相同;上面指针q定义成了int型而指向了char型,结果输出*q打印出了2049,(假设在16位编译器下,指针长度为2字节)

     int b=8;    //地址ffeedd0c
                 //地址ffeedd0d
    char c= 1;   //地址ffeedd0b
    int *q=&c;      //ffeedd0b
 

由于局部变量是存储在stack堆栈里面的,所以先存储b再存储c、q,当打印*q的时候,其实就是以q指向的地址对应的空间开始取两个字节的数据(因为定义q的时候它指向的是int型,在16位编译器下int类型的长度为2个字节),刚好定义的b和c空间连续,所以就取到b的其中一个字节,最后*p二进制存储为“0000100000000001”,十进制表示就是2049;

*指针变量占用的空间和它所指向的变量类型无关。

3.数组和指针

数组的存储是连续的,数组名也就是数组的起始地址,这样一来数组和指针就有了关系:

#include <stdio.h>

void changeValue(int a[]){
    a[0]=2;
}
void changeValue2(int *p){
    p[0]=3;
}

int main(int argc, const char * argv[]) {
    int a[]={1,2,3};
    int *p=&a[0]; //等价于:*p=a;//p指向数组的起始地址//p指向a[0],p+1指向a[1],以此类推,所以我们通过指针也可以取出数组元素
    for(int i=0;i<3;++i){
        //printf("a[%d]=%d\n",i,a[i]);
        printf("a[%d]=%d\n",i,*(p+i));
    }
    /*输出结果:
     a[0]=1
     a[1]=2
     a[2]=3
     */

    changeValue(p); //等价于:changeValue(a)
    for(int i=0;i<3;++i){
        printf("a[%d]=%d\n",i,a[i]);
    }
    /*输出结果:
     a[0]=2
     a[1]=2
     a[2]=3
     */

    changeValue2(a); //等价于:changeValue2(p)
    for(int i=0;i<3;++i){
        printf("a[%d]=%d\n",i,a[i]);
    }
    /*输出结果:
     a[0]=3
     a[1]=2
     a[2]=3
     */

    return 0;
}
从上面的例子我们可以得出如下结论:

*数组名a等于&a[0]等于p;

*如果p指向一个数组,那么p+1指向数组的下一个元素,同时注意p+1移动的长度并不固定,具体需要根据p指向的数据类型而定;

*不管函数的形参为数组还是指针,实参都可以使用数组名或指针;

4.字符串和指针

在C语言中字符串就是字符数组,则字符串和数组的关系如下:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    char a[]="itheima";
    printf("%s\n",a);//结果:itheimachar b[]="itheima";
    char *p=b;
    printf("b=%s,p=%s\n",b,p);//结果:b=itheima,p=itheima

    //指针存储的是地址,而数组名存储的也是地址,既然字符数组可以表示字符串,那么指向字符的指针同样也可以,如下方式可以更简单的定义一个字符串
    char *c="itheima"; //等价于char c[]="itheima";
    printf("c=%s\n",c); //结果:c=itheima

    return 0;
}

5.函数指针

我们先来研究一下返回指针类型数据的函数,指针类型也是C语言的数据类型,下面以一个字符串转换为大写字符的程序为例:

#include <stdio.h>

char * toUpper(char *a){
    char *b=a; //保留最初地址,因为后面的循环会改变字符串最初地址
    int len=‘a‘-‘A‘; //大小写ASCII码差值相等
    while (*a!=‘\0‘) { //字符是否结束
        if(*a>‘a‘&&*a<‘z‘){//如果是小写字符
            *(a++) -= len; //*a表示数组对应的字符(-32变为小写),a++代表移动到下一个字符
        }
    }
       return b;
}

int main(int argc, const char * argv[]) {
    char a[]="itheima";
    char *p=toUpper(a);
    printf("%s\n",p); //结果:ITHEIMA
    return 0;
}

注意:大家都是知道函数只能有一个返回值,如果需要返回多个值,那么我们怎么办呢,其实只需要将指针作为函数参数传递就可以了:

#include <stdio.h>

int operate(int a,int b,int *c){
    *c=a-b;
    return a+b;
}

int main(int argc, const char * argv[]) {
    int a=1,b=2,c,d;
    d=operate(a, b, &c);
    printf("a+b=%d,a-b=%d\n",d,c);//结果:a+b=3,a-b=-1
    return 0;
}

难点:函数也是需要在内存中存储的,函数也有一个起始地址,函数名就是函数的起始地址。函数指针定义的形式:返回值类型 (*指针变量名)(形参1,形参2),函数指针其实等同于这个函数,函数的操作都可以通过指针来完成。既然指针作为C语言的数据类型,可以作为参数、作为返回值,那么当然函数指针同样可以作为函数的参数和返回值:

#include <stdio.h>
int sum(int a,int b){
    return a+b;
}
int sub(int a,int b){
    return a-b;
}
//函数指针作为参数进行传递
int operate(int a,int b,int (*p)(int,int)){
    return p(a,b);
}

int main(int argc, const char * argv[]) {
    int a=1,b=2;
    int (*p)(int ,int)=sum;//函数名就是函数首地址
    int c=p(a,b);
    printf("a+b=%d\n",c); //结果:a+b=3

    //函数作为参数传递
    printf("%d\n",operate(a, b, sum)); //结果:3
    printf("%d\n",operate(a, b, sub)); //结果:-1

    return 0;
}

虽然我知道这个函数指针这个概念,但是我现在还没遇到需要使用函数指针的场合。但是函数指针可以作为函数参数进行传递,实在太强大了。

时间: 2024-09-30 14:24:50

黑马程序员_IOS开发_Objective-C学习笔记_指针复习的相关文章

黑马程序员_JAVA UDP网络编程学习笔记

一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路.与TCP通信不同,UDP是面向无连接的.不可靠的基于数据包的传输协议.即应用进程(或程序)在使用UDP协议之前,不必先建立连接.自然,发送数据结束时也没有连接需要释放.因此,减少了开销和发送数据之前的延时.UDP也采用端口来区分进程. 在java中,java.

黑马程序员_IOS开发_Objective-C学习笔记_类(对象)

1.面向对象编程基本介绍: 面向对象程序设计(英语:Object-oriented programming,缩写:OOP),指一种程序设计范型,同时也是一种程序开发的方法.对象指的是类的集合,类是对象的模板,它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的安全性.重用性.灵活性和扩展性. 2.面向对象与面向过程 面向过程就是分析出解决问题所需要的步骤,然后基于这些步骤用代码和函数把这些步骤一步一步实现. 面向对象是把构成问题事务分解成各个对象,然后各个对象分别对相关的程序功能做出处

黑马程序员_IOS开发_Objective-C学习笔记_基本数据类型

 1.数据类型和常量: 我们在做IOS程序开发的时候使用的最多的恐怕就是基本数据类型,在Objective-c中提供了4种基本的数据类型:int float double以及char. 1.1声明为int的变量只能用于保存整形值. 1.2声明为float类型的变量可存储浮点类型值(即包含小数位数).. 1.3double类型和float类型一样,只不过前者的精度大约是后者的2倍. 1.4最后是char 数据类型,char类型可用来存储单个字符,例如字母a,数字6,或是一个分号.或者通过char指

黑马程序员_IOS开发_Objective-C学习笔记_NSString

NSString NSString是一个IOS OC开发中经常使用到得一个对象类型.NSString是NSObject(Foundation的基础对象)的子类,所以具有NSObject的所有特性. NS是Cocoa类对象类型的前缀,来源于乔布斯公司的操作系统的名字 NEXTSTEP 1.创建NSString对象1.1此方法不需要手动释放内存. NSString *aString = @"This is String"; //注意:这里和C语言字符串不一样,需要在头部放一个@符号 1.2

黑马程序员_IOS开发_Objective-C学习笔记_分析和调试自己的一个黑马基础测试题程序

1.程序 1.1程序介绍 从键盘输入6个字符串(仅仅包含英文字母和数字),对着6个字符串从小到大排列并输出结果.(C语言) 1.2程序设计分析 这个是我在基础测试题目环节从黑马报名系统里面下得题目. 初看题目,觉得此题不难,无非是: *建立一个字符串的数组 *然后在一个循环6次的循环体里面循环输入6次,然后再把输入的字符串放到字符串数组里面 *然后建立一个长度数组来统计相对应的字符串的长度 *最后对长度数组中的元素按大小来排序(注意:对长度数组中的元素排序的时候,同时要对字符串数组进行一样的操作

黑马程序员_IOS开发_Objective-C学习笔记_Foundation框架常用的结构体

1.Foundation框架简介 1.1使用Foundation可以: *创建和管理集合,比如数组和字典 *访问存储在应用程序里的图片和其它资源 *创建和管理字符串 *提交和接收通知 *创建日期和时间对象 *自动发现IP网络上的设备 *操作URL流 *执行异步代码 注意:Foundation框架是默认导入的 所以不用刻意去导入 是必备的框架之一 1.2Foundation框架中一些经常接触到的结构体 由于Objective-C程序中 对象的结构体成员变量中的元素 不能通过对象直接来访问 所以我们

黑马程序员_IOS开发_Objective-C学习笔记_继承和分类

1.什么是继承 继承是面向对象程序的三大特征之一,意思是基于一个类的基础,定义一个新类. 被继承的类成为父类,新类成为子类. 继承的实际例子有许多.例如,一个儿子继承了他父亲,那么他就继承了他父亲的财产. 例如基类是Person,那么我们可以继承Person这个类,产生新的子类Student,Teacher 在这里Person是Student类和Teacher类的父类. 引入继承的概念是有许多好处的: *能够抽取重复代码 *建立了类和类之间的联系 *不改变原来模型的基础上,能够扩展出新的方法 2

黑马程序员_IOS开发_Objective-C学习笔记_内存管理

1.内存管理概述 1.1什么是内存管理:内存管理是程序设计中常用的资源管理的一部分,每个计算机系统可供程序使用的内存都是有限的. 1.2为什么要使用内存管理:当我们的程序运行结束的时候,操作系统将回收其我们程序占用内存.但是,只要程序还在运行,它就会一直占用内存.如果不进行及时清理不用的内存,内存最终将被耗尽.每个程序都会使用内存,我们必须确保在需要的时候分配内存,而在程序运行结束时释放占用的内存.如果我们只分配而不释放内存,将发生内存泄漏. 1.3引用计数:1.3.1只有当你对一个对象了all

黑马程序员_IOS开发_Objective-C学习笔记_NSArray/NSMutableArray

1.数组简介 NSArray:用来存储有序数组,它是不可变的(不能插入删除数据元素),也不能存储C语言中的基本数据类型(int.float.double.enum.struct),也不能存nil,如果需要使用基本数据类型,需要先转成Objective-c中的对象,用@包装下. NSMutableArray:它是可变的(可以插入删除数据元素),还能限制元素个数 capacity.当你需要对数组元素进行加减操作的时候,就需要使用NSMutableArray. 2.创建数组 //1.array NSA