C语言中数组名到底是什么?

一般情况下,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;
}

输出的结果可能是:

ellipse
图1 结果示意

示例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;
}

输出结果:

ellipse
图2 结果示意

指针和数组同阶示例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]
  • 17
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值