++ --和*的优先级相同,同属第二等级,从右向左结合。
废话不多说,直接看代码。
void test0() {
int arr[5] = {1,6,10,8,4};
int* p = arr;
cout << *p++ << endl;
cout << *++p << endl;
cout << ++*p << endl;
cout << *p << endl;
cout << *--p << endl;
cout << --*p << endl;
cout << *p-- << endl;
cout << *p << endl;
for (size_t i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
数组名出现在表达式右边时,会将其转换为数组首元素的地址。注意这里不是数组的地址而是数组首元素的地址。
void test01() {
int arr1[10] = { 0 };
/*int arr2[10] = arr1;*/
int* p = arr1;
/*for (size_t i = 0; i < 10; i++){
cout << *arr1++ << endl; //int * const p
}*/
cout << sizeof(arr1) << endl; // sizeof(int) * 10 int[10]
cout << arr1 << endl;
cout << arr1 + 1 << endl;
cout << &arr1 + 1<< endl;
}
总结一下:数组名在遇到sizeof和取地址符&时候是当做数组的首地址对待,对于一维整形数组而言,类型为int[],其余情况下当做数组首元素地址对待,就是int*。
C和C++中数组是按照行存储的,二维数组可以看做是每个元素为一维数组的一维数组。一维数组的规则同样适用于这里,下面结合具体代码分析下:
void test03() {
int d2Arr1[][4] = { {1,2,3,4},{5,6,7,8},{8,9,3,1} };
int d2Arr2[][4] = { 1,2,3,4,5,6,7,8 };
int d2Arr3[1][4] = { 1,2,3,4 };
//int(*pp1)[4] = d2Arr1[0];
int(*pp1)[4] = d2Arr1;
int(*pp2)[4] = &d2Arr1[0];
cout << *(pp1 + 1) << endl;//第一行的首元素的地址
cout << *(*(pp1 + 1) + 1) << endl;//第一行第一个元素的值
cout << d2Arr1[0]<< endl;
cout << d2Arr1[0] + 1 << endl;
cout << &d2Arr1[0] + 1 << endl;
}
**总结一下:
arr + i == p + i arr表首元素地址,二维数组相当于元素为int[]的一维数组。
a[i] == p[i] == *(a + i) == *(p + i) 都代表第i行的首元素的地址
a[i][j] == p[i][j] == (a[i] + j) == (p[i] + j) == ((a + i) + j) == ((p + i) + j) 结合上图理解一下。
从右向左看
1、const 关键字出现在 * 的左边:作用于指针指向的内容。
2、const 关键字出现在 * 的右边:作用于指针本身。
void test02() {
int a = 4;
int b = 5;
int const* p1 = &a;//const 在 * 左边 表示int类型的数不能被改变,是一个常量指针。
const int* p2 = &a;//const 在 * 左边同上,不过个人感觉上面写法易于阅读。
int* const p3 = &b;//const 在 * 的右边,指针本身不能被改变,是一个指针常量。
const int* const p4=&a; //从右向左 第一个const在*右边作用于指针,指针不能变,第二个在*的左边,int类型的数不能变,因此表示指向常量的指针常量。
int const* const p5=&b; //同上
}
函数指针的定义方式: 函数返回值类型 (* 指针变量名) (函数参数列表)
如:int(*p)(int, int);
怎么判断一个指针变量是指向变量的指针变量还是指向函数的指针变量呢?首先看变量名前面有没有“”,如果有“”说明是指针变量;其次看变量名的后面有没有带有形参类型的圆括号,如果有就是指向函数的指针变量,即函数指针,如果没有就是指向变量的指针变量。函数指针没有++ --运算。
函数指针这么简单吗?No!不要太天真。看下面代码:
double (* DC( double (*)(double) ) )(double);
void (*signal(int, void(*)(int)))(int);
char *(* c[10])(int **p);
你能说出来他们是什么意思吗?可以的话不要浪费时间往下看了。
先分析 double (* DC( double ()(double) ) )(double);
DC(double()(double)) 是一个函数,函数的参数类型为double()(double),即参数类型是一个函数指针类型(参数为double返回值为double的函数指针类型)。
除了DC(double()(double))之外的都是函数的返回值,即返回值类型是
double(*)(double)即返回值也是一个函数指针类型。
这个函数指针是一个 输入一个函数,输出也是一个函数的函数指针。
void (signal(int, void()(int)))(int);
signal(int, void()(int)) 参数为int和void()(int) ,void()(int)是一个函数指针类型。
void ()(int) 返回值也是一个函数指针类型。
这是一个参数是int和一个函数指针类型,返回值是一个函数指针类型的函数指针。
char ( c[10])(int **p);
c[10]数组,数组元素的类型是char ()(int p);
这明显是一个函数指针类型,即参数是intp类型返回值是char*类型的函数指针类型。
因此这代表一个大小为10,数组元素类型为函数指针类型的一个数组。
看起来特别麻烦! C语言中可以使用typedef简化:
void (*signal(int, void(*)(int)))(int);
typedef void(*pfun_t)(int) ;
pfun_t signal(int,pfun_t);
C++可以使用auto
auto DC(auto (*)(double) -> double) -> auto (*)(double) -> double;
这里的问题就是,C/C++并没有把“定义变量”和“变量的类型”这两件事分开,而是用类型说明符来同时承担了。也就是说,“定义一个int类型变量”这件事中,int这一个关键字不仅表示“int类型”,还表示了“定义变量”这个意义。这件事放在定义变量这件事上可能还不算明显,但放到定义函数上就不一样了!
由于数组不可以复制,导致了数组同样不支持传参,因此我们只能采用“首地址 + 长度”的方式来传递数组:
void copyArr(int origin[]) {
cout << sizeof(origin) << endl; // 4/8 int * p
cout << sizeof(origin) / sizeof(origin[0]) << endl;
}
void test01() {
int arr1[10] = { 0 };
copyArr(arr1); //隐式类型转换 int[10] 数组类型 --> int * 首元素指针
}
上面的参数类型写成这几种是等价的:int origin[10] int origin[] int *p
可能的原因是作者考虑的是数组的实际使用场景,是经常会进行切段截取的,
也就是说,一个数组类型并不总是完全整体使用,我们可能更多时候用的是其中的一段。
解决办法:
void copyArr(int* p, size_t size) {
//C传递数组
}
//C++特有
void copyArr(int(&origin)[10]) {
cout << sizeof(origin) << endl;
cout << sizeof(origin) / sizeof(origin[0]) << endl;
}
关于参数的传递有 值传递,指针传递,和引用传递(&)
值传递指的是传递的就是实参的一个副本。
指针传递其实也是一种值传递,不过传递的是指针的副本。
引用传递,传递的是原本的数据。
推荐详细内容看这篇博客:https://www.cnblogs.com/dingxiaoqiang/p/8012578.html
int arr1[5];//整形数组
int (*p1)[5] = &arr1//指向整形数组的指针
int* arr2[5];//整形指针数组
int* (*p2)[5];//指向整形指针数组的指针
int(*p3)(int,int);//函数指针
int(*p3[4])(int,int)//[]优先级比*高,表示一个函数指针数组
int(*(*p3)[4])(int,int)//指向函数指针数组的指针。