OC学习3——C语言特性之指针

1、指针是C语言中的一个非常重要的概念,实际上,OC系统类的变量、自定义类的变量等都是指针。定义指针变量的语法格式如下,其中*代表一个指针变量,整个语法代表定义一个指向特定类型的变量的指针变量。注意:指针变量不能保存普通的数值,它只能保存指针(也就是变量或对象的地址)。函数的形参传递方式有值传递和地址传递两种,其中地址传递就是传递的指针。

类型* 变量名 ;

float* ft ;
int* a ;

2、关于指针,还有两个最基本的运算符,取地址运算符(&)和取变量运算符(*)。

&:取地址运算符,单目运算符,后面通常紧跟一个变量,该运算符用于读取该变量所在的内存地址。

*:取变量运算符,单目运算符,后面通常紧跟一个指针变量,该运算符用于读取该指针变量所指向的内存中的变量。

3、数组变量的本质就说指针常量,该指针常量指向第一个数组元素。下面两种赋值方式的本质是一样的,而且大部分时候都会采用第二种方式来获取数组的首地址。所以,将数组变量作为参数传递到函数中实际上是一种地址传递。

int arr[5] = {1, 2, 3, 4, 5} ;

int* p1 = &arr[0] ;  //将第一个数组元素的地址赋值给指针变量p1
int* p2 = arr ;  //将数组变量保存的地址赋值给指针变量p2

需要指出的是,虽然数组变量保存的是数组第一个元素的地址,但是数组中保存的地址是不能改变的,因此,数组变量应称为指针常量。所以,执行arr++,arr += 2这种语句都是试图对arr数组变量进行重新赋值,这都是错误的

4、指向多维数组的指针变量,例如int arr[3][4] ;实际上是相当于定义了如下的三个数组变量:

arr[0]:该数组中再次包含了arr[0][0]、arr[0][1]、arr[0][2]、arr[0][3]四个元素,其中arr[0]表示指向arr[0][0]元素的地址的指针变量。

arr[1]:该数组中再次包含了arr[1][0]、arr[1][1]、arr[1][2]、arr[1][3]四个元素,其中arr[1]表示指向arr[1][0]元素的地址的指针变量。

arr[2]:该数组中再次包含了arr[2][0]、arr[2][1]、arr[2][2]、arr[2][3]四个元素,其中arr[2]表示指向arr[2][0]元素的地址的指针变量。

注意,arr[1][2]与 *(arr[1]+2) 、*(*(arr+1)+2)表示的意义是一样的,都是表示取arr[1][2]的值

5、指针的运算除了取地址、取变量和赋值之外,还有一些其他的运算需要注意,具体介绍如下:

指针变量加(减)一个整数:当指针变量加或减n时,代表将该指针的地址加或减n*变量大小个字节。举例来说,对于int* p;类型的变量,假如当前p变量中保存的地址为0x00010004,p+2则代表的地址是0x0001000C,因为一个int类型的数据占据4个四节,所以p+2实际上是往后移两个int类型的数据,相当于移8个字节。而对于char* p1;类型,若p1保存的地址是0x00020003,则p1+4则表示的地址是0x00020007。

当两个指针变量指向同一个数组的元素时,两个指针变量可以相减:两个指针变量相减,返回两个指针所指数组之间元素的个数。如果两个指针不指向同一个数组的元素,那么这两个指针变量相减没有任何意义。

当两个指针变量指向同一个数组的元素时,两个指针变量可以比较大小:指向前面的数组元素的指针小于指向后面的数组元素的指针。需要指出的是,如果两个指针不指向同一个数组的元素,那么这两个指针变量比较大小没有任何意义。

6、C语言的底层没有对字符串进行定义,一般都是通过字符数组进行保存字符串。此外,还可以通过字符指针来表示字符串,即定义一个字符指针变量,然后将C格式的字符串赋给该指针变量。

char* str = "I love IOS" ;

C语言的自字符串在底层依然是才用字符数组进行保存的,而str则是一个char*型的指针变量,它指向该字符数组的第一个元素,也就是指向该字符数组的首地址。

7、指针变量除了可以指向普通的int变量、float变量和数组之外,还可以指向函数的入口。当定义函数之后,C语言允许定义一个指针变量来指向该函数,然后就可以通过该指针变量来调用函数了,使用函数指针变量的语法格式步骤如下:

  1. 定义函数指针变量:函数返回值类型 (*指针变量名)();
  2. 将任何已有的函数赋值给函数指针变量:指针变量名 = 函数名 ; 
  3. 使用函数指针变量来调用函数:(*函数指针变量名)(参数);
 1 #import <Foundation/Foundation.h>
 2 
 3 int max(int * data, int len)
 4 {
 5     int max = *data ;
 6     //采用指针遍历data数组的元素
 7     for(int *p = data; p < data+len; p++)
 8     {
 9         //保证max始终存储较大的值
10         if(*p > max)
11         {
12             max = *p ;
13          }
14     }
15     return max ;
16 }
17 
18 int main(int argc, char* argv[])
19 {
20     int data[] = {1,2,3,4};
21     //定义函数指针变量fnPt,并将max函数赋值给fnPt
22     int (*fnPt) () = max ;
23     //通过函数指针变量调用函数
24     NSLog(@"最大值 max = %d", (*fnPt) (data , 5)) ;
25 }

函数指针的主要作用就是(1)把指针函数当作形参传递给某些具有一定通用功能的模块。并封装成接口来提高代码的灵活性和后期维护的便捷性;(2)有些地方必须使用函数函数指针才能完成给定的任务,如linux系统中的异步信号中断处理,当发生某一触发信号时,需要调用相应的处理函数,此时需要使用函数指针来实现。

void (*signal(int signum,void(* handler)(int)))(int);  

参数一为信号条件,第二个参数为一个函数指针,它所指向的函数需要一个整型参数,无返回值。该函数的返回值也是一个函数指针,返回的指针所指向的函数有一个整型参数(一般不用)

8、函数既可以返回普通的int、float等类型,也可以返回一个指针。但是当函数返回一个指针的时候需要注意,由于函数返回的指针只保存了一个地址值,如果该指针指向的是被调用函数中的局部变量,这就非常危险了,因为函数中的局部变量在函数调用结束之后会被自动释放,这样会导致该内存中所保存的数据是不确定的。所以,为哦了保证函数返回的指针是有效的,有三种方式:

  1. 如果函数返回的指针是指向函数中的局部变量,该局部变量应该使用static修饰。
  2. 让函数返回的指针指向暂时不会被释放的数据,如指向main()函数中的变量。
  3. 让函数返回的指针指向全局变量。

9、指针数组是值数组中的每个元素都是一个指针变量,常见的main()函数的形参第二个参数定义是char* argv[]就是一个指针数组。详情可以参见数组指针和指针数组的区别

 1 //指针数组的定义语法
 2 类型* 变量名[长度];
 3 
 4 char* arr[3] ;
 5 arr[0] = "hello world!";
 6 arr[1] = "I love IOS";
 7 arr[2] = "how are you ?";
 8 
 9 //注意区分上面的指针数组与下面的区别
10 类型 (*变量名)[长度];
11 
12 //第二种写法中(*变量名)先形成一个整体,代表一个指针变量,该指针指向一位数组,因此表示定义一个指向一位数组的指针变量
13 int a[3][4];
14 int (*p)[4];  //该语句是定义一个数组指针,指向含4个元素的一维数组。
15 p=a;           //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
16 p++;          //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

 10、指向指针变量的指针:也称指针的指针。指针变量也是变量,也需要保存在内存中,因此指针变量也有自己的存储地址,如果再次定一个一个指针变量来保存这个地址,则这个指针变量就说指向指针变量的指针。

类型** 变量名;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C#

C#泛型方法解析

    C#2.0引入了泛型这个特性,由于泛型的引入,在一定程度上极大的增强了C#的生命力,可以完成C#1.0时需要编写复杂代码才可以完成的一些功能。但是作为开...

21590
来自专栏小樱的经验随笔

C/C++对bool operator < (const p &a)const的认识,运算符重载详解(杂谈)

下面来进行这段代码的分析: struct node {  //定义一个结构体node(节点) int x; int y; int len;   //nod...

35160
来自专栏星回的实验室

js重修课[五]:类与模块

这个Range类中定义了一个接受两个参数的构造函数,和includes、foreach、toString三个实例方法,以及ZERO一个静态类方法。

13320
来自专栏wym

运算符重载(超详细)

C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋...

18420
来自专栏峰会SaaS大佬云集

C#学习---基础入门(一)

值类型:整数/实数/字符/布尔    结构/枚举   -------值类型直接存储值(对一个对象进行修改,不会改动其他对象值)

8740
来自专栏Deep learning进阶路

Python随记(二)字符串

Python随记(二)字符串 1、  字符串的基本操作 字符串string,也是序列(sequence)的一种,所以所有标准的序列操作:索引、分片、乘法、判断成...

21500
来自专栏康怀帅的专栏

Redis set 类型

从集合 (set) 中插入或者删除元素,set 中不能有重复值 sadd set1 12 13 14 移除 srem set1 12 13 集合元素数量 sca...

36640
来自专栏Danny的专栏

【J2SE快速进阶】——Java中的equals和==的区别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

8120
来自专栏小樱的经验随笔

【Java学习笔记之三】java中的变量和常量

变量和常量     在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中...

32760
来自专栏机器学习算法工程师

最长递增子序列

最长递增序列不要求数组元素连续问题,返回递增序列长度和递增序列。o(n^2)做法,顺序比较以第i个元素开头的递增序列即可。 利用动态规划来做,假设数组为1, -...

42660

扫码关注云+社区

领取腾讯云代金券