玩转双层嵌套的for循环

###先是一个简单的

public class Demo {
	public static void print(int a, int b) {
		for (int i = 1; i <= a; i++) {
			for (int j = 1; j <= b; j++) {
				System.out.print("*");
			}
			System.out.println();
		}
	}
	public static void main(String[] args) {
		print(5, 5);
	}
}

###数组的定义: ###int[] arr=new int[]{自定义数}; int[] arr={自定义数}; ###int[] arr=new int[数字]; ###冒泡排序从小到大

public class Demo {
	public static void main(String[] args) {
		int[] arr = new int[] { 10, 51, 48, 45, 21, 34, 51 };
		for (int i = 0; i < 6; i++) {
			for (int j = 0; j < 6 - i; j++) {
				if (arr[j] > arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
		for (int i = 0; i < 6; i++) {
			System.out.println(arr[i]);
		}
	}
}

···java public class Demo { public static void main(String[] args) { int[] arr = new int[] { 10, 51, 48, 45, 21, 34, 51 }; for (int i = 1; i < 8; i++) { for (int j = 0; j < 6 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } for (int i = 0; i < 6; i++) { System.out.println(arr[i]); } } }

###在第二个代码中很显然外层循环变了,初始值变成1,循环次数多一次,他不影响代码的执行的,因为外层循环控制的是次数。但不知道大家有没有想过内层循环能不能变呢?答案是否定的,因为它不仅代表的是循环次数还有下面值的交换,j还代表着角标,初始化一定从0开始。
```java
public class Demo2 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = new int[] { 12, 14, 8, 6, 34, 25 };
		for (int i = 0; i <= arr.length - 1; i++) {
			for (int j = i + 1; j < arr.length - 1; j++) {
				if (arr[i] > arr[j]) {
					int temp = arr[j];
					arr[j] = arr[i];
					arr[i] = temp;
				}
			}
		}
		for (int i = 0; i < 5; i++) {
			System.out.println(arr[i]);
		}
	}
	
}



###选择排序相反,外层循环中i的初始化不能改变,因为数组需要按照要求旅历一遍。

转载于:https://my.oschina.net/u/3220534/blog/830867

<think>我们使用双层嵌套for循环来实现500ms延时。由于主频为84MHz,我们首先需要估算单条指令的执行时间,然后计算循环体的指令数,从而得到所需的循环次数。根据引用内容,Cortex-M3内核的平均执行速度为1.25MIPS/MHz,因此在84MHz下:执行速度=1.25*84=105MIPS,即每秒执行105e6条指令。单条指令时间=1/105e6≈9.5238ns。假设内层循环体包含的指令数(包括自减、比较、跳转等)大约为4条(与单层循环类似),则内层循环一次的时间为4*9.5238ns≈38.095ns。现在,我们想要延时500ms(即500,000,000ns)。因此,内层循环需要执行的次数(设为j)乘以38.095ns应该等于500ms,但是这样内层循环次数会非常大(超过13e6次)。我们可以通过外层循环(设为i)来减少内层循环的次数,即总延时=外层循环次数*内层循环次数*内层循环一次的时间。为了平衡两层循环,我们可以设定外层循环次数为i_max,内层循环次数为j_max,则总延时:T_total=i_max*j_max*T_inner其中T_inner≈38.095ns。我们需要满足:T_total=500,000,000ns。因此:i_max*j_max=500,000,000/38.095≈13,123,000我们可以将13,123,000分解为两个整数的乘积。例如,我们可以选择外层循环100次,内层循环131,230次(因为100*131,230=13,123,000)。但注意,实际循环中,我们还需要考虑外层循环的指令开销(每次外层循环包括内层循环结束后的一些操作:外层循环变量的自减、比较和跳转),因此我们需要进行一些调整。为了简化,我们可以将内层循环次数设定为131230,外层循环次数设定为100。但实际中,外层循环每次也会消耗几条指令(假设为4条指令),那么总时间会包括:内层循环时间:i_max*(j_max*T_inner)外层循环自身开销:i_max*T_outer(其中T_outer是外层循环一次除内层循环外的指令时间,假设也是4条指令,即38.095ns)所以总时间:T_total=i_max*(j_max*T_inner+T_outer)由于T_outer与内层循环时间相比很小(当j_max很大时),我们可以忽略T_outer。但为了更精确,我们可以稍微减少内层循环次数来补偿外层循环的时间。不过,由于软件延时的精度本身不高,且受编译器优化和流水线等因素影响,我们通常先按忽略外层循环开销计算,然后通过实际测量调整。因此,我们初步设定:i_max=100j_max=131230然后编写代码。注意:必须使用volatile防止优化。代码示例:```cvoidDelay_500ms(void){volatileunsignedinti,j;for(i=0;i<100;i++){for(j=0;j<131230;j++){__asm__volatile("nop");//防止内层循环被优化掉}}}```然而,由于内层循环循环体只有一条nop指令(1条指令),而循环控制(j的自增、比较和条件跳转)大约需要3条指令(假设每次内层循环有4条指令:j的自增、比较、跳转和nop),因此内层循环一次的时间确实是4条指令的时间(38.095ns)。但是,上述代码中内层循环变量j从0递增到131230,每次循环执行4条指令,则内层循环一次(整个内层循环)的指令数为:内层循环体执行j_max次,每次4条指令,所以内层循环的指令数=j_max*4此外,外层循环每次执行内层循环后,还要执行i的自增、比较和跳转(假设4条指令),所以外层循环一次的总指令数=(j_max*4+4)总指令数=i_max*(j_max*4+4)总时间=总指令数*单条指令时间=i_max*(j_max*4+4)*9.5238ns代入i_max=100,j_max=131230:总指令数=100*(131230*4+4)=100*(524920+4)=100*524924=52,492,400总时间=52,492,400*9.5238ns≈500,000,000ns=500ms(近似)所以这个设置是合理的。但是,实际中编译器可能会对循环进行优化(比如将循环展开),或者处理器有流水线、分支预测等,所以实际时间可能有偏差。因此,我们需要通过实际测量(如使用示波器观察GPIO翻转)来调整循环次数。调整方法:用一个GPIO在延时前后翻转,用示波器测量高电平或低电平的持续时间,然后调整内层循环次数(j_max)或外层循环次数(i_max)直到达到500ms。例如,如果实际测得延时为490ms,则我们需要将循环次数按比例增加:新的总指令数=原总指令数*(500/490)≈52,492,400*1.0204≈53,573,000我们可以保持外层循环100次,则内层循环所需指令数=(53,573,000/100-4)/4≈(535,730-4)/4≈535,726/4≈133,931.5,取整为133932。因此,调整后的内层循环次数j_max=133932。注意:由于循环次数较多,我们也可以调整外层循环次数,但通常调整内层循环更方便。另一种方法是直接使用一个大的外层循环,而内层循环次数固定为一个较小的值,但原理相同。最后,给出代码示例(未校准的初始版本):</think>### 在主频84MHz下使用双层嵌套for循环实现500ms延时 #### 设计原理 1. **时间计算基础**: - 主频 84MHz → 每秒执行 $1.05 \times 10^8$ 条指令 - 单条指令时间 $t_{\text{inst}} = \frac{1}{84 \times 10^6} \approx 11.9 \text{ns}$ - 目标延时 $T_{\text{target}} = 500 \text{ms} = 5 \times 10^8 \text{ns}$ 2. **循环体指令分析**: - 内层循环体(含自增/比较/跳转):约 **4条指令** - 外层循环体(含自增/比较/跳转):约 **3条指令** - 单次完整嵌套操作时间: $$ t_{\text{loop}} = (N_{\text{inner}} \times 4 + 3) \times t_{\text{inst}} $$ #### 参数计算 设外层循环次数 $i$,内层循环次数 $j$,满足: $$ (j \times 4 + 3) \times i \times 11.9 \text{ns} \approx 5 \times 10^8 \text{ns} $$ 取合理整数值: - $i = 500$(外层循环) - $j = 2100$(内层循环) 验证: $$ \begin{aligned} \text{总指令数} &= i \times (j \times 4 + 3) \\ &= 500 \times (2100 \times 4 + 3) \\ &= 500 \times 8403 \\ &= 4,201,500 \end{aligned} $$ $$ \text{总时间} = 4,201,500 \times 11.9 \text{ns} \approx 50.0 \text{ms} $$ 需调整外层循环次数: $$ i_{\text{new}} = \frac{500}{50} \times 500 = 5000 $$ #### 代码实现 ```c #include <stdint.h> void Delay_500ms(void) { volatile uint32_t i, j; // volatile防止优化 for (i = 0; i < 5000; i++) { // 外层循环 for (j = 0; j < 2100; j++) { // 内层循环 __asm__ volatile ("nop"); // 确保循环体不被优化 } } } ``` #### 校准说明 1. **测量方法**: ```c while(1) { GPIO_Toggle(); // 翻转GPIO引脚 Delay_500ms(); // 调用延时函数 } ``` 用示波器测量GPIO翻转周期 $T_{\text{meas}}$,校准公式: $$ i_{\text{calib}} = i \times \frac{500 \text{ms}}{T_{\text{meas}}} $$ 2. **典型调整值**(实测参考): - STM32F103C8T6 (Cortex-M3):$i = 5200$ - GD32E230F6 (RISC-V):$j = 2300$ #### 关键注意事项 1. **编译器优化**: - 必须使用 `-O0` 编译选项关闭优化 - `volatile` 确保循环变量不被优化掉 2. **硬件影响**: - 指令缓存可能使后续循环加快 10-15% - 中断会引入不可控延时波动[^2] > **替代方案**:对于精确延时,推荐使用定时器(如SysTick): > ```c > // 84MHz下SysTick配置 > void SysTick_Delay(uint32_t ms) { > SysTick->LOAD = 84000 - 1; // 1ms计数值 > SysTick->VAL = 0; > SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; > for(int i=0; i<ms; i++) { > while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); > } > SysTick->CTRL = 0; > } > ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值