在倒数计数循环中使用size_t时的无限循环

时间:2016-12-10 13:06:59

标签: c loops infinite-loop size-t

所以我在任何索引for循环中使用size_t而不是int来防止负索引。但倒数时,这会导致溢出:

for (size_t i = 10; i >= 0; --i) {
    // Do something, f.ex. array[i] = i
}

防止这种情况的好方法是什么?

  • 改为使用int
  • 使用i == 0作为终止条件? (如果我需要0)
  • ,这将无效

我很感激任何反馈!

7 个答案:

答案 0 :(得分:5)

for (size_t i = 10; i <= 10; --i) // do something

当溢出发生时,它将舍入到最大整数,因此条件将失败。

答案 1 :(得分:4)

for (size_t i = 11; i-- > 0; ) {
    // Do something, f.ex. array[i] = i
}

答案 2 :(得分:4)

从技术上讲,它不是溢出因为size_t是无符号类型,但它绝对是一个无限循环,因为终止条件总是为真。

0处递减时,无符号整数环绕。请注意,在环绕发生之前,您的循环将运行11次,而不是10

您必须在减少索引之前检查条件。使用比最大有效索引多一个的初始值启动枚举可以提高视觉一致性并简化测试。

以下是更正后的版本,您可以看到i的初始值是数组元素的数量:

int array[11];
for (size_t i = 11; i-- > 0; ) {
    // Do something, f.ex. array[i] = i
}

答案 3 :(得分:2)

虽然不是每个人的品味方式,但惯用的是使用幻灯片操作符:

for (size_t i = 10 + 1; i--> 0; )

这不是真正的运营商,而是多年来人们所熟知的。

答案 4 :(得分:1)

最简单的方法是增加上限值。例如

using System;
using System.Data;
using System.Data.SqlClient;

namespace SqlDependencyExample
{
    class Program
    {

        static string connectionString = @"Data Source=.;Initial Catalog=YourDatabase;Application Name=SqlDependencyExample;Integrated Security=SSPI";

        static void Main(string[] args)
        {

            SqlDependency.Start(connectionString);

            getDataWithSqlDependency();

            Console.WriteLine("Waiting for data changes");
            Console.WriteLine("Press enter to quit");
            Console.ReadLine();

            SqlDependency.Stop(connectionString);

        }

        static DataTable getDataWithSqlDependency()
        {

            using (var connection = new SqlConnection(connectionString))
            using (var cmd = new SqlCommand("SELECT Col1, Col2, Col3 FROM dbo.MyTable;", connection))
            {

                var dt = new DataTable();

                // Create dependency for this command and add event handler
                var dependency = new SqlDependency(cmd);
                dependency.OnChange += new OnChangeEventHandler(onDependencyChange);

                // execute command to get data
                connection.Open();
                dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));

                return dt;

            }

        }

        // Handler method
        static void onDependencyChange(object sender,
           SqlNotificationEventArgs e)
        {

            Console.WriteLine($"OnChange Event fired. SqlNotificationEventArgs: Info={e.Info}, Source={e.Source}, Type={e.Type}.");

            if ((e.Info != SqlNotificationInfo.Invalid)
                && (e.Type != SqlNotificationType.Subscribe))
            {
                //resubscribe
                var dt = getDataWithSqlDependency();

                Console.WriteLine($"Data changed. {dt.Rows.Count} rows returned.");
            }
            else
            {
                Console.WriteLine("SqlDependency not restarted");
            }

        }


    }
}

const size_t N = 10;

for (size_t i = N + 1; i != 0; --i) {
    // Do something, f.ex. array[i-1] = i-1
}

一般情况下,当i可以等于存储在const size_t N = 10; for (size_t i = N + 1; i-- != 0; ) { // Do something, f.ex. array[i] = i } 类型的对象中的最大值时,您可以使用以下技巧

size_t

否则你可以使用do-while循环。在这种情况下更合适。例如

#include <stdio.h>

int main( void )
{
    const size_t N = 10;

    for (size_t i = N, j = N; !( i == 0 && j == -1 ); j--)
    {
        i = j;
        printf( "%zu ", i );
    }

    printf( "\n" );
}

答案 5 :(得分:1)

size_t i = 10; i >= 0;永远不会为假,因为size_t是一些无符号类型且所有值都大于或等于零。

  

... size_t,它是sizeof运算符结果的无符号整数类型; ...
  C11§7.192

启用了警告的好的编译器会对此发出警告。
希望这个无限循环永远不会发生,因为对警告的调查首先会纠正这个问题。

最佳选择取决于编码目标

良好的代码可以避免像这样裸体的魔法数字10.如果代码派生出来,那就更好了。在这个简单的例子中,应该是11。

#define A_SIZE 11
int array[A_SIZE];
...
for (size_t i = A_SIZE; i-- > 0; ) {
    // Do something, f.ex. array[i] = i
}

OTOH,代码可能在循环中有break个条件,在以后的代码中需要i来表示array[]用法

size_t i = A_SIZE;
while (i > 0) {
  if (...) break; 
  i--;
  // Do something, f.ex. array[i] = i
  if (...) break; 
}
// Do something with i

代码可能有合同要求,以便在不同的地方使用10

// Contract says loop must handle indexes 0 to N, inclusive
#define N 10
int array[N + 1];

for (size_t i = N; i + 1 > 0; i--) {
  // Do something, f.ex. array[i] = i
}

优秀的优化编译器不会在每个+1上执行i + 1 > 0,而是创建等效的高效代码。

代码是一种最能传达代码整体含义的方式。

答案 6 :(得分:0)

问题在于,在您的实施中,size_t的类型为unsigned longunsigned int

当i = 0时,条件完成, - i将其转换为4294967295UL = ULONG_MAX,因此循环中的测试条件i >= 0永远不会为假。

size_t有一些无符号类型,我永远不会消极。