Paragma & Bytes

// 非标准数据类型字节对齐原则
    // 数组  : 按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。 
    // 联合  : 按其包含的长度最大的数据类型对齐。 
    // 结构体: 结构体中每个数据类型都要对齐。

// 两种更改C编译器缺省字节对齐方式的方法 : 
    // #paragma pack(n); /* 设置 n 字节对齐 */
    // #paragma pack();  /* 设置字节对齐 */

    //__attribute__ ((aligned (n)));
    //__attribute__((packed));得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐.

    //使用实例:
    struct stu                                      struct stu
    {                               <---等同--->    {
        char sex;                   <---等同--->        char sex;
        int length;                 <---等同--->        int length;
        char name[10];              <---等同--->        char name[10];
    }__attribute__ ((aligned (1))); <---等同--->    }__attribute__ ((packed));

--------------------------------------------------------------------------------

#paragma pack(4);//设置4字节对齐

int main()
{
    unsigned char puc[4];

    struct tagPIM
    {
        unsigned char ucPim1;
        unsigned char ucData1 : 1;
        unsigned char ucData2 : 2;
        unsigned char ucData3 : 3;
    } *pstPimData;

    pstPimData = (struct tagPIM*)puc;

    memset(puc, 0, 4);

    pstPimData->ucPim1 = 1;
    pstPimData->ucData1 = 2;
    pstPimData->ucData2 = 3;
    pstPimData->ucData3 = 4;

    printf("%02X %02X %02X %02X \n", puc[0], puc[1], puc[2], puc[3]);

    return 0;
}

#paragma pack();//恢复默认字节对齐

/*
 * 打印结果 : “01 26 00 00”;
 */
 
/* 解释 : 
 * 对齐函数无论为何种都无意义,因为结构体中最大成员才1个字节,所以该成员会按自身对齐方式进行对齐。
 * 结构体强转等操作不解释,直接讲解关键处 : 
 *  结构体第1个成员 : ucPim1 占一个字节,ucPim1占据(struct tagPIM*)puc这快内存的第一个字节。puc[0] = 1;
 *  结构体后3个成员 : 占据了第二个字节的7个bit。
 *      ucData1 : 占1bit, 2的二进制为10,可将10中的一位存放在第二个字节的第1位。
 *      ucData2 : 占2bit, 3的二进制为11,正好可将其存放在第二个字节的第2、3位。
 *      ucData3 : 占3bit, 4的二进制为100,正好可将其存放在第二个字节的第4、5、6位。
 *  则第二个字节为 : 00100110,其16进制数为26。即puc[1] = 26;
 *  第三、四个字节根本就没用,所以为初始值0。
 */

转载于:https://www.cnblogs.com/xuejianhui/archive/2012/11/21/2780483.html

&lt;think&gt;我们正在讨论`WriteConsoleOutputCharacterA`函数,具体问题是关于`&amp;bytes`参数的含义以及如何让它存储整型数字。 首先,我们来分析`WriteConsoleOutputCharacterA`函数的原型: ```c BOOL WINAPI WriteConsoleOutputCharacterA( _In_ HANDLE hConsoleOutput, _In_ LPCSTR lpCharacter, _In_ DWORD nLength, _In_ COORD dwWriteCoord, _Out_ LPDWORD lpNumberOfCharsWritten ); ``` 参数说明: - `hConsoleOutput`: 控制台输出缓冲区的句柄。 - `lpCharacter`: 指向要写入的字符数组的指针(在ANSI版本中是单字节字符)。 - `nLength`: 要写入的字符数。 - `dwWriteCoord`: 控制台缓冲区中开始写入的位置(坐标)。 - `lpNumberOfCharsWritten`: 指向一个DWORD变量的指针,用于接收实际写入的字符数。这就是问题中提到的`&amp;bytes`,即传入一个DWORD变量的地址,函数会将实际写入的字符数存储在这个变量中。 用户的问题有两个部分: 1. `&amp;bytes`的含义:这个参数是用于输出实际写入的字符数。注意,这个参数是一个指向DWORD的指针,所以调用时我们需要传递一个DWORD变量的地址(例如,`DWORD bytes;`,然后传递`&amp;bytes`)。 2. 如何让它存储整型数字:这个参数本身就是用来存储整型数字(实际写入的字符数)的,所以用户可能误解了。或者用户可能想要写入整型数字(比如整数123)到控制台缓冲区?但是,这个函数是用来写入字符的,而不是直接写入整数。所以,如果用户想要在控制台显示整数,需要先将整数转换为字符串,然后写入该字符串。 因此,我们需要区分: - 参数`lpNumberOfCharsWritten`(即`&amp;bytes`)的作用:它存储的是实际写入的字符数(一个整数),这个整数是由函数填充的。 - 如果用户想要在控制台输出整型数字(比如123),则需要将这个整数转换为字符串,然后将字符串传递给`lpCharacter`参数。 所以,回答的重点: 1. 解释`lpNumberOfCharsWritten`参数的作用。 2. 说明如何将整数转换为字符串以便用该函数输出。 步骤: 1. 定义一个DWORD类型的变量(例如`bytesWritten`)来接收实际写入的字符数。 2. 调用函数时,将此变量的地址(即`&amp;bytesWritten`)作为最后一个参数。 3. 如果用户想要输出整数,需要先将整数转换为字符串(使用`sprintf`或`_itoa`等函数),然后将该字符串作为第二个参数传递。 示例代码片段: ```c #include &lt;windows.h&gt; #include &lt;stdio.h&gt; int main() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos = {0, 0}; // 从控制台缓冲区的(0,0)位置开始写 int number = 12345; // 想要输出的整数 char buffer[20]; // 用来存储转换后的字符串 // 将整数转换为字符串 sprintf(buffer, &quot;%d&quot;, number); DWORD charsWritten; // 写入字符串到控制台 BOOL success = WriteConsoleOutputCharacterA( hConsole, buffer, strlen(buffer), pos, &amp;charsWritten // 注意:这里传递的是charsWritten的地址 ); // 检查是否成功 if (!success) { // 处理错误 } // 此时,charsWritten中存储了实际写入的字符数(即整数的位数,例如12345是5个字符) return 0; } ``` 注意:`WriteConsoleOutputCharacterA`函数写入的是ANSI字符串(单字节字符),如果使用宽字符,应该用`WriteConsoleOutputCharacterW`。 关于用户提到的&ldquo;如何让它存储整型数字&rdquo;: - 参数`lpNumberOfCharsWritten`(即`&amp;bytes`)本身就是用来存储整型数字(实际写入的字符数)的,所以它已经存储了整型数字。 - 如果用户想要输出整数到控制台屏幕上,那么需要按照上述方法将整数转换为字符串。 因此,回答中将明确这两点。 相关问题: 1. 如何将整数转换为字符串以便在控制台输出? 2. `WriteConsoleOutputCharacterA`和`WriteConsoleOutputCharacterW`有什么区别? 3. 控制台输出函数`WriteConsole`和`WriteConsoleOutputCharacter`有何不同? &lt;/think&gt;### `WriteConsoleOutputCharacterA`函数中`&amp;bytes`的含义及整型数字存储方法 #### 一、`&amp;bytes`参数详解 在`WriteConsoleOutputCharacterA`函数中,`lpNumberOfCharsWritten`(即`&amp;bytes`)是一个**输出型参数**[^1]: ```c BOOL WINAPI WriteConsoleOutputCharacterA( HANDLE hConsoleOutput, LPCSTR lpCharacter, // 待写入的字符串 DWORD nLength, // 要写入的字符数 COORD dwWriteCoord, // 起始坐标 LPDWORD lpNumberOfCharsWritten // 即 &amp;bytes ); ``` - **作用**:接收实际成功写入的字符数量(整型值) - **数据类型**:指向`DWORD`(32位无符号整型)的指针 - **工作流程**: ```mermaid sequenceDiagram 程序-&gt;&gt;函数: 传入 &amp;bytes(DWORD变量地址) 函数-&gt;&gt;控制台: 执行字符写入操作 函数-&gt;&gt;bytes: 存储实际写入的字符数 程序-&gt;&gt;bytes: 读取写入结果 ``` #### 二、存储整型数字的方法 虽然`&amp;bytes`本身用于存储写入字符数,但可以通过以下方法存储**自定义整型数字**: 1. **直接存储整型结果** `bytes`变量直接存储整型值(如写入字符数): ```c DWORD bytes; // 声明DWORD整型变量 WriteConsoleOutputCharacterA(..., &amp;bytes); printf(&quot;成功写入 %d 个字符&quot;, (int)bytes); // 直接使用整型值 ``` 2. **存储转换后的整型字符串** 若需在控制台显示整型数字,需先转换为字符串: ```c int number = 42; // 要显示的整型变量 char buffer[20]; sprintf(buffer, &quot;%d&quot;, number); // 整型转字符串 DWORD bytes; WriteConsoleOutputCharacterA( hOutput, buffer, // 传入整型的字符串形式 strlen(buffer), (COORD){0,0}, &amp;bytes // 存储实际写入的字符数 ); ``` #### 三、完整示例:显示动态整型值 ```c #include &lt;windows.h&gt; #include &lt;stdio.h&gt; int main() { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); for(int i = 1; i &lt;= 10; i++) { char text[50]; int len = sprintf(text, &quot;当前计数: %d&quot;, i); // 整型转字符串 DWORD bytes; WriteConsoleOutputCharacterA( hOut, text, // 包含整数的字符串 len, (COORD){0, i}, // 每行显示一个数 &amp;bytes // 存储实际写入字符数 ); // 验证写入结果 if(bytes != len) { printf(&quot;写入错误! 预期:%d 实际:%d&quot;, len, (int)bytes); } Sleep(500); } return 0; } ``` 输出效果: ``` 当前计数: 1 当前计数: 2 ... 当前计数: 10 ``` #### 四、关键注意事项 1. **字节对齐问题** 当处理多字节字符集时,字符数与字节数可能不等: ```c // 计算实际字节长度 char str[] = &quot;中文测试&quot;; DWORD charCount = strlen(str); // 返回8(字节数) DWORD actualChars = charCount/2; // 实际显示4个字符 ``` 2. **错误处理** 始终检查函数返回值和`bytes`值: ```c if(!WriteConsoleOutputCharacterA(...) || bytes != expected) { DWORD err = GetLastError(); // 处理错误... } ``` 3. **缓冲区溢出防护** 当显示大整数时: ```c #define MAX_DIGITS 20 char buffer[MAX_DIGITS + 1]; // +1 for null terminator snprintf(buffer, MAX_DIGITS, &quot;%lld&quot;, 123456789012345LL); ``` &gt; 在Dev C++ 5.11中编译时,需开启**ANSI支持**(项目属性 &gt; 参数 &gt; 编译器 &gt; 添加`-fexec-charset=GBK`)[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值