C/C++数组初始化的一些误区

C/C++数组初始化的一些误区以前我这样初始化一个数组,并自我感觉良好:inta[5]={0};//全部初始化为0这种简单的写法让我非常爽,于是我又想把数组全部初始化为1:inta[5]={1};//我想全部初始化为1直到十分钟前,我都以为这句代码确实能够将5个元素全部初始化为1,但事实跟我想的完全不同!(基础的东西革命的本钱,疏漏不得啊)全部初始化为0的那行代码确实是没问题的,可以

大家好,又见面了,我是你们的朋友全栈君。

以前我这样初始化一个数组,并自我感觉良好:

int a[5] = { 0 };    // 全部初始化为0

这种简单的写法让我非常爽,于是我又想把数组全部初始化为1:

int a[5] = { 1 };    // 我想全部初始化为1

直到十分钟前,我都以为这句代码确实能够将5个元素全部初始化为1,但事实跟我想的完全不同!(基础的东西革命的本钱,疏漏不得啊)

全部初始化为0的那行代码确实是没问题的,可以正常工作。问题就出在想把数组全部初始化成一个非0的数,即非默认值,是行不通的(查看内存发现,只有数组的第一个元素被初始化为1,其他全为0)。这倒不是因为编译器对初始化为0给了个后门,而是因为一条基本语法规则:

数组初始化列表中的元素个数小于指定的数组长度时,不足的元素补以默认值。

对于基本类型int来说,当然就是补int()即0了。再看一下非基本类型的数组:

string a[5] = { "foo" };

有了上面的规则,就很容易知道其实相当于:

string a[5] = { "foo", "", "", "", "" };

即后面4个元素调用了string的默认构造函数进行的初始化,而第一个则调用的string::string(const char*)进行的初始化。

还有一个区别:

int a[5];
string a[5];

如果不明确指出初始化列表,那么基本类型是不会被初始化的(除全局变量和静态变量外),所有的内存都是“脏的”;而类类型则会为每个元素调用默认构造函数进行初始化。

注意,在C++11中中间的赋值号可以省略,即 int a[5]{1}; 并且,如果初始化列表为空,如 int a[5]{},那将初始化所有元素为默认值,即与 int a[5]{0}; 等价


动态数组的初始化

说完了栈中的数组的初始化,我发现new一个数组和其又有一些不同:

int* a = new int[5];
string* a = new string[5];
int* a = new int[5] { 0 };
string* a = new string[5] { "foo" };

上面几行代码遵循栈中数组的初始化规则,除此之外这里还有一个新语法:

int* a = new int[5]();

注意后面的一对圆括号,它的意思是使用默认值初始化整个数组,所以对于类类型来说,new string[5] 与 new string[5]()是等价的,都会调用默认构造函数进行初始化;但是对于基本类型就不同了,new int[5]根本不会初始化,而new int[5]() 则会使用int()的值即0进行初始化。

看到这对圆括号,我想它该不会是元素的构造函数的参数列表吧,那么我可能会想将数组全部初始化为1:new int[5](1); 看起来很合理,但其实不行。事实上这对圆括号不是数组元素的构造参数,
可能是整个数组的,它有三个重载版本:

C/C++数组初始化的一些误区
C/C++数组初始化的一些误区
C/C++数组初始化的一些误区


看起来像是常引用、右值引用、和默认版本。所以假如已经有一个相同大小的数组b,试着用b来初始化a:

int* a = new int[5](b);

结果编译出错,提示error C3074: an array cannot be initialized with a parenthesized initializer,看来这个括号的作用和我想的不一样,其实也应该看出来的,要是是用另一个数组初始化的话那么参数应该是const int (&)[5] 而不是 const int [5] &,而且后者好像是一个错误的类型。这个问题暂时无解。

错过了初始化时机(memset的误区)

如果想在数组创建结束后再对其进行初始化,可以使用C函数memset(),但是memset的使用有个大问题,就是它只对char类型的数组管用:

char a[10];
memset(a, 1, 10);    // 将每个元素设置为1

如果将上面的a数组换成int或其他类型的,就会出现问题,因为memset的内部实现是以字节为单位进行赋值的,int 类型大于一个字节(假设是4个),数组内存连续,如果有下面代码:

int a[10];
memset(a, 1, sizeof(a));

将只会对前sizeof(a)即
40个字节进行赋值1的操作,即给“前5个int”进行了赋值0x01010101的操作,失之毫厘谬以千里!

如果实在想再初始化,那么老老实实循环赋值吧。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/161046.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 基本的导航条的制作

    基本的导航条的制作1、垂直导航条的制作一想到导航菜单就会想到用 ulli无序列表来制作。因为他的语义非常接近条目性的内容。<ulclass="nav"><li><ahref="#">首  页</a></li><li><ahref="#">关于我

    2022年7月22日
    8
  • RegisterStartupScript() 和 RegisterClientScriptBlock()[通俗易懂]

    RegisterStartupScript() 和 RegisterClientScriptBlock()[通俗易懂] 使用RegisterStartupScript()和RegisterClientScriptBlock()添加客户端脚本块  System.Web.UI.Page类包含的两个方法可以将客户端脚本代码发送到由ASP.NETWeb页面提供的HTML中: RegisterStartupScript(key,sc

    2022年7月20日
    13
  • eclipse检出svn代码_系统出现乱码怎么办

    eclipse检出svn代码_系统出现乱码怎么办eclipse默认编码格式为GBK 将其更改为utf-8即可

    2022年10月14日
    1
  • NeatUpload的安装使用

    NeatUpload的安装使用版本:NeatUpload-1.2.32,用于文件上传。可传大文件。1.在VS工具箱中点右键选“选择项”……将Brettle.Web.NeatUpload.dll添加到工具箱。可以在添加后的工具箱看到

    2022年7月2日
    29
  • Oracle修改表名「建议收藏」

    Oracle修改表名「建议收藏」rename原表名to新表名转载于:https://www.cnblogs.com/LeiYang5237/p/8549526.html

    2022年5月13日
    42
  • 自定义类加载器加载jar包_类加载器的可见性

    自定义类加载器加载jar包_类加载器的可见性spring根本不会去管自己被放在哪里,它统统使用TCCL来加载类,而TCCL默认设置为了WebAppClassLoader,也就是说哪个WebApp应用调用了spring,spring就去取该应用自己的WebAppClassLoader来加载bean。这在真正理解线程上下文类加载器(多案例分析)中已有详细描述。因此,为了使spring使用自定义的类加载器进行加载,需要开一个线程,将这个线程的类加载器设置为自定义类加载器。publicStringtest(){try{

    2025年9月19日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号