原创内容,转载请保留出处。 我承认C/C++里面的指针、数组相当令人蛋疼。为了不让以后蛋疼,所以最近就研究了一下这个蛋疼的问题。先列一个本文内容的表,也算是为more标签做点贡献:
(本文基于C++语言,C语言可能略有不同)
指针的意义、定义及使用方法(水)
数组的意义、定义及使用方法(水)
使用负数下标访问数组 (雷人)
数组指针?指针数组? (吓人)
一.指针的意义、定义及使用方法
所谓指针,就是指向一个对象的变量,这个对象可以是内置类型、类类型甚至指针类型。 学习指针的最好方法是coding!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//定义:
//定义指针也很简单,只要在变量名之前加*就可以声明一个指针。一个指针变量所接受的值即是它指向的类型变量的地址,要得到这个地址可以用&操作符得到。
int ival1 = 123 ;
int * iptr1 ; //这样就定义了一个指针
iptr1 = & ival1 ; //这样就把iptr1指向了ival
//另外一种简洁点的方法:
int ival2 = 123 , * iptr2 = & ival2 ;
//使用:
//指针变量保存的是变量的地址,要使用指针指向的变量,就需要对指针进行解引用。解引用使用操作符*:
std :: cout << "the value of ival2 :" << ival2 << std :: endl
<< "the address of ival2 :" << iptr2 << std :: endl
<< "the value of *iptr2 :" << * iptr2 << std :: endl
<< "the address of iptr2 :" << & iptr2 << std :: endl
<< "is iptr2 == &ival2 ?" << ( iptr2 == & ival2 ) << std :: endl << std :: endl ;
//我们还可以使用指向指针的指针……
int ** pptr = & iptr2 , *** ppptr = & pptr ;
std :: cout << " ppptr:" << ppptr << std :: endl
<< " *ppptr:" << * ppptr << std :: endl
<< " **ppptr:" << ** ppptr << std :: endl
<< "***ppptr:" << *** ppptr << std :: endl ;
运行的结果如下:
1
2
3
4
5
6
7
8
9
10
the value of ival2 : 123
the address of ival2 : 0x7fff405d4ab8
the value of * iptr2 : 123
the address of iptr2 : 0x7fff405d4aa0
is iptr2 == & ival2 ? 1
ppptr: 0x7fff405d4a98
* ppptr: 0x7fff405d4aa0
** ppptr: 0x7fff405d4ab8
*** ppptr: 123
二.数组的意义、定义及使用方法
对于C++程序员,相比C程序员,可能就比较不喜欢数组了。因为数组的长度固定,需要自己管理,容易溢出……C++ STL提供了vector容器,安全性大大提高,但效率并不比数组差多少,因此C++程序员更喜欢vector。但也不得不说,合格的程序员是能够自己控制好程序的,溢出是可以避免的,长度也可以预设或者动态分配,况且还有那么些情况不能使用vector(比方说NOIP),那么数组就是唯一选择了。
所谓数组,就是一列类型相同的变量,它们在内存的分配上是连续 的。定义数组就是在变量名后加[size],size为数组大小,必须为常量、正整数。
1
2
3
4
5
6
int iarr [ 10 ]; //iarr实际上是一个指针,指向数组第一个元素
for ( size_t i = 0 ; i != 10 ; ++ i )
iarr [ i ] = i ;
cout << " iarr :" << iarr << endl
<< "*iarr :" << * iarr << endl
<< "iarr[0] :" << iarr [ 0 ] << endl ;
注意,C/C++的数组下标从0开始,一直到n-1(n为数组元素个数)。 输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
address of each item:
& iarr [ 0 ] = 0x7fff88eb0b60
& iarr [ 1 ] = 0x7fff88eb0b64
& iarr [ 2 ] = 0x7fff88eb0b68
& iarr [ 3 ] = 0x7fff88eb0b6c
& iarr [ 4 ] = 0x7fff88eb0b70
& iarr [ 5 ] = 0x7fff88eb0b74
& iarr [ 6 ] = 0x7fff88eb0b78
& iarr [ 7 ] = 0x7fff88eb0b7c
& iarr [ 8 ] = 0x7fff88eb0b80
& iarr [ 9 ] = 0x7fff88eb0b84
iarr : 0x7fff88eb0b60
* iarr : 0
iarr [ 0 ] : 0
三.使用负数下标访问数组
C/C++数组无法使用负数下标,这点让众多程序员很是头疼,尤其是OIer!!过去的方法基本上是在使用下标时加上一个偏移量。效率不说,万一某一个地方少加上偏移量了,那就慢慢debug去吧……其实,只要明白了数组和指针的关系,负数下标都是浮云啊!
1
2
3
4
int iarr [ 3 ] = { 0 , 1 , 2 };
for ( size_t i = 0 ; i != 3 ; ++ i )
cout << "iarr[" << i << "] = " << iarr [ i ]
<< "tt*(iarr + " << i << ") = " << * ( iarr + i ) << endl ;
输出:
1
2
3
iarr [ 0 ] = 0 * ( iarr + 0 ) = 0
iarr [ 1 ] = 1 * ( iarr + 1 ) = 1
iarr [ 2 ] = 2 * ( iarr + 2 ) = 2
!!!也就是说
a[n]与(a+n)是相同的 !也就是说&a[n]等同于&(a+n)等同于(a+n)!! 你是不是想到了什么?
1
2
3
4
5
6
7
int iarr [ 3 ] = { 0 , 1 , 2 };
for ( size_t i = 0 ; i != 3 ; ++ i )
cout << "iarr[" << i << "] = " << iarr [ i ]
<< "tt*(iarr + " << i << ") = " << * ( iarr + i ) << endl ;
int * a = iarr + 1 ; // equal to &iarr[1], but quite faster than it
for ( int i = - 1 ; i <= 1 ; ++ i )
cout << "a[" << i << "] = " << a [ i ] << endl ;
输出:
1
2
3
4
5
6
iarr [ 0 ] = 0 * ( iarr + 0 ) = 0
iarr [ 1 ] = 1 * ( iarr + 1 ) = 1
iarr [ 2 ] = 2 * ( iarr + 2 ) = 2
a [ - 1 ] = 0
a [ 0 ] = 1
a [ 1 ] = 2
更简单一点,用一个宏搞定。
1
2
3
4
5
6
7
8
#define ARRAY(TYPE, NAME, LEFT, RIGHT) TYPE __myarray_##NAME[(RIGHT-LEFT)+1]; TYPE *NAME = __myarray_##NAME - LEFT;
ARRAY ( unsigned int , a , - 3 , 2 )
for ( int i = - 3 ; i <= 2 ; ++ i )
{
a [ i ] = i + 3 ;
cout << "a[" << i << "] = " << a [ i ]
<< "tt__myarray_a[" << i << " + 3] = " << __myarray_a [ i + 3 ] << endl ;
}
这个宏用到了#define中的##,##用来把前后两个参数连接起来。 输出:
1
2
3
4
5
6
a [ - 3 ] = 0 __myarray_a [ - 3 + 3 ] = 0
a [ - 2 ] = 1 __myarray_a [ - 2 + 3 ] = 1
a [ - 1 ] = 2 __myarray_a [ - 1 + 3 ] = 2
a [ 0 ] = 3 __myarray_a [ 0 + 3 ] = 3
a [ 1 ] = 4 __myarray_a [ 1 + 3 ] = 4
a [ 2 ] = 5 __myarray_a [ 2 + 3 ] = 5
4.数组指针?指针数组?
我们知道sizeof 一个数组,回返回整个数组占用的空间大小;而sizeof 一个指针,回返回指针变量的大小。看看下面的程序,猜猜如何输出:
1
2
3
4
5
6
7
8
9
10
11
int ( * iarr1 )[ 10 ];
cout << "sizeof iarr1 :" << sizeof iarr1 << endl ;
cout << "sizeof *iarr1 :" << sizeof * iarr1 << endl << endl ;
int * ( iarr2 [ 10 ]);
cout << "sizeof iarr2 :" << sizeof iarr2 << endl ;
cout << "sizeof *iarr2 :" << sizeof * iarr2 << endl << endl ;
int * iarr3 [ 10 ];
cout << "sizeof iarr3 :" << sizeof iarr3 << endl ;
cout << "sizeof *iarr3 :" << sizeof * iarr3 << endl << endl ;
输出:
1
2
3
4
5
6
7
8
sizeof iarr1 : 8
sizeof * iarr1 : 80
sizeof iarr2 : 80
sizeof * iarr2 : 8
sizeof iarr3 : 80
sizeof * iarr3 : 8
够雷人的吧?下面来细细分析一下: int (iarr1)[10];定义了一个数组指针。所谓数组指针 ,就是一个指向数组的指针。因此iarr1占用空间为8。 iarr1指向一个10个元素的数组,所以iarr1占用空间为80。 int (iarr2[10]);定义了一个指针数组。所谓指针数组 ,就是一个数组元素为指针类型的数组。iarr2中保存了10个指针,因此iarr2占用空间为80。iarr2是一个指针,因此占用空间为8。 int iarr3[10];等同于int (iarr3)[10];,这是由于 操作符的右结合性以及优先级高于[]操作符。
The End.本文写的并不是很好,草草地掠过了以上四个知识点。至于详细的,还是实践出真知,大家不妨多加尝试。