for循环的两种格式 In C++

本文深入探讨了C++中for循环的使用,包括其语法、如何执行特定次数的循环、空条件下的无限循环,以及范围基for循环的使用,特别介绍了如何遍历字符串和容器中的元素。
  • for loop in C++

A for loop is a repetition control structure that allows you to efficently write a loop that needs to execute a specific number of times.

  • Syntax

  1. normal
    for (init; condition; increment){
        statement(s);
    }
    

    which can also expressed :

    for (initialization; condition; increase) statement;
    

    The three fileds in a for-loop are optional. They can be left empty, but in all cases the semicolon signs between them are required.

    A loop with no condition is equivalent to a loop with true as condition (i.e., an infinite loop).

    It is valid that more than a single expression as any of initialization, condition, or statement.

  2. Range-based for loop

    The for-loop has another syntax, which is used exclusively with ranges:

    for (declaration : range) statement;
    

    This kind of for loop iterates over all the elements in range, where declaration declares some variable able to take the value of an element in this range. Ranges are sequences of elements, including arrays, containers, and any other type supporting the functions begin and end.

    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
        string str {"hello"};
        for (char c : str)
        {
            cout << c;
        }
        cout << '\n';
    }
    

    What precedes the colon (:) in the for loop is the declaration of a char variable (the elements in a string are of type char). We then use this variable, c, in the statement block to represent the value of each of the elements in the range.

    This loop is automatic and does not require the explicit declaration of any counter variable.

    Range based loops usually also make use of type deduction for the type of the elements with auto. Typically, the range-bases loop above can also be written as :

    for (auto c : str) 
        cout << c;
    

    Here, the type of c is automatically deduced as the type of the elemets in str.

  • References

  1. cpluplus
<think>我们讨论的是在C++中连续调用两次相同函数和使用for循环调用两次的效率比较。这个问题涉及到编译器优化、代码可读性以及性能权衡。首先,我们需要明确两种写法的区别:写法1:连续调用两次函数func();func();写法2:使用for循环调用两次for(inti=0;i<2;i++){func();}在效率上,现代编译器在开启优化的情况下,对于循环次数固定的循环(比如2次),很可能会将循环展开(loopunrolling),因此两种写法可能会生成相同的机器码,效率相同。但是,如果没有开启优化,或者循环次数较大(比如1000次)且没有展开,那么循环会有额外的开销(循环变量的增加和条件判断)。因此,对于只有两次调用,我们通常可以认为两种写法在优化后的效率是相同的。然而,我们还需要考虑可读性。如果调用两次完全相同的函数,连续写两次更清晰。如果调用的次数较多,或者需要根据条件来调用多次,则循环更合适。下面我们用一段代码来测试两种方式的效率,并查看汇编代码验证。我们测试两种场景:1.不开启优化2.开启优化(-O2)函数示例:一个简单的函数,执行一些操作,比如累加。注意:由于现代CPU的流水线、分支预测等,即使有循环开销,在循环次数较多时,每次循环的开销也会被分摊,所以对于循环次数较大的情况,循环开销可以忽略。但问题中我们只比较两次调用,所以循环开销相对比较明显(尽管绝对量很小)。我们通过实际测试代码来比较:测试代码:```cpp#include<iostream>#include<chrono>voidfunc(){//模拟一些计算volatileintx=0;//使用volatile避免被优化掉for(inti=0;i<1000;i++){x+=i;}}voidtestTwoCalls(){autostart=std::chrono::high_resolution_clock::now();func();func();autoend=std::chrono::high_resolution_clock::now();std::chrono::duration<double>diff=end-start;std::cout<< "Twocalls:"<< diff.count()<< "seconds\n";}voidtestLoop(){autostart=std::chrono::high_resolution_clock::now();for(inti=0;i<2;i++){func();}autoend=std::chrono::high_resolution_clock::now();std::chrono::duration<double>diff=end-start;std::cout<< "Loop:"<< diff.count()<< "seconds\n";}intmain(){testTwoCalls();testLoop();return0;}```注意:由于我们使用volatile,函数func不会被优化掉,并且会执行一些操作。在编译时不开启优化,我们可能会看到testLoop比testTwoCalls稍慢,因为循环有额外的开销(初始化循环变量i,每次循环后i的自增和条件判断)。开启优化后(如-O2),编译器会进行循环展开,将for循环展开成两个func调用,这样两种方式的汇编代码相同,效率也就一样。下面我们查看两种写法在开启优化后的汇编代码(以x86-64gcc为例):连续调用两次的汇编代码(伪代码):callfunccallfunc循环的汇编代码(开启优化后):callfunccallfunc因此,在开启优化的情况下,两者没有区别。结论:-如果性能极其敏感且要求没有循环开销(例如在极端嵌入式系统中),并且循环次数非常小(比如2次),建议直接写两次调用。-否则,使用循环可以提高代码的可读性(特别是次数可变或较多时),并且编译器优化后效率相同。在实际项目中,我们通常更注重代码的可读性和可维护性,所以如果次数较少(2-3次)且不会变化,可以连续写;如果次数可能会变化,或者次数较多,使用循环更好。值得注意的是,如果循环次数是编译期常量,编译器通常会对小循环进行展开,所以不必担心性能损失。另外,在C++11中,我们可以使用范围循环或算法来增加可读性,但对于只有两次的简单调用,用循环反而可能使代码更复杂。所以,具体问题具体分析。最后,我们提出一些相关问题:</think> ### C++函数调用与循环效率对比分析 在 C++中,连续两次调用同一函数和使用 `for` 循环调用两次的效率对比需要考虑多种因素,包括编译器优化、硬件架构和调用上下文。 #### 性能对比测试代码 ```cpp #include <iostream> #include <chrono> // 测试函数 void testFunc(int& value) { value = value * 2 + 1; // 简单计算避免被优化 } // 方案1:两次直接调用 void doubleCall(int& value) { testFunc(value); testFunc(value); } // 方案2:循环调用两次 void loopCall(int& value) { for (int i = 0; i < 2; i++) { testFunc(value); } } // 性能测试 void benchmark() { const int iterations = 10000000; // 测试两次直接调用 { int val = 0; auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; i++) { doubleCall(val); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "两次调用耗时: " << duration.count() << " μs (结果为: " << val << ")" << std::endl; } // 测试循环调用 { int val = 0; auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; i++) { loopCall(val); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "循环调用耗时: " << duration.count() << " μs (结果为: " << val << ")" << std::endl; } } int main() { benchmark(); return 0; } ``` ### 效率分析及优化建议 1. **编译器优化差异** - 现代编译器(GCC/Clang/MSVC)会在 `-O2` 及以上优化级别将固定次数的循环展开(loop unrolling) - 在优化后,两者生成的汇编代码基本一致 - 未优化状态下,循环调用会有额外开销: ```asm ; 两次调用 call testFunc call testFunc ; 循环调用 (未优化) mov ecx, 0 loop_start: call testFunc inc ecx cmp ecx, 2 jl loop_start ``` 2. **实际性能影响因素** ```plaintext | 优化级别 | 两次调用 | 循环调用 | 差距原因 | |----------|---------|---------|---------| | O0(无优化)| 更快(0循环开销) | 稍慢(循环控制开销)| 循环变量增减+条件判断 | | O1及以上 | 基本持平 | 基本持平 | 编译器自动展开循环 | ``` 3. **分支预测影响** - 在大型循环中,循环结构会影响分支预测 - 固定次数的小循环可完美预测,无分支错误预测惩罚 4. **代码可读性对比** - **两次调用**:意图更明显,适合简单操作 - **循环调用**:更适合可扩展的场景 - 权衡建议: - 当调用次数固定且小于3次时,优先选择两次调用 - 当调用次数可能变化或需要扩展时,使用循环 ### 性能优化建议 1. **强制内联函数** ```cpp __attribute__((always_inline)) // GCC/Clang __forceinline // MSVC inline void testFunc(int& value) { value = value * 2 + 1; } ``` 2. **模板元编程(适用于编译期确定次数)** ```cpp template<size_t COUNT> void unrolledCall(int& value) { if constexpr (COUNT > 0) { testFunc(value); unrolledCall<COUNT-1>(value); } } // 调用 unrolledCall<2>(val); ``` 3. **使用宏实现重复调用** ```cpp #define CALL_TWICE(func, arg) do { func(arg); func(arg); } while(0) ``` ### 性能关键场景建议 1. **实时系统/超低延迟场景** - 优先使用两次直接调用 - 添加内联指令确保无函数调用开销 - 禁用所有循环结构避免不可预测的延迟 2. **通用应用场景** - 保持循环结构提升代码可读性 - 依赖编译器的循环展开优化 - 开启编译器优化选项(至少 `-O2` / `/O2`) 3. **模板元递归** - 在需要编译期计算时使用 - 完全消除运行时开销 - 代码复杂性增加,调试困难 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值