你真的清楚数组吗?我们还是以题目的方式来讲解
数组的类型就是数组,数组本身就是一个类型,任何不把数组当做类型都是愚蠢的,同时数组边界也是数组类型的一部分,我们举个例子。
int main() {std::is_same_v;//truestd::is_same_v;//false
}
int main() {const char array[10]{};using T = decltype(array[0]);
}
答案: const char&。
解释:
对数组类型(通过 typedef 或模板操作)应用cv 限定符会将限定符应用到它的元素类型,但元素是有 cv 限定类型的任何数组类型都会被认为拥有相同的 cv 限定性。如果在C语言,直到C23在明确这一概念。
始终认为数组类型与其元素类型有等同的限定,除了始终不认为数组类型有 _Atomic 限定: | (C23 起 |
---|---|
int main() {using T = decltype(("***"));
}
答案: 字符串字面量的类型是const char[4],T的类型是const char(&)[4]。
解释: 这两个看似是一个问题,其实不对,这里其实还考察了你对decltype这个关键字的了解。
字符串字面量的类型标准早有规定是**const char[N],N表示大小,并且字符串字面量属于左值,**那么按照decltype的规定,自然也推导出左值引用了。
#include
struct test
{int a;double b;char c[0];
};
int main() {auto t = (test*)malloc(sizeof(test) + 27 * sizeof(char));memset(t->c, 0, 27);//记得置0std::cout << sizeof * t << std::endl;for (int i = 0; i < 26; i++) {t->c[i] = 'A' + i;}std::cout << t->c << std::endl;free(t);
}
打印:16 ABCDEFGHIJKLMNOPQRSTUVWXYZ
答案:可以通过编译,经测试在vs2022和GCC12.2下,这是柔性数组。
解释: 可能这里有很多人会有疑问,C/C++明确规定不允许定义长度为0的数组,为什么这里却可以?
非标准扩展,不是c++的东西。
柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizeof 返回的这种结构大小不包括柔性数组的内存。
柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
我们必须得提一下,其实柔性数组这个东西是c99添加到c语言标准的,它实际上不算什么扩展,但是是结构体最后成员拥有不完整的数组类型,我们上面写的是大小为0的,所以我说那种是非标准扩展,并**不是说柔性数组是非标准的,**如果你有别的想法可以评论。文档说法见下
https://zh.cppreference.com/w/c/language/structzh.cppreference.com/w/c/language/struct
#include"iostream"
int main(){int n=10;int array[n];for(auto& i:array){i=6;}for(const auto&i:array){std::cout<
答案: 可以正常运行,在支持c99标准的编译器下,变长数组或者叫非常量长度数组是c99添加的,但是msvc不支持。
解释:
若 表达式 不是整数常量表达式,则数组声明器声明一个非常量大小的数组( VLA )。
#include"iostream"
int main(){int n = 1;label:;int a[n]; // 重分配 10 次,每次拥有不同大小printf("The array has %zu elements\n", sizeof a / sizeof *a);if (n++ < 10) goto label; // 离开作用域的 VLA 结束其生存期
}
答案: 能。
解释: 每次控制流经过该声明时,会求值 表达式 (而且它必须每次求值为大于零的值),然后分配数组(对应地, VLA 的生存期在其声明离开作用域时结束)。 VLA 实例的大小不会在其生存期改变,但在另一次经过同一代码时,它可能被分配不同大小。
#include"stdio.h"
void foo(size_t x, int a[*]);
void foo(size_t x, int a[x])
{printf("%zu\n", sizeof(a)); // 大小同sizeof(int*)
}
int main(){size_t n=10;int array[n];foo(n,array);
}
答案:能,原因如下。
解释:若大小是*
,则声明是对于未指定大小的 VLA 的。这种声明只能出现于函数原型作用域,并声明一个完整类型的数组。其实,所有函数原型作用域中的 VLA 声明器都被处理成如同用*替换表达式。友情提示,这种形式只能是C,而不是C++,经测试在gcc12.2的c++环境下不行,得c。
5.下面声明的数组,哪些错误,哪些正确?原因?
extern int n;
int A[n];
extern int (*p2)[n];
int B[100];
void fvla(int m, int C[m][m]);
答案: 第一第二错误,第三第四对。
解释:非常量长度数组与从它们派生的类型(指向它们的指针,等等) 被通称为“可变修改类型”( VM )。任何可变修改类型的对象只能声明于块作用域或函数原型作用域中。
6.下面哪几个声明的数组正确,哪些错误,为什么?
int main(){int n=10;static int array[n];extern int array_[n];int array__[n];
}
答案: 第一第二错误,第三个正确。
解释:VLA 必须拥有自动或分配存储期。指向 VLA 的指针,但不是 VLA 自身亦可拥有静态存储期。 VM 类型不能拥有链接。。
7.下面的代码能否正常运行?为什么
struct tag {int z[n]; int (*y)[n];
};
答案: 不能。
解释: 可变修改的类型不能是结构体或联合体的成员。
struct test
{int a;double b;char c[];
};
答案: 正确。
解释: 在struct定义中,未知大小数组必须出现作最后一个元素(只要有一个具名成员),这种情况下,这是称为柔性数组成员的特殊情形。C99起。同时按照划分,柔性数组也算未知大小的数组,我们之前写的柔性数组是大小为0的,其实都可以。
//test.cpp
int array[6]{ 1,2,3,4,5,6 };//main.cpp
extern int array[];
int main() {for (size_t i = 0; i < 6; i++)std::cout << array[i] << ' ';
}
答案: 可以。
解释:若忽略数组声明器中的表达式,则它声明一个未知大小数组。除了函数参数列表中的情况(这种数组被转换成指针),而且当初始化器可用时,这种类型是一个**不完整类型。**
int main() {int array[10]{};int array_[10]{};array = array_;
}
答案:不可以。
解释: 数组类型的对象不是可修改左值,尽管它们可以取地址,它们不能出现于赋值运算符的左侧。
#includevoid f(int(&& x)[10])
{std::cout << sizeof x << '\n';
}
int main() {using Type = int[];using Type2 = int[10];auto p = Type{1};auto p_ = Type2{1};auto p__ = Type2{};auto p2 =Type(10);auto p2_ =Type2(10);auto p2__ = Type2();f(Type2{10});//√f(Type2{});//√f(Type2(10));f(Type2());
}
答案: 在msvc下全部正常运行,在GCC下只有f(Type2{10}); f(Type2{});是正确的。
解释:
尽管数组无法从函数按值返回,且不能作为大多数转型表达式的目标,数组纯右值依然可以通过使用类型别名构成,并用花括号初始化的函数式转型来构造数组的临时量。至于编译器的结果,只能说,msvc这些写法基本没有必要,并且也是危险的,比如这里的p都被推导为一个int*的指针,指针指向一个纯右值的数组,显然,如果你在后面使用了这个指针去访问内存,至少是已经UB了。
至于用()而不是{}构造临时量,只能说是msvc允许,但按道理是不对的,也没必要。
int* p = new int[0];
delete[] p;
答案: 正确。
解释: 虽然之前说过C/C++标准规定数组大小不能为0,但是在用于[new] 表达式时,数组的大小可以为零;这种数组没有元素。你可能会疑问为什么还要delete?有的时候规定就是如此,你就算是new int[0]也一样调用了operator new等,返回了地址,p也指向了某个玩意,你不用在意,反正正常人不会这么写。
int main() {int array[6]{};using T = decltype( + array);
}
答案: T是int*类型,表达式的值类别是纯右值。
解释:
存在从数组类型的左值和右值到指针类型的右值的隐式转换:它构造一个指向数组首元素的指针。凡在数组出现于不期待数组而期待指针的语境中时,均使用这个转换。
你可能会觉得这个+很奇怪,它只是为了创造期待指针的语境从而让数组转换为纯右值仅此而已,类似在对lambda也有效。
int f(char s[3]);
int f(char[]);
int f(char* s);
答案: 1个
解释: 每个函数形参的类型根据规定,如果类型是“T 的数组”或“T 的未知边界数组”,那么它被替换成类型“T 的指针”。其实这应该是函数声明的内容,链接如下:函数声明 - cppreference.com
函数声明 - cppreference.comzh.cppreference.com/w/cpp/language/function
void f(int a[0]){}
答案: msvc错误,GCC正确
解释: 你们觉得是谁没遵守标准??
如果描述有误,请各位提出,窝会修改、、