C语言中定义int a[10][10],a是什么类型?

论坛 期权论坛 期权     
匿名用户1024   2021-5-17 15:20   10122   5
我的理解是a是指向二维整型数组的指针
a[0]是指向一维整型数组的指针
a[0][0]是整型
这样对吗?
分享到 :
0 人收藏

5 个回复

倒序浏览
2#
有关回应  16级独孤 | 2021-5-17 15:20:33 发帖IP地址来自
借助Clang的AST来讲解这个问题。

对于这样的代码例子:
  1. int foo() {  int a[10][10];  return a[2][3];}
复制代码
在Clang中上述代码对应的AST长这样:
  1. `-FunctionDecl 0x102829c20  foo 'int ()'  `-CompoundStmt 0x102829fa0     |-DeclStmt 0x102829e08     | `-VarDecl 0x102829db0  a 'int [10][10]'    `-ReturnStmt 0x102829f80       `-ImplicitCastExpr 0x102829f68  'int'         `-ArraySubscriptExpr 0x102829f40  'int' lvalue          |-ImplicitCastExpr 0x102829f28  'int *'           | `-ArraySubscriptExpr 0x102829eb0  'int [10]' lvalue          |   |-ImplicitCastExpr 0x102829e98  'int (*)[10]'           |   | `-DeclRefExpr 0x102829e20  'int [10][10]' lvalue Var 0x102829db0 'a' 'int [10][10]'          |   `-IntegerLiteral 0x102829e48  'int' 2          `-IntegerLiteral 0x102829ed8  'int' 3
复制代码
可以看到,Clang所理解的是:
  • a的类型就是int [10][10],不多不少
  • 在把a当作指针使用时,它的类型会通过隐式类型转换来退化为一个指针类型,int (*)[10]
  • 然后,第一维下标访问后的表达式类型为 int [10]
  • 然后跟前面过程同理,把这个值当作指针使用会导致它的类型被退化为 int *
  • 然后,第二维下标访问后的表达式类型为 int。
3#
有关回应  16级独孤 | 2021-5-17 15:20:34 发帖IP地址来自
a的类型就是int[10][10],a[0]的类型是int[10],a[0][0]的类型是int

数组类型参与运算可能会自动转换为指针,int[10][10] -> int (*)[10],int[10] -> int *。也就是说a会变成一个指向一维数组的指针,a[0]会变成一个指向int的指针
4#
有关回应  16级独孤 | 2021-5-17 15:20:35 发帖IP地址来自



0. 如何理解
不过如果要更加系统化地理解C的类型表达式,可以使用下面的方法

一个声明本身可以看作是一个不断嵌套括号的“表达式”,例如int a[10][10]这个语句,其实它的构成是:
int (((a)[10])[10])
编译器在遍历抽象语法树的时候是这么考虑的:
1. 首先,令x1 =(((a)[10])[10]),这时相当于int x1;x1的类型是int,记作x1.type = int。
2. 接着,领x2 = ((a)[10]),这时有x1 = x2[10];这说明x2是一个长度为10,元素类型为x1.type的数组,其类型x2.type = array(10, x1.type) = array(10, int);
3. 然后,令x3 = a,则有x2 = x3[10];则有x3.type = array(10, x2.type);代入2)中的结果得:x3.type = array(10, array(10, int))。

其中,array(length, type)表示一个长度为length,元素类型为type的数组类型。
用递归方法表示就是:
1)a.type = array(10, t1)
2)t1 = array(10, t2);
3)t2 = int

1. 举一反三
那么好了,用这个方法,我们可以解释相当一部分很难理解的类型,例如这个诡异的声明语句:
int (*(x[10]))(char, char)
这是个什么鬼呢?首先,我们按照层次顺序来看:
1)x1 = (*(x[10]))(char, char);这时候声明等价于int x1;说明x1.type = int;
2)x2 = (*(x[10]));这时由x1 = x2(char, char),说明x2是一个返回类型为x1.type,输入两个char类型参数的函数,我们记作x2.type = func(x2.type, [char, char]);代入x1.type得到:x2.type = func(int, [char,char]);
3)x3 = (x[10]);这时候有x2 = *x3,说明x3是指向x2类型的指针,因此x3其实就是个函数指针啦!有:x3.type = ptr(x2.type) = ptr(func(int, [char,char]))
4)x4 = x;这时候有x3 = x4[10],说明x4是一个指向x3类型的长度为10的数组,即:x4.type = array(10, x3.type) = array(10, ptr(func(int, [char,char])))

那么x的类型是什么呢?用自然语言说起来有点绕口,那就是:
1)x是一个长度为10的数组,它的元素类型为t1;
2)类型t1是一个指针类型,t1指针内容的类型为t2;
3)类型t2是一个函数类型,该函数输入两个char类型参数,返回类型为t3;
4)类型t3为int

2. 如何使用?
声明一个复杂类型的变量容易,但是能合法地使用并不是很简单。以章节1部分的那个声明为例:
int (*(x[10]))(char, char)
这个变量的一个合法使用是:x[1]('a', 'b')

怎么理解这个表达式?也是按照层次来分析:
1)首先x[1],由于x.type = array(10, ptr(func(int,[char,char])))),
因此x[1]的类型是ptr(func(int, [char, char])),即一个指向函数的指针,指向的函数接收两个char类型值,返回int类型。
2)接着(x[1])('a','b'),相当于在x[1]的类型上再调用一次函数,由于x[1]是一个函数指针,因此可以直接作为函数调用,它接受了两个字符输入,因此这个表达式其实就是一个函数调用!

那么简单的如何解释呢?
x[1]首先获取x数组位置1的函数指针;
接着用参数('a','b')调用x[1]所指向的函数;
最后,调用完成后,返回这个函数在('a','b')下的返回值。

3. 应用
在弄明白了C语言这些声明语句的层次关系后,现在请设计一个函数表类型,这个函数表输入函数名称,返回一个该函数名称的重载函数列表的指针;这些函数都有相同的声明形式,如下:
int func(void *, void *)
在C++中,这么声明(本人没尝试过,哪位闲着无聊可以试一下能不能通过编译。。。):

using namespace std;
map func_map;

例如,当我要使用名称为“compare_100”函数名的函数列表时,通过:
auto list = func_map["compare_100"];
接着,我们选取这个list的第一个函数指针
auto fptr = list[0];
最后,调用该函数:
auto retval = fptr(x, y);

当然你也可以一步写完:
auto retval = func_map["compare_100"][0](x, y);
4. 用C写函数式编程范式程序
最后各位可以试一下下面这个定义声明,是不是能通过编译?理解一下它神马意思?
int (*(select_function(char *))(char, char));
给你们一点提示,下面是使用这个变量select_function的例子:
auto fptr = select_function("min_char");
auto a_b_min = fptr('a', 'b');
是不是有点像函数式编程范式?根据名称返回一个函数?

顺带一提,这可都是用纯C/C++语法写出来的哟,没借助什么函数编程范式的特有语法哦╮(╯▽╰)╭

PS:上面的声明我没有试着用VS测试过,如果有错的话请轻喷=A=!
PPS:当年硕士项目组的静态分析器我们就是这么识别变量类型的,如果这些声明有错,那,那我反正是不负责啦(怒摔)!
5#
有关回应  16级独孤 | 2021-5-17 15:20:36 发帖IP地址来自
指针、数组什么的只是表象,a本质上是个无符号常整数,即 const unsigned int 。变量也是表象,本质是内存。
如果你写过没有操作系统的单片机程序,可以经常见到这样定义变量的操作:
#define A (*(unsigned int *)0x01003020)
程序员清楚的知道以0x01003020为首地址的4字节内存空间是做什么用的——无论是没有被分配的空闲内存还是具体分配给了哪几个变量。
所以int a[10][10]; 可以这样实现:
#define a(i,j)  (*((int *)0x01003020 + (i) * (j)))
6#
有关回应  16级独孤 | 2021-5-17 15:20:37 发帖IP地址来自
就是int [10][10]
因为可以typedef int MyArray[10][10];
然后MyArray a;
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:136515
帖子:27303
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP