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 #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等类型,也可以返回一个指针。但是当函数返回一个指针的时候需要注意,由于函数返回的指针只保存了一个地址值,如果该指针指向的是被调用函数中的局部变量,这就非常危险了,因为函数中的局部变量在函数调用结束之后会被自动释放,这样会导致该内存中所保存的数据是不确定的。所以,为哦了保证函数返回的指针是有效的,有三种方式:
- 如果函数返回的指针是指向函数中的局部变量,该局部变量应该使用static修饰。
- 让函数返回的指针指向暂时不会被释放的数据,如指向main()函数中的变量。
- 让函数返回的指针指向全局变量。
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、指向指针变量的指针:也称指针的指针。指针变量也是变量,也需要保存在内存中,因此指针变量也有自己的存储地址,如果再次定一个一个指针变量来保存这个地址,则这个指针变量就说指向指针变量的指针。
类型** 变量名;