由printf()引出的c/c++运行时库问题

本文探讨了在多线程环境下使用非线程安全版本的C运行时库导致的printf函数冲突问题,并介绍了如何通过配置项目设置选用多线程版本的C运行时库来解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写多线程时写到这么一段代码:

#define WIN32_LEAN_AND_MEAN
#include   
<stdio.h> 
#include   
<stdlib.h>
 
#include   
<windows.h>
 

DWORD WINAPI ThreadFunc(LPVOID); 
void AnotherFunc(void
); 

int
 main() 

    HANDLE hThrd;  
    DWORD  exitCode
=0

    DWORD  threadId
=0


    hThrd
=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&
threadId);  
    
if
(hThrd) 
    

        printf(
"launched  "
); 
    }
 

    
for
(;;) 
    

        BOOL rc; 
        rc
=::GetExitCodeThread(hThrd,&
exitCode); 
        
if(rc && exitCode!=
STILL_ACTIVE) 
            
break

    }
 

    ::CloseHandle(hThrd); 
    printf(
"returned %d "
,exitCode); 

    
return
 EXIT_SUCCESS; 
}
 

DWORD WINAPI ThreadFunc(LPVOID n) 

    
//Sleep( 100 ); 

    printf("running  "); 
    AnotherFunc(); 

    
return 0

}
 

void
 AnotherFunc() 

    ::ExitThread(
4
); 
}

打印出来的结果是:
launched
launched
running
returned 4

奇怪!怎么printf()执行了2次呢?去网上查了一下,原来我调用的printf是非线程安全版本的C运行时库 这个版本的printf内部使用了全局的缓冲区,当另外一个线程调用printf时,将launched重复打印了,我可以这样进行设置:

改用Multithreaded多线程版本的C运行时库

在Project->Settings->C/C++->Code Generation->Multithreaded

还有几个都可以选
Multithreaded DLL
Debug Multithreaded 
Debug Multithreaded DLL

附:在网上搜到一篇不错的介绍这方面的文章:

1)运行时库就是   C   run-time   library,是   C   而非   C++   语言世界的概念:取这个名字就是因为你的   C   程序运行时需要这些库中的函数.  
   
  2)C   语言是所谓的“小内核”语言,就其语言本身来说很小(不多的关键字,程序流程控制,数据类型等);所以,C   语言内核开发出来之后,Dennis   Ritchie   和   Brian   Kernighan   就用   C   本身重写了   90%   以上的   UNIX   系统函数,并且把其中最常用的部分独立出来,形成头文件和对应的   LIBRARY,C   run-time   library   就是这样形成的。  
   
  3)随后,随着   C   语言的流行,各个   C   编译器的生产商/个体/团体都遵循老的传统,在不同平台上都有相对应的   Standard   Library,但大部分实现都是与各个平台有关的。由于各个   C   编译器对   C   的支持和理解有很多分歧和微妙的差别,所以就有了   ANSI   C;ANSI   C   (主观意图上)详细的规定了   C   语言各个要素的具体含义和编译器实现要求,引进了新的函数声明方式,同时订立了   Standard   Library   的标准形式。所以C运行时库由编译器生产商提供。至于由其他厂商/个人/团体提供的头文件和库函数,应当称为第三方   C   运行库(Third   party   C   run-time   libraries)。  
   
  4)C   run-time   library里面含有初始化代码,还有错误处理代码(例如divide   by   zero处理)。你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C   run-time库,main()就不会被调用,exit()也不能被响应。因为C   run-time   library包含了C程序运行的最基本和最常用的函数。  
   
  5)到了   C++   世界里,有另外一个概念:Standard   C++   Library,它包括了上面所说的   C   run-time   library   和   STL。包含   C   run-time   library   的原因很明显,C++   是   C   的超集,没有理由再重新来一个   C++   run-time   library.   VC针对C++   加入的Standard   C++   Library主要包括:LIBCP.LIB,   LIBCPMT.LIB和   MSVCPRT.LIB  
   
  6)Windows环境下,VC提供的   C   run-time   library又分为动态运行时库和静态运行时库。  
  动态运行时库主要是DLL库文件msvcrt.dll(or   MSVCRTD.DLL   for   debug   build),对应的Import   library文件是MSVCRT.LIB(MSVCRTD.LIB   for   debug   build)  
  静态运行时库(release版)对应的主要文件是:  
  LIBC.LIB   (Single   thread   static   library,   retail   version)  
  LIBCMT.LIB   (Multithread   static   library,   retail   version)  
   
  msvcrt.dll提供几千个C函数,即使是像printf这么低级的函数都在msvcrt.dll里。其实你的程序运行时,很大一部分时间时在这些运行库里运行。在你的程序(release版)被编译时,VC会根据你的编译选项(单线程、多线程或DLL)自动将相应的运行时库文件(libc.lib,libcmt.lib或Import   library   msvcrt.lib)链接进来。  
   
  编译时到底哪个C   run-time   library联入你的程序取决于编译选项:  
  /MD,   /ML,   /MT,   /LD       (Use   Run-Time   Library)  
  你可以VC中通过以下方法设置选择哪个C   run-time   library联入你的程序:  
  To   find   these   options   in   the   development   environment,   click   Settings   on   the   Project   menu.   Then   click   the   C/C++   tab,   and   click   Code   Generation   in   the   Category   box.   See   the   Use   Run-Time   Library   drop-down   box.  
   
  从程序可移植性考虑,如果两函数都可完成一种功能,选运行时库函数好,因为各个   C   编译器的生产商对标准C   Run-time   library提供了统一的支持.

<think>好的,用户想了解C++中scanf和printf的用法,并请求示例。我需要先回顾这两个函数的基本用法,然后考虑如何清晰地呈现给用户。 首先,scanf和printf是C标准库中的函数,用于输入输出。C++中虽然常用cin和cout,但在需要高性能或格式化控制时,这两个函数仍然有用。需要提醒用户注意类型安全,尤其是在C++中使用时可能出现的问题,比如格式说明符与变量类型不匹配会导致未定义行为。 接下来,我需要给出基础示例。比如printf的常见用法,如整数、浮点数、字符串的输出。注意说明格式字符串中的占位符,如%d对应int,%f对应double,%s对应字符串。对于scanf,需要强调变量前要加取地址符&,除了字符数组的情况。还要提到缓冲区溢出的风险,比如使用%s没有指定长度可能导致问题,建议使用%Ns(如%99s)来限制输入长度。 然后,考虑高级用法。比如printf中的宽度、精度控制,左对齐,填充字符等。例如%-10d表示左对齐,宽度10;%05d用0填充。对于scanf,可以介绍扫描集的使用,比如%[^\n]读取直到换行符,或者%*d跳过某些输入。 需要特别注意C++中的类型适配问题。比如,printf中打印long long要用%lld,size_t用%zu。而scanf读取double时要用%lf,但在printf中%f和%lf都可以,因为float会被提升为double。这些细节容易出错,必须明确指出。 然后,对比C++的流式输入输出,比如cout和cin的类型安全优势,但性能可能较低。在需要高性能的场景,如大量数据输入输出时,使用scanf/printf更合适,但要注意正确性。 最后,生成相关问题,可能涉及安全性、性能比较、替代方案等。比如如何避免缓冲区溢出,C++中有没有更安全的替代函数,或者如何提高输入输出效率。 在回答过程中,还要引用提供的引用资料。虽然引用1是关于printf的替代库,但可以提到类型安全的问题,指出原版printf的类型不安全,从而引出使用时的注意事项。 需要检查LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能用不上数学公式。不过如果有涉及变量或表达式的地方,比如提到宽度为5,可以用$5$来表示。 总结结构:先介绍基本用法,给出示例代码,再讲高级用法,注意事项,对比C++的流操作,最后生成相关问题。确保每个代码示例正确,语法无误,并标注必要的注释。</think>以下是C++中`scanf`和printf的用法详解及示例: --- ### 一、基础用法 #### 1. printf 格式化输出 ```cpp int num = 42; double pi = 3.1415926; char str[] = "Hello"; printf("整数: %d\n", num); // 输出整数 printf("浮点数: %.2f\n", pi); // 保留两位小数 printf("字符串: %s\n", str); // 输出字符串 printf("十六进制: %x\n", num); // 输出十六进制 ``` - `%d`:整数,`%f`:浮点数,`%s`:字符串,`%x`:十六进制 - 格式控制符需与变量类型严格匹配[^1] #### 2. scanf 格式化输入 ```cpp int age; double salary; char name[100]; scanf("%d", &age); // 输入整数 scanf("%lf", &salary); // 输入双精度浮点数 scanf("%99s", name); // 输入字符串(防缓冲区溢出) ``` - 变量前必须加`&`(字符数组除外) - `%99s`限制输入长度防止溢出 --- ### 二、高级用法 #### 1. printf 控制格式 ```cpp printf("左对齐: %-10d\n", num); // 左对齐宽度10 printf("前导零: %05d\n", num); // 输出00042 printf("科学计数法: %e\n", pi); // 输出3.141593e+00 ``` #### 2. scanf 高级匹配 ```cpp int y, m, d; scanf("%d-%d-%d", &y, &m, &d); // 输入"2023-08-18" scanf("%*[^0-9]%d", &num); // 跳过非数字字符后读取整数 ``` --- ### 三、注意事项 1. **类型安全** - `long long`类型需用`%lld` - `size_t`需用`%zu` - `double`在scanf中用`%lf`,printf中`%f/%lf`通用 2. **性能对比** - `scanf/printf`比`cin/cout`快约2-3倍(适合大数据量场景) 3. **替代方案** - C++20的`<format>`库提供类型安全格式化: ```cpp std::cout << std::format("{} {}", num, str); // 类型自动推导 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值