一般情况下,C语言中数组名在表达式中被解读为指向数组首元素的指针
C语言中数组名在表达式中被解读为指向数组首元素的指针, 即数组名在表达式中值为数组首元素的地址。(但有个例外,int a[2];int (*pa)[2]; pa=&a;
数组名前加&
,取得“指向数组的指针”,这时a
不解读为指向数组首元素的地址)。根据ANSI C的定义,在数组前加上&,可以取得“指向数组的指针”。
例如,int a[2];
在表达式中a
表示数组首元素地址;
再例如,int a[3][2];
在表达式中a
仍然表示数组首元素的地址,不过此时数组a
的基本元素是int[2]
,因此a+1
每次会增加基本元素int[2]
的大小。
C语言中不存在多维数组,只存在数组的数组,所谓的多维数组是便于理解提出的一种说法而已。
C语言中数组类型是包含元素个数的。
示例1:
#include <stdio.h>
int main()
{
int a[3][2] = { 0 };
for (int i = 0; i != 3; ++i)
{
for (int j = 0; j != 2; ++j)
{
printf("a[%d][%d]: %p\t",i,j,&a[i][j]);
}
printf("\n");
}
printf("\n");
printf("&a + 1: %p\n", &a + 1); // 实际相对于a[0][0]地址增加了3*2*sizeof(int)=24
printf("a + 1: %p\n", a + 1); // 实际相对于a[0][0]地址增加了2*sizeof(int)=8
printf("\n--------------------------------------------\n\n");
int b[3] = { 0 };
printf("&b[0]: %p\n", &b[0]); // 数组b首元素地址
printf("&b + 1: %p\n", &b + 1); // 实际相对于b[0]地址增加了3*sizeof(int)=12
printf("b + 1: %p\n", b + 1); // 实际相对于b[0]地址增加了sizeof(int)=4
return 0;
}
输出的结果可能是:
示例2:
#include <stdio.h>
int main() {
int arr[5] = { 1,2,3,4,5 };
int(*parr)[5];
parr = &arr; // parr只有一个元素,即arr
for (int i = 0; i != 5; ++i)
{
// (*(parr+0))[i] += 1;
// (*parr)[i] += 1;
parr[0][i] += 1; // 很容易错写成parr[i] += 1;
}
for (int i = 0; i != 5; ++i)
{
// printf("%d\n", (*parr)[i]);
printf("%d\n", parr[0][i]); // 输出结果为2 3 4 5 6
}
}
C语言指针和数组同阶
所谓阶是方便理解自己发明的名词。
指针和数组同阶是指使用数组名的地方可以直接替换为指针。
如果数组作为指针(所指向的数组)的一个元素,则指针是数组的高阶。此时,称指针是高阶指针,数组是低阶数组。【注意不同颜色数组的含义】【此时指针是数组指针】
(不讨论其它诸如低阶指针、高阶数组的情况)。
题外话,C语言中用英语的规则阅读复杂数据类型便于理解。
例如,int a[3];
表示a
是数组,该数组的元素是int
类型。 用英语阅读则是:a is array of int。
再例如,int a[3][2];
表示a
是数组(元素个数2,元素类型int
)的数组(元素个数3,元素类型int[2]
),数组a
的元素还是数组(int [2]
)。因此在表达式中a
仍然表示指向数组首元素(类型是int[2]
)的地址,类型为int(*)[2]
。 用英语阅读则是:a is array(元素个数3,元素类型int[2]
) of array(元素个数2,元素类型int
)。
指针和数组同阶示例1:
#include <stdio.h>
/* num表示数组指针p指向的数组包含的元素个数,类比成所谓的多维数组的行数 */
void triple(int (*p)[2], int num)
{
for (int i = 0; i != num; ++i)
{
for (int j = 0; j != 2; ++j)
{
p[i][j] *= 3;
}
}
}
int main()
{
/* C语言不存在多维数组.数组a有三个元素每个元素是int[2]类型 */
int a[3][2] = { {1,2},{3, 4},{ 5,6 }};
for (int i = 0; i != 3; ++i)
{
for (int j = 0; j != 2; ++j)
{
printf("%d\t", a[i][j]);
}
printf("\n");
}
printf("---------------\n");
/* 在表达式中数组名表示数组首元素的地址,
a的元素类型是int[2],指向int[2]类型的指针的类型是int(*)[2],
即在表达式中a表示函数triple()第一个参数的类型 */
triple(a, 3); // 数组a与triple()的第一个参数p是同阶的,同阶指针赋值法是将数组名直接赋值给指针
for (int i = 0; i != 3; ++i)
{
for (int j = 0; j != 2; ++j)
{
printf("%d\t", a[i][j]);
}
printf("\n");
}
return 0;
}
输出结果:
指针和数组同阶示例2:
#include <stdio.h>
int main() {
int arr[5] = { 1,2,3,4,5 }; // 低阶数组
int(*parr)[5]; // 高阶指针
parr = &arr; // 高阶指针赋值法,将低阶数组的地址赋给高阶指针
for (int i = 0; i != 5; ++i)
{
// (*(parr+0))[i] += 1;
// (*parr)[i] += 1;
parr[0][i] += 1; // 很容易错写成parr[i] += 1;
}
for (int i = 0; i != 5; ++i)
{
// printf("%d\n", (*parr)[i]);
printf("%d\n", parr[0][i]); // 输出2 3 4 5 6
}
return 0;
}
指针和数组同阶示例3:
#include <stdio.h>
int main() {
int arr[5] = { 1,2,3,4,5 }; // arr是parr的同阶数组
int *parr; // parr是arr的同阶指针
parr = arr; // parr与arr是同阶的.同阶指针赋值法,将数组名直接赋给指针
for (int i = 0; i != 5; ++i)
{
parr[i] += 1;
}
for (int i = 0; i != 5; ++i)
{
printf("%d\n", parr[i]); // 输出2 3 4 5 6
}
return 0;
}
指针和数组同阶示例4:
#include <stdio.h>
int main()
{
int b[4] = { 0,1,2,3 };
int c[4] = { 4,5,6,7 };
int(*p)[4] = malloc(sizeof(int)*4*2);
for (int j = 0; j < 2; ++j)
{
for (int i = 0; i < 4; ++i)
{
if (j == 0)
{
p[j][i] = b[i];
}
if (j == 1)
{
p[j][i] = c[i];
}
}
}
for (int i = 0; i < 2; ++i)
{
printf("%d %d %d %d \n", p[i][0], p[i][1], p[i][2], p[i][3]); // 输出:0 1 2 3
}
// 输出:
// 0 1 2 3
// 4 5 6 7
free(p);
return 0;
}
传参示例:
#include <iostream>
#include <typeinfo>
/*
编译环境:cmake -G "Visual Studio 15 2017" -A x64 ..
*/
namespace test_hybrid
{
void testPointArray(){
int a[2][3] = {-1,-2,-3,1,2,3};
int (*p)[3] = a; // p是数组指针,每加1步进a的一个元素个大小,即3*sizeof(int);(注意多维数组是数组的数组,即a的每个元素是int[3])
for(int i = 0; i < 3; ++i){
// 可能的输出p[0] = 00000000001BF7C8, p[1] = 00000000001BF7D4, p[2] = 00000000001BF7E0,
std::cout << "p[" << i << "] = " << p[i] << ", ";
}
std::cout << std::endl;
// p = a[1]; // error C2440: “=”: 无法从“int [3]”转换为“int (*)[3]”
p = &a[1];
for(int i = 0; i < 3; ++i){
std::cout << "(*p)[" << i << "] = " << (*p)[i] << ", ";
}
std::cout << std::endl;
}
void testFunc1(int(*p)[3], int size){
std::cout << "&p is: " << &p << std::endl; // 可能的输出: 0133F86C,与&a不同
std::cout << "&(p[0]) is: " << &(p[0]) << std::endl; // 可能的输出: 0133F880,与&a相同
for(int i = 0; i < size; ++i){
for(auto it: p[i]){
std::cout << it << ", ";
}
}
std::cout << std::endl;
}
void testFunc2(int **p){
std::cout << "Entering testFunc2()...\n";
}
auto main() -> void {
std::cout << "testing hybrid..." << std::endl;
testPointArray();
int a[2][3] = {-1,-2,-3,1,2,3};
std::cout << "&a is: " << &a << std::endl; // 可能的输出: 0133F880
testFunc1(a, sizeof(a) / sizeof(a[0]));
// testFunc2(a); // [ERROR]error: cannot convert 'int (*)[3]' to 'int**'
auto b = a;
std::cout << "type(b): " << typeid(b).name() << std::endl; // int (* __ptr64)[3]
int a2[] = {1,2,3,4};
auto c = a2;
std::cout << "type(c): " << typeid(c).name() << std::endl; // int * __ptr64
auto &d = a2;
std::cout << "type(d): " << typeid(d).name() << std::endl; // int [4]
}
}
输出:
testing hybrid...
p[0] = 000000F287F9F978, p[1] = 000000F287F9F984, p[2] = 000000F287F9F990,
(*p)[0] = 1, (*p)[1] = 2, (*p)[2] = 3,
&a is: 000000F287F9FA08
&p is: 000000F287F9F9E0
&(p[0]) is: 000000F287F9FA08
-1, -2, -3, 1, 2, 3,
type(b): int (* __ptr64)[3]
type(c): int * __ptr64
type(d): int [4]