printf参数,little endian,初始化列表来初始化

1,假设在一个 32 位 little endian 的机器上运行下面的程序,结果是多少?
这里写图片描述

解析:
1,大端字节序:高位存储在低字节
  小段字节序:低位存储在低字节
2,printf是一个库函数,C,C++中函数的参数是从右往左入栈的;
3,栈的生长方向是从高往低的

这里写图片描述
4,%d格式输出的是4个字节大小,而long long为8个字节
5,printf函数的原型是printf(const char*,…);第二个参数是任意个数目的参数,所以printf函数不是分个把参数入栈,而是一股脑全部压入堆栈,因此对于8字节的数据来说,堆栈中的数据是0001 0000
0002 0000 0003 0000,%d按四字节输出,因此,第一个%d输出1,第二个%d输出0,第三个%d输出2。
所以输出为:1,0,2

大端字节序存储的话:0000 0001 0000 0010 0000 0011

二:初始化列表的问题

1,如下:有一类A,其数据成员如下:
这里写图片描述
则构造函数中,成员变量一定要通过初始化列表来初始化的是:__

解析:

什么是初始化列表啊???

1,与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。(在C++中,struct和class的唯一区别是默认的访问性不同,而这里我们不考虑访问性的问题)

class foo
{
    string name ;
    int id ;
    foo(string s, int i):name(s), id(i){} ; // 初始化列表
};

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
初始化阶段

所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。

计算阶段
一般用于执行构造函数体内的赋值操作,下面的代码定义两个结构体,其中Test1有构造函数,拷贝构造函数及赋值运算符,为的是方便查看结果。Test2是个测试类,它以Test1的对象为成员,我们看一下Test2的构造函数是怎么样执行的。

class Test1
{
    Test1() // 无参构造函数
    { 
        cout << "Construct Test1" << endl ;
    }

    Test1(const Test1& t1) // 拷贝构造函数
    {
        cout << "Copy constructor for Test1" << endl ;
        this->a = t1.a ;
    }

    Test1& operator = (const Test1& t1) // 赋值运算符
    {
        cout << "assignment for Test1" << endl ;
        this->a = t1.a ;
        return *this;
    }

    int a ;
};

class Test2
{
    Test1 test1 ;
    Test2(Test1 &t1)
    {
        test1 = t1 ;
    }
};

调用代码:

Test1 t1 ;
Test2 t2(t1) ;

运行结果:
这里写图片描述

而这里:当我们将其Test2改变为这样之后就是:

class Test2
{
    Test1 test1 ;
    Test2(Test1 t1)//这里去除了引用了
    {
        test1 = t1 ;
    }
};

同样的,调用代码:

Test1 t1 ;
Test2 t2(t1) ;

运行结果:
这里写图片描述

由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。同样看上面的例子,我们使用初始化列表来实现Test2的构造函数

struct Test2
{
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
}

使用同样的调用代码,输出结果如下:
这里写图片描述

构造函数初始化时必须采用初始化列表一共有三种情况,
1,需要初始化的数据成员是对象(继承时调用基类构造函数)
2,需要初始化const修饰的类成员 ,因为常量只能在初始化,不能赋值,所以必须放在初始化列表中;
3,需要初始化引用成员数据 ,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表中;
4,没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

对于没有默认构造函数的类,我们看一个例子:

class Test1
{
    Test1(int a):i(a){}
    int i ;
};

class Test2
{
    Test1 test1 ;
    Test2(Test1 &t1)
    {
        test1 = t1 ;
    }
};

以上代码无法通过编译,因为Test2类中Test1 test1;需要调用默认的构造函数,但是Test1类没有无参的构造函数,但是由于Test1没有默认的构造函数,故而编译错误。正确的代码如下,使用初始化列表代替赋值操作。



class Test2
{
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
}

因为static属于类并不属于具体的对象,所以 static成员是不允许在类内初始化的,那么static const 成员是不是在初始化列表中呢?
答案是NO
一是static属于类,它在未实例化的时候就已经存在了,而构造函数的初始化列表,只有在实例化的时候才执行。
二是static成员不属于对象。我们在调用构造函数自然是创建对象,一个跟对象没直接关系的成员要它做什么呢

初始化:从无到有,创建了新对象;如:string foo = “Hello World!”
赋值:没有创建新对象,而是对已有对象赋值。 如:string bar; bar = “Hello World!”
有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。
如果成员是const或者是引用的话,必须将其初始化。类似的,当成员属于某种类类型且该类没有定义默认构造函数时,也必须将这个成员初始化

成员变量的初始化顺序

#include <iostream>
#include <stdio.h>
using namespace std;

class A
{
        public:
        int i;
        int j;
        A(int x):i(x),j(i){};
        void print()
        {
                cout<<i<<endl;
                cout<<j<<endl;
        }
};

int main()
{
        A a(3);
        a.print();
        return 0;
}

运行结果:这里写图片描述
若将代码该成这样:

A(int x):j(x),i(j){};

运行结果:
这里写图片描述
这里的i相当于未定义(因为先于j声明)。所以,一个好的习惯是,按照成员定义的顺序进行初始化。

关于const:

在类中声明变量为const类型,const常量的初始化必须在构造函数初始化列表中初始化,而不可以在构造函数函数体内初始化
#include <iostream>
using namespace std;

class A
{
    public:
        A(int size) : SIZE(size) {};
    private:
        const int SIZE;
};

int main()
{
    A a(100);
}

但是,但是,但是:
此时的const变量属于具体的一个对象,如何在整个类中都恒定不变呢?

答案是利用枚举,举例:

#include <iostream>
using namespace std;

class A
{
    private:
        enum {SIZE = 100};
    public:
        int array[SIZE];
};

int main()
{
    A a;
}

枚举常量不会占据对象的存储空间,在编译时被全部求值

但是,它隐含的数据对象类型为整形,不能表示其他类型。

问题

如何定义在类中定义非整形常量?(待解决)

const成员函数

任何不修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或调用了其他非const成员函数,编译器就会指出错误。

#include <iostream>
using namespace std;
class Stack
{
    public:
        void Push(int item);
        int Pop(void);
        int GetCount(void) const;
    private:
        int m_num; 
        int m_data[100];
};

int Stack::GetCount(void) const
{
    ++m_num;     //编译错误,企图修改数据成员
    Pop();       //编译错误,企图调用非const函数
    return m_num;
}

同一个类中,可以仅通过是否是const定义两个函数名字、参数、返回值完全相同的两个成员函数,例如:

#include <iostream>
using namespace std;

class A
{
    public:
        A(int v): val(v) {}
        void print_val() { cout << "not const:" << val << endl;}
        void print_val() const { cout << "const print_val:" << val << endl;}
    private:
        int val;
};
int main(int argc ,char **argv)
{
    A b(45);
    b.print_val();

    const A a(12);
    a.print_val();
}

输出
这里写图片描述
结论

同函数名、参数、返回值可以仅通过是否为const来定义为类的两个成员函数。在调用时,const对象调用const成员函数,非const对象调用非const成员函数。

问题 1:

不可以在const函数中改变成员变量的值,那么有没有办法改变?

答案是可以的,把成员变量声明为mutable类型。看程序:

#include <iostream>
using namespace std;

class A
{
    public:
        A(int v): val(v) {}
        void print_val() { cout << "not const:" << val << endl;}
        void print_val() const { val++; cout << "const print_val:" << val << endl;}
    private:
        mutable int val;
};
int main(int argc ,char **argv)
{
    A b(45);
    b.print_val();

    const A a(12);
    a.print_val();
}

结果:这里写图片描述

问题2:

当类中只有const函数,非const对象是否可以调用const函数?

答案是可以的,程序:

#include <iostream>
using namespace std;

class A
{
    public:
        A(int v): val(v) {}
//        void print_val() { cout << "not const:" << val << endl;}
        void print_val() const { val++; cout << "const print_val:" << val << endl;}
    private:
        mutable int val;
};
int main(int argc ,char **argv)
{
    A b(45);
    b.print_val();

    const A a(12);
    a.print_val();
}

结果:这里写图片描述
但是:只有const函数时,非const对象不可以调用那个const函数(否则,类的数据变量就会发生变化)。

问题3:

当类中存在只有 是否为const 不同的两个函数时,const函数是否可以暂时调用那个非const函数?

答案是可以的。用const_cast将转化掉表达式的const性质

#include <iostream>
using namespace std;

class A
{
    public:
        A(int v): val(v) {}
        void print_val() { cout << "not const:" << val << endl;}
        void const print_val() const { cout << "const print_val:" << val << endl;}
    private:
        int val;
};
int main(int argc ,char **argv)
{
    A b(45);
    b.print_val();

    const A *a = new A(45);
    const_cast<A*>(a)->print_val();
    a->print_val();
}

结果:这里写图片描述

注意

单纯用类转化不行

const A a(45);
const_cast<A> a.print_val();

报错:
这里写图片描述

问题4:返回类型是const是怎么回事?

const返回类型只有在修饰指针或引用是才有用。单凭const返回类型不可以重载。

<think>首先,用户的问题是:“我想使用memset函数将数组的所有元素初始化为特定值 请问如何使用memset初始化数组的所有元素为一个特定值”。我需要基于系统级指令和用户可见层的要求来回答。 系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用: - [^1]: 解释了memset用于初始化数组为0,但警告了对于非字符数组的潜在问题。 - [^2]: 讨论了memset初始化数组的“坑”,指出不能直接初始化为1等值。 - [^3]: 展示了使用memset初始化二维数组为0的例子。 - [^4]: 演示了memset用于字符数组初始化为字符。 用户的问题是如何使用memset初始化数组的所有元素为一个特定值。从引用中,我看到memset是按字节操作的,所以对于整数数组,初始化为0是安全的,但初始化为其他值(如1)可能会导致问题,因为它设置每个字节的值。 关键点: - memset(void *ptr, int value, size_t num) 设置每个字节为value。 - 对于整数数组(假设int是4字节),如果value=1,它会将每个字节设置为1,所以每个int元素会是0x01010101(在little-endian系统上),这相当于16843009,而不是1。 - 因此,memset只适用于初始化为0或-1(因为-1的二进制是全1,每个字节0xFF,所以int为0xFFFFFFFF,即-1)。 在回答中,我需要: 1. 解释memset的工作原理。 2. 说明适用于初始化为0或-1。 3. 警告对于其他值的潜在问题。 4. 提供代码示例。 5. 生成相关问题。 结构回答: - 逐步解释:先介绍memset,然后如何正确使用它。 - 使用中文。 - 添加引用标识:在引用参考内容的地方添加[^1], [^2], [^3], [^4]。 从引用: - [^1] 提到memset用于初始化0,但警告非字符数组的问题。 - [^2] 具体说明坑:不能初始化为1。 - [^3] 展示二维数组例子。 - [^4] 展示字符数组例子。 所以,在回答中,我可以自然地引用这些。 回答大纲: 1. **介绍memset函数**:解释memset是C标准库函数,用于设置内存块的值。 2. **如何使用它初始化数组**: - 对于初始化为0:安全,因为0的每个字节都是0。 - 对于初始化为-1:也安全,因为-1的每个字节是0xFF。 - 对于其他值:不安全,需要避免。 3. **代码示例**: - 整数数组初始化为0。 - 字符数组初始化为特定字符。 - 避免整数数组初始化为1的例子。 4. **注意事项**:强调memset按字节操作,对于非字符数组要小心。 5. **替代方案**:建议使用循环或其他方法初始化非0或-1的值。 6. **生成相关问题**:在最后添加相关问题部分。 确保LaTeX:如果有数学表达式,如内存地址或值,使用$...$。例如,$0x01010101$。 最终回答应结构清晰: - 开头直接回答问题。 - 逐步解释。 - 代码块(使用正确的格式)。 - 结尾相关问题。</think>### 如何使用 memset 函数初始化数组的所有元素为特定值 在 C 语言中,`memset` 函数是一种高效的内存初始化工具,但使用时需注意其底层机制。`memset` 的语法为 `void *memset(void *ptr, int value, size_t num)`,其中: - `ptr` 是数组起始地址。 - `value` 是设置的值(以 `int` 形式传递,但实际按字节处理)。 - `num` 是字节数(使用 `sizeof(arr)` 计算数组总字节大小)。 **关键点:`memset` 按字节操作**,这意味着它设置内存中每个字节为 `value` 的低 8 位(即 `value & 0xFF`)。因此: - **安全场景**:仅当数组元素类型为 `char` 或初始化为 `0` 或 `-1` 时,结果符合预期。 - 初始化为 `0`:所有字节为 0,元素值也为 0(整数、浮点数等均有效)。 - 初始化为 `-1`:所有字节为 `0xFF`,元素值为全 1(如整数 `-1`)。 - **风险场景**:对于非字符数组(如 `int` 数组),初始化为其他值(如 `1`)会导致意外结果,因为每个元素会被填充为重复字节模式(例如,`int` 元素值变为 $0x01010101$,即 16843009,而非 1)[^2]。 下面我将逐步说明如何正确使用 `memset`,并提供代码示例。 #### 步骤 1: 确定数组类型和初始化值 - 如果数组是 **字符数组**(`char` 类型),`memset` 可直接初始化为任何字符值(因为 `char` 占 1 字节)。 - 如果数组是 **整数或其他类型数组**,仅推荐初始化为 `0` 或 `-1`。其他值需避免使用 `memset`,改用循环初始化。 #### 步骤 2: 使用 `memset` 初始化数组 以下是安全用例的代码示例: 1. **初始化整数数组为 0**(安全且高效): ```c #include <stdio.h> #include <string.h> // 包含 memset 头文件 int main() { int arr[5]; // 使用 memset 将所有元素初始化为 0 memset(arr, 0, sizeof(arr)); // sizeof(arr) 计算数组总字节数 // 打印验证 for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); // 输出: 0 0 0 0 0 } return 0; } ``` - 说明:`memset` 将 `arr` 的每个字节设为 0,确保所有元素为 0。适用于一维或多维数组(如二维数组 `int arr[3][4]`,同样用 `memset(arr, 0, sizeof(arr))`)[^1][^3]。 2. **初始化字符数组为特定字符**(安全): ```c #include <stdio.h> #include <string.h> int main() { char str[10]; // 使用 memset 将所有元素初始化为 'a' memset(str, 'a', sizeof(str)); // 'a' 的 ASCII 值为 97 str[9] = '\0'; // 添加字符串结束符 puts(str); // 输出: aaaaaaaaa return 0; } ``` - 说明:字符数组每个元素占 1 字节,`memset` 直接设置每个字节为字符值[^4]。 3. **避免错误:初始化整数数组为 1(风险示例)**: ```c #include <stdio.h> #include <string.h> int main() { int arr[5]; memset(arr, 1, sizeof(arr)); // 错误:尝试初始化为 1 for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); // 输出类似: 16843009 16843009 ... (非 1) } return 0; } ``` - 原因:`int` 占 4 字节,`memset` 设置每个字节为 1(二进制 $00000001_2$),导致每个元素值为 $0x01010101$(十进制 16843009),而非预期的 1[^2]。 #### 步骤 3: 注意事项和替代方案 - **注意事项**: - `memset` 仅适合初始化为 0、-1 或字符值。对于其他值(如 1 或 100),结果不可预测。 - 在非字符数组上使用 `memset` 时,确保 `value` 的字节模式与元素类型兼容(例如,浮点数初始化为 0 可行,但初始化为 -1 可能不兼容 IEEE 754 标准)。 - 使用 `sizeof(arr)` 计算大小,避免手动计算错误。 - **替代方案**:如果需初始化为非 0/-1 的值(如 1),使用循环: ```c int arr[5]; for (int i = 0; i < 5; i++) { arr[i] = 1; // 正确初始化每个元素为 1 } ``` #### 总结 - 使用 `memset` 初始化数组为特定值时: - 字符数组:安全,可设任意字符。 - 非字符数组:仅安全用于 0 或 -1。 - 避免对整数数组初始化非 0/-1 的值,以防止字节级错误[^2][^4]。 如果您有特定数组类型或值需求,可提供更多细节,我会给出针对性建议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值