您的位置:首页 > 编程语言 > C语言/C++

C指针总结

2016-03-22 19:36 253 查看
       C语言以其灵活性,从第一个标准直到现在,得到了广泛的应用。因为其贴近底层,能最大限度的将程序员从机器代码中解脱出来。但也因其语言的灵活性,使这成为一门较有难度的语言。而其完全相信程序员,将一切交与程序员处理的思想,也让很多不熟悉计算机原理的程序员大呼C语言的晦涩。

       今天我总结了部分C指针的资料,为自己学习,记录于此,也希望对别人有帮助。

一、指针简单介绍:

       指针不仅在C语言中,在更底层的汇编语言,机器语言中也使用很广泛。说到底,即是指向内存中某个单元(一般每个单元为8bits = 1Byte),这个位置可以是数据,比如说一个整型数据,或者一个字符串,或者一个整型数组,结构体;也可以是一段可以运行的机器代码,机器代码也像数据一样,存储在内存单元中,而这些代码也可以看作一些数据,只看你怎么解析,是数据,还是可执行的代码。

       已经知道指针是什么了,那么指针也是需要一块空间来存储的,这个空间有多少?首先指针必须能寻找到整个内存的所有单元。对于32位程序来说,其虚拟内存空间有4GB(2^32 bytes),对于64位程序来说,其虚拟内存空间有16TB * 2^20(2^64,但一般用不了这么多)。于是在一个32位程序中,4bytes的空间足以覆盖整个虚拟内存,64位程序需要8bytes。现在我们通俗点描述指针:指针是一块内存空间(其大小必须能将整个虚拟内存地址编码进去),这块空间的值是标示一个内存单元地址的。

二、指针类型:

       指针功能只是能标识一个内存地址,至于指向的这块内存到底是个什么数据类型,有什么数据,则需要用户指定,于是才出现了指针类型。其作用就是告诉编译器,指向的内存到底有多大(int 4bytes,long 4或8bytes,结构体A有sizeof(A)大小,指向的若是指针类型,则有4或8bytes。),需要怎么解析这部分数据。

int temp = 0x01020304; /* we assume it 4 bytes. */
char* ptr = (char*) &temp; /* explicitly convert it to char* */
for (i = 0; i < 4; ++ i)
printf ("%d ", ptr [i]);
       以上代码定义了一个temp类型,在小端字节序下,其内容从低位到高位依次是0x01、0x02、0x03、0x04。现在定义了一个char指针,可以通过使用这个指针逐个字节访问temp内容,可读也可写,这里打印出了4个字节的内容。
       指针长度,在同一台计算机上,同一种目标代码(32或64 bits),都是一样的。无论其类型是什么,即无论其指向的内存块是什么数据格式。类型本身只告诉编译器这块内存如何解读,这块内存总大小。

注意: void 类型的指针表示任意类型,但因其是void类型,即是没有类型的,所以编译器也不知道其指向内存大小是什么,怎么解析,我们不能用取值符* 将值取出来,要使用时,需进行强制转换。

三、指针运算:

        当指针声明类型后,如上一部分所说,告诉了编译器内存解析方法,指向内存大小,还告诉了编译器,其步进的长度。步进只是在指针上施加加法或者减法运算。以下部分我全部假设指针大小为4 bytes,我们不妨将指针的内容,不是指向的内容看作是整数类型(当然,若你想将之看作float或者4bytes长度的char*也行,但我保证这没有整数看起来明显)。

(1)加法,减法:指针只能与整数做加法,不能两个指针做加法,也可以是整型变量。

int temp = 0x01020304; /* 4bytes. */
char* ptr = (char*) &temp; /* explicitly convert it to char* */
int* int_ptr = &temp;
ptr += 1; /* addr + 1 */
int_ptr += 1; /* addr + 4 */
       此处我们假设temp的第一字节个内存地址位0x0001,则第二个字节内存地址位0x0002,第三个第四个分别为0x0003, 0x0004。当我们在char指针上做加法时,ptr + 1 得到0x0002。但是若再int指针上做加法,int_ptr + 1得到的值却不是0x0002,而是0x0005,也就是说,用int指针做整数加法,步进了int长度4。
      现在我们可以总结一下了,在指针上做整数加法,真正得到的地址 = 原来地址 + sizeof (指针类型) * 欲加的整数。此处地址值,就是指针内容,都是按整数表示的,这样看起来更直观。加法如此,减法也是同样的道理了。

(2)赋值:

       C中各个指针之间都可以随意转换,因为都是4bytes类型,看作int。但有的编译器在指针出现隐式转换时,可能会产生警告或者直接终止编译。这时候最好写一个强制转换,或者调整编译器选项。这在C++中尤其要注意。

       除此之外,C指针的值甚至可以直接硬编码,但也可能会在编译时警告(如GCC)或者直接终止。而且很多时候这样写在运行期很容易断错误。

(3)位云算,逻辑运算:

       将指针看作int,位云算与逻辑运算也即像整数运算一样。

       注意:指针指向的位置是否可读可写,与运行时进程结构和对虚拟内存管理有关,如x86_64平台上的Linux,进程将内存分页,每页有页描述符,其中标示了这个页到底是可读,可写,还是可执行,或者兼而有之。当是不可写的,则写入会出现运行时中断,当是可执行的(这一般是代码段),一般也不允许写入。

        C语言中的NULL是一个宏,一般定义如下:

#define NULL ((void*) 0)
有时候也会看到一些书上如是写: int * ptr = 0; 意思差不多,但多了一个强制转换。

四、一些较复杂的C指针定义:

首先解释一下,一般const关键字总是修饰靠近它右侧的东西。[]取值符的优先级比*的有限级高,一般先结合左面。

(1)const int  *ptr :const 右侧是int,即是修饰int的,再看*号,*号右结合,结合ptr,说明ptr是指针类型,现在便能看到,ptr是一个指针,指向类型为const int;也就是说,ptr不可以改变指向内容的值,但可以读取。

注意: const int *ptr指向的类型是const int,但也能传入int类型的地址。不过还是不能修改,只读指针;

             int* ptr 指针可以接收const int 类型的地址,将之理解为指针类型转换,并且可以通过这个指针修改const int的值,所以不安全,有的编译器只是爆出警告;

             const int* ptr 指针可以被另一个指针赋值,也就是说const不是修饰ptr的。

const int data = 20;
int data2 = 32;
const int* ptr = &data;
int* ptr1 = &data;
ptr = &data2;
// (*ptr) ++; // error.
(*ptr1) ++;
(2)int* const ptr:const右边是ptr,用来修饰ptr,之后有个*右结合,结合const ptr,或者理解成结合ptr,这样ptr就是一个指针,指向类型为int。因为const修饰ptr,所以ptr在初始定义后,就不能被其他指针赋值了。因为指针内容不可改变(4bytes的指针空间,不是指针指向的空间)。
(3)const int* const ptr :ptr指向类型为const int,有const修饰ptr,所以是只读指针,不能通过指针改变指向内容的值。

(4)int* ptr
:N是常量或常量表达式,且不能省略,若想使用如下的写法:

int* ptr [] = malloc (sizeof (int*) * 20); // 20 elements. error in initializing.
       是错误的,除非使用int** ptr;
       根据优先级,[ ]先结合左边的ptr,也即是说ptr是个数组,那类型即是int*,所以这个就是指针的数组,按刚才的声明 int * ptr [ 3];的大小应该是3 * sizeof (int*) = 3 * 4

(5)const int* ptr
:N是常量或者常量表达式,且不能省略,是一个储存const int 指针的数组。

(6)const int* const ptr
:常量指针的数组,指针类型为const int。

(7)int (*ptr)
:先看优先级,括号中的优先级最高,ptr是指针,之后是指向int
类型的指针,即是指向有N 个元素的int类型数组的指针,有点拗口……你不妨这样理解,将之看作是int
(*ptr),只是这样的写法C中是没有的,起码目前比较流行的GCC和VC都不支持这样写,切记!

int i = 0;
int data
= {1, 2, 3};
int datas [] = {1, 2, 3, 4}
int (*ptr)
= &data;
for (; i < 3; i ++)
printf ("%d ", (*ptr)[i]);
ptr = &datas;
(8)const int (*ptr)
:指向数组元素个数是N的指针,元素类型为const int。指向N + x 大小的数组也行,只是会警告,或者有的编译器可能无法通过编译。
(9)const int (* const ptr)
:有const 修饰ptr,其他和(8)一样。

(10)rtn_type (*funcptr) (params ...):函数指针的构造,rtn_type为返回值,funcptr是指针名字,后面一个括号中写参数表;函数指针是个只读指针,不能将值赋给指向的内存,因为指向部分为进程中的代码段,这类区域一般只是可读可执行不可写,但这个检测可能不是在编译时完成的,可能是程序运行时期由操作系统发出的错误中断。

       呃,好吧,要是上面的都理解了,可以看看这个构造,保证恶心:

const int* (*complex_ptr) (const int (* const)
, const int (* const)
) = NULL;
可以自己分析分析啊……

实验平台:4.0.0-kali1-amd64 #1 SMP Debian 4.0.4-1+kali2 x86_64

gcc version 4.9.2 (Debian 4.9.2-10)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 指针