七彩云南 发表于 2025-1-23 16:58:49

VS2005中sprintf函数结果不正确

下面的代码:
/*------------------------------------------
   RANDRECT.C -- Displays Random Rectangles
               (c) Charles Petzold, 1998
------------------------------------------*/

#include <windows.h>
#include <stdlib.h>         // for the rand function
#include <stdio.h>
#include <time.h>

HDC hdc;
long int times;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DrawRectangle (HWND) ;

int cxClient, cyClient ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                  PSTR szCmdLine, int iCmdShow)
{
   static TCHAR szAppName[] = TEXT ("RandRect") ;
   HWND         hwnd ;
   MSG          msg ;
   WNDCLASS   wndclass ;
   
   wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
   wndclass.lpfnWndProc   = WndProc ;
   wndclass.cbClsExtra    = 0 ;
   wndclass.cbWndExtra    = 0 ;
   wndclass.hInstance   = hInstance ;
   wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
   wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
   wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
   wndclass.lpszMenuName= NULL ;
   wndclass.lpszClassName = szAppName ;
   
   if (!RegisterClass (&wndclass))
   {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
   }
   
   hwnd = CreateWindow (szAppName, TEXT ("Random Rectangles"),
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        NULL, NULL, hInstance, NULL) ;
   
   ShowWindow (hwnd, iCmdShow) ;
   UpdateWindow (hwnd) ;
   
   while (TRUE)
   {
          if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
          {
               if (msg.message == WM_QUIT)
                  break ;
               
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
          }
          else
               DrawRectangle (hwnd) ;
   }
   return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
       char strTimes = { 0 };
       static time_t starTime, endTime;

   switch (iMsg)
   {
       case WM_CREATE:
                  hdc = GetDC (hwnd) ;
                  starTime = time(NULL);

                  return 0;

   case WM_SIZE:
          cxClient = LOWORD (lParam) ;
          cyClient = HIWORD (lParam) ;

          return 0 ;

       case WM_CLOSE:
                  endTime = time(NULL);
                  sprintf(strTimes, "用时 = %d 秒\n矩形数 = %d\n", endTime - starTime, times);
                  MessageBox(hwnd, strTimes, "结果", MB_OK | MB_ICONINFORMATION);
                  ReleaseDC (hwnd, hdc);
                  DestroyWindow(hwnd);

                  return 0;
         
   case WM_DESTROY:
          PostQuitMessage (0) ;

          return 0 ;
   }
   return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}

void DrawRectangle (HWND hwnd)
{
   HBRUSH hBrush ;
   //HDC    hdc ;
   RECT   rect ;
   
   if (cxClient == 0 || cyClient == 0)
          return ;
   
   SetRect (&rect, rand () % cxClient, rand () % cyClient,
                     rand () % cxClient, rand () % cyClient) ;
   
   hBrush = CreateSolidBrush (
                  RGB (rand () % 256, rand () % 256, rand () % 256)) ;

   //hdc = GetDC (hwnd) ;
   FillRect (hdc, &rect, hBrush) ;
   //ReleaseDC (hwnd, hdc) ;
   DeleteObject (hBrush) ;
       times++;
       Sleep(1);
}   


以上代码中 sprintf函数调用中的times, 用VS2005编译, 结果总是为0,但是用VC6编译能得以正确结果(生成的矩形数), 问题出的哪里?

FishC 发表于 2025-1-23 17:00:21

在您的代码中,问题的根源在于 `sprintf` 函数用于格式化字符串时,参数类型不匹配。在C语言中,`sprintf` 函数对于 `"%d"` 需要一个 `int` 类型的参数。然而,`times` 是 `long int` 类型(通常是 64 位),而 `time_t` 类型也是平台相关的。因此,您需要确保正确使用的格式化符号以匹配这些类型。

通过确保 `sprintf` 使用正确的格式化字符串可以解决此问题:

1. 将 `sprintf` 函数的格式化字符串更改为匹配 `long int` 类型。
2. 使用 `"%ld"` 来格式化 `long int` 类型的数据。


以下是修正后的代码:

char strTimes = { 0 }; // 确保有足够空间存储结果字符串
static time_t starTime, endTime;

...

case WM_CLOSE:
   endTime = time(NULL);
   sprintf(strTimes, "用时 = %ld 秒\n矩形数 = %ld\n", (long)(endTime - starTime), times);
   MessageBox(hwnd, strTimes, "结果", MB_OK | MB_ICONINFORMATION);
   ReleaseDC (hwnd, hdc);
   DestroyWindow(hwnd);
   return 0;

这样调整后,`sprintf` 函数应该会正确地输出 `times` 的值。

另外,要确保字符串缓冲区 `strTimes` 的大小是足够的,以便能够存储格式化后的字符串。此处我将其设定为 100 字符长度,只是为了确保足够空间以避免缓冲区溢出问题,您可以根据实际需要调整。

如果还有其他问题,请告诉我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

七彩云南 发表于 2025-1-23 17:16:01

但是我把times 定义为int输出时使用 %d ,结果还是为0;

同样的把times定义为long long int, 输出时使用 %lld, 结果仍然是0;

以上情况出现在VSS2005中, VC6能得出正确结果.

FishC 发表于 2025-1-23 17:16:37

七彩云南 发表于 2025-01-23 17:16
但是我把times 定义为int输出时使用 %d ,结果还是为 ...

这个问题可能与 `sprintf` 在不同编译器版本中的实现或编译器设置有关。在Visual Studio 2005中,新的C运行库可能会引入一些行为上的差异。以下是一些建议的排查和修复步骤:

1. **确保时间计算的正确性**:由于您提到 `times` 总是为0,请确保在代码中 `times` 变量实际被增量。仔细检查相关逻辑,比如在 `DrawRectangle` 中增加 `times++` 代码。

2. **字符串缓冲区问题**:确保 `strTimes` 的大小足够大,以包含格式化后的结果。此缓冲区需要能够容纳最长的字符串。设定一个合理的缓冲区大小,如:

   char strTimes;

3. **调试输出**:在 `WM_CLOSE` 之前,输出或打断点检查 `times` 的值,以确保在 sprintf 之前它是正确的。例如,您可以在调试模式下通过直接输出到控制台或通过其他方法来检查 `times` 的值。

4. **调用 _set_SSE2_enable(0)**:在一些特定旧系统上,问题可能与浮点运算相关,可以尝试调用 `_set_SSE2_enable(0)`。但大多数情况下这与您的问题无关。

5. **Visual Studio 设置**:检查编译器设置,确保没有设置特定选项从而导致对 `sprintf` 行为的影响。

以下是一些调试代码的例子:

#include <stdio.h>

// 在 WM_CLOSE 之前打印 times
fprintf(stdout, "times = %d\n", times);

// 或者在调试器中设置断点查看 times 的值

如果问题仍然存在,请逐步验证每一步以找到根源。确保没有遗漏初始化或者条件竞争等潜在错误。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

很cool的阳 发表于 2025-1-23 17:20:16

使用局部静态变量
将 times 定义为局部静态变量,而不是全局变量。这样可以确保 times 的值在函数调用之间保持不变。
修改代码如下:
void DrawRectangle(HWND hwnd)
{
    static long int times = 0;// 定义为局部静态变量
    HBRUSH hBrush;
    RECT rect;

    if (cxClient == 0 || cyClient == 0)
      return;

    SetRect(&rect, rand() % cxClient, rand() % cyClient,
                     rand() % cxClient, rand() % cyClient);

    hBrush = CreateSolidBrush(
                RGB(rand() % 256, rand() % 256, rand() % 256));

    FillRect(hdc, &rect, hBrush);
    DeleteObject(hBrush);
    times++;
    Sleep(1);
}
然后在 WM_CLOSE 消息中,将 times 的值传递给 MessageBox:
case WM_CLOSE:
    endTime = time(NULL);
    sprintf(strTimes, "用时 = %d 秒\n矩形数 = %d\n", endTime - starTime, times);
    MessageBox(hwnd, strTimes, "结果", MB_OK | MB_ICONINFORMATION);
    ReleaseDC(hwnd, hdc);
    DestroyWindow(hwnd);
    return 0;

七彩云南 发表于 2025-1-23 17:40:38

很cool的阳 发表于 2025-1-23 17:20


怎么把DrawRectangle函数里的静态变量传给WinMain中的sprintf呢?

七彩云南 发表于 2025-1-23 18:35:18

我用:
sprintf(strTimes, "矩形数: %d\n", times);

替换
sprintf(strTimes, "用时 = %d 秒\n矩形数 = %d\n", endTime - starTime, times);

即只传 times 一个变量给 sprintf函数时, 可以正确输出 times 的值, 但传2 个变量时times 的值总是0.

很cool的阳 发表于 2025-1-23 18:36:37

七彩云南 发表于 2025-1-23 17:40
怎么把DrawRectangle函数里的静态变量传给WinMain中的sprintf呢?

#include <windows.h>
#include <stdio.h>

// DrawRectangle 函数
int DrawRectangle() {
    static int staticVar = 0; // 静态变量
    return staticVar++;       // 返回静态变量的值并自增
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    int value = DrawRectangle(); // 获取 DrawRectangle 函数的返回值

    char buffer;
    sprintf(buffer, "Static Variable Value: %d", value); // 使用返回值
    MessageBox(NULL, buffer, "Message", MB_OK);

    return 0;
}

七彩云南 发表于 2025-1-23 19:10:27

真见鬼了.

把两个参数交换下位置, 先输出矩形数, 后输出时间
即把sprintf改为:
sprintf(strTimes, "矩形数 = %d \n时间 = %d 秒\n", times, endTime - starTime);

可以得到正确输出.

七彩云南 发表于 2025-1-23 19:56:32

增加一个中间变量保存 endTime - starTime, 用于保存时间变量的值, 再把这个变量作为sprintf参数:
即:
int shijian;

shijian = endTime - starTime;
sprintf(strTimes, "时间 : %d 秒\n矩形数 : %d\n", shijian, times)
可以正确输出.

另:GCC11.4.0上没有这个问题, 可以正确执行并正确输出.
页: [1]
查看完整版本: VS2005中sprintf函数结果不正确