? ?但是有一个特例,那就是赋值0 的时候,这个时候表达的含义是空指针,比如:
int *ptr; ptr = 0; //这个是可以的
因此:
在这个程序中str1[0] = 'A' 的语句将会正确执行,而str2[0] = 'B' 的语句将会引发程序异常。
这是因为str2 仅存储了字符串常量Hello 的地址,常量数据是只读的,操作系统不允许对其进行修改。
而str1 虽然也是使用字符串常量进行初始化的,但采用的是复制的方式,即将字符串常量复制到str1 的内存位置上,这个时候str1 就和原来的常量没有关系了,可以任意使用。
- 注意:与其他变量一样,没有初始化或者赋值的指针变量存的值是随机的,如果此时直接使用
* 运算符,则会因为访问了不属于程序的内存位置而发生异常。也就是所谓的访问越界。 -
指针相关运算符的优先级 指针相关的运算符,优先级从高到底依次是: -
后置自增自减运算符。 -
前置自增自减运算符,取地址运算符,指针运算符。 -
&*ptr = &(*ptr) 。 -
*&a = *(&a) 。 -
(*ptr)++ 不等于*ptr++ 。 -
#include <iostream>
using namespace std;
//反转数组中的元素,参数为数组的第一个和最后一个元素
void Reverse(int *start,int *end)
{
/********** Begin **********/
int n = end - start + 1;
for(int i = 0 ; i < n/2 ; i++)
{
int t = *start;
*start = *end;
*end = t;
start ++;
end --;
}
/********** End **********/
} -
*++p 相当于a[++i] ,先将p 自加,再作* 运算。 -
*--p 相当于a[--i] ,先使p 自减,再作* 运算。 -
*p++ 相当于a[i++] ,先做* 运算,再将p 自加。 -
*p-- 相当于a[i--] ,先做* 运算,再将p 自加 -
//字符指针,复制字符串
char a[] = "Hello World",b[20]; //这两个是字符数组
char *pa = a, *pb = b; //这两个是字符串指针
while(*pa != '\0')
{
*pb = *pa;
pa++; //指针自增
pb++;
}
*pb = '\0'; //别忘了在字符串最后加上\0
cout << pa << endl;
cout << pb << endl; -
如果你观察细致的话,就会发现cout 对待字符指针与对待其他指针是不一样的,它会直接输出字符串的内容,如果想输出字符指针的地址值,则需要强制类型转换 -
char str1[] = "Hello";
char *str2 = "Hello";
str2[0] = 'B'; //别忘了指针也是可以使用[]运算符的
str1[0] = 'A';
数组变量的类型
之前我们说过数组变量可以看成是一个指向数组第一个元素的指针,但是它与指针还是有差别的。
对于一个数组int arr[10] 来说,arr 的类型不是int* ,而是**int[10] **,这样就产生一个问题,比如:
int *ptr; int arr[4];
cout << &arr << endl; //&arr的类型则是int(*)[4] cout << &arr + 1 << endl;
cout << &ptr << endl; //&ptr的类型是int**
? ? ? ?得到的结果是:??0x62cc3c
??? ? ? ? ? 0x62cc4c
?? ? ? ? ? ?0x62cc4c
? ? ? ? ? ?? 0x62cc50
可以注意到,&arr + 1 增加 的地址值是16 ,这是因为**int[4] 类型**的长度是4 * 4 = 16 字节。
这一点,从**sizeof 运算符**上也可以体现出来,比如:
int *ptr;
int arr[4];
cout << sizeof(ptr) << endl << sizeof(arr) << endl;
得到的结果是: 4 16
二维数组
-
int arr[2][3];
int *p1 = arr; //错误,类型不匹配
int (*p2)[3] = arr; //正确。但是注意:int *p2[3]不等于int (*p2)[3],前者是一个长度为3的数组,数组的每一个元素是int*的指针
cout << arr << endl;
cout << arr + 1 << endl;
cout << p2 << endl;
cout << p2 + 1 << endl; -
总的来说,以数组int a[4] 为例,有这三条规律: -
数组名可以看做是指向数组第一个元素的指针。 -
&a + 1 将跨越16 字节,相当于把a 的管辖范围上升了一个级别。 -
*a 等价于a[0] ,相当于把a 的管辖范围下降了一个级别。 -
之前说到数组作为形参时,第一个维度是可以省略长度的,这实际上是因为数组形参会被变成其元素指针类型,比如: void Func(int a1[],int a2[][10]); //等价于 void Func(int *a1,int (*a2)[10]) -
void Size(int arr[10][10])
{
cout << sizeof(arr) << endl;
}
int main()
{
int arr[10][10];
Size(arr);
cout << sizeof(arr) << endl;
} 输出 4
-
int a;
const int b = 0; //一个整型常量
const int *p1 = &b; //常量指针
p1 = &a; //正确
*p1 = 0; //错误
int * const p2 = &b; //指针常量,只能初始化
p2 = &b; //错误
p2 = &a; //错误
*p2 = 0; //正确
const int * const p3 = &a; //指向常量的指针常量,等于上面两种之和 常量指针和指针常量 -
常量指针说的是指向一个常量的指针,用于保证所指内容不被修改,但是指针本身是可以被修改的,而且不仅可以指向const常量b,也可以指向变量a; -
指针常量只能初始化,可以改内容,不能改指针本身; -
指针能作为函数参数,自然也能作为函数的返回值。不过需要注意的是,返回的指针不应该指向函数的局部变量,因为局部变量只在函数这一次被调用期间有效,如果返回了指向局部变量的指针,又在之后的程序中访问了这个指针所指的内容,就会访问越界,可能会引发程序异常 -
静态局部变量与非静态局部变量的区别在于,在函数结束后,静态局部变量依然存在,而且在下次执行函数时使用的仍然是同一个变量。但需要注意的是,它只进行一次初始化,之后调用函数时会跳过初始化的语句
-
char s[10] = "China";
char *sptr = s;
cout << sptr;
cout << *sptr ;// 只输出C
这条语句将输出字符串“China”。之前学习字符数组时应该知道,语句cout << s; 会输出数组 s 中存储的整个字符串,实际上 C++ 在使用 cout 输出 char 类型指针时,不是输出字符指针的值(地址),而是输出从该地址开始的字符串(逐个输出一个个字符,直到碰到 ‘\0’ 为止)。所以cout << sptr; 和cout << s; 的作用一样,都是输出字符串“China”。?
如果函数参数传了个指针,返回值也不是指针的话,对指针str++什么的,最后返回到主函数里还是从str[0]开始输出
????????
|