Accessing static Data and Functions in Legacy C
http://www.renaissancesoftware.net/blog/archives/450 It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped f
http://www.renaissancesoftware.net/blog/archives/450
It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped from running in China. Somehow calling them glitches seems to make it someone else’s fault, something out of their control. How long have leap years been around? Julius Caesar introduced Leap Years in the Roman empire over 2000 years ago. The Gregorian calendar has been around since 1682. This is not a new idea, or a new bug.
I’m going to try to take one excuse away from the programmers that create these bugs by answering a question that comes up all the time, “How do I test static functions in my C code?”
In code developed using TDD, static functions are tested indirectly through the public interface. As I mentioned in a this article TDD is a code rot radar. It helps you see design problems. Needing direct access to hidden functions and data in C is a sign of code rot. It is time to refactor.
But what about existing legacy code that has statics? It is probably way past the time for idealism and time for some pragmatism. In this article and the next, we’ll look at how to get your code
untouched into the test harness and access those pesky <span>static </span>
variables
and functions.
If you don’t mind touching your code, you could change all mentions of static
to STATIC
.
Then using the preprocessor, STATIC
can
he set to static
during
production and to nothing for test, making the names globally accessible. In gcc you would use these command line options
-
For production builds use
-dSTATIC=static
-
For unit test builds use
-dSTATIC
Let’s look at two options that, at least for access to statics, you will not have to touch your code to get it under test. First is <span>#include</span>
-ing
your .c in the test file. In the next article we’ll build a test adapter .c that give access to the hidden parts.
We are going to revisit code that is similar to the original code responsible for the Zune Bug. I rewrote the code to avoid attracting any lawyers but it is structured similarly to the original Zune driver, complete with static data and functions that must be correct for the function to work.
The header file provides a data structure and initialization function for figuring out the information about the date. Here is the header:
typedef struct Date { int daysSince1980; int year; int dayOfYear; int month; int dayOfMonth; int dayOfWeek; } Date; void Date_Init(Date *, int daysSince1980); enum { SUN = 0, MON, TUE, WED, THU, FRI, SAT }; enum { JAN = 0, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
Date_Init
populates
the Date
instance
passed in. Ignoring the fact that I can probably fully test this by rigging the daysSince1980
,
and inspecting the contents of the resultingDate
structure,
we are going to see how we can directly test the hidden functions and data.
Date_Init
has
three hidden helper functions.
void Date_Init(Date* date, int daysSince1980) { date->daysSince1980 = daysSince1980; FirstSetYearAndDayOfYear(date); ThenSetMonthAndDayOfMonth(date); FinallySetDayOfWeek(date); }
Date_Init
is
the tip of the iceberg. All the interesting stuff is happening in the hidden data and functions:
#include "Date.h" #includeenum { STARTING_YEAR = 1980, STARTING_WEEKDAY = TUE }; static const int nonLeapYearDaysPerMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const int leapYearDaysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static bool IsLeapYear(int year) { if (year % 400 == 0) return true; if (year % 100 == 0) return false; if (year % 4 == 0) return true; return false; } static int GetDaysInYear(int year) { if (IsLeapYear(year)) return 366; else return 365; } static void FirstSetYearAndDayOfYear(Date * date) { int days = date->daysSince1980; int year = STARTING_YEAR; int daysInYear = GetDaysInYear(year); while (days > daysInYear) { year++; days -= daysInYear; daysInYear = GetDaysInYear(year); } date->dayOfYear = days; date->year = year; } static void ThenSetMonthAndDayOfMonth(Date * date) { int month = 0; int days = date->dayOfYear; const int * daysPerMonth = nonLeapYearDaysPerMonth; if (IsLeapYear(date->year)) daysPerMonth = leapYearDaysPerMonth; while (days > daysPerMonth[month]) { days -= daysPerMonth[month]; month++; } date->month = month + 1; date->dayOfMonth = days; } static void FinallySetDayOfWeek(Date * date) { date->dayOfWeek =((date->daysSince1980-1)+STARTING_WEEKDAY)%7; } void Date_Init(Date* date, int daysSince1980) { date->daysSince1980 = daysSince1980; FirstSetYearAndDayOfYear(date); ThenSetMonthAndDayOfMonth(date); FinallySetDayOfWeek(date); }
Let’s say you want to check the days per month vectors. You might want to write a test to check the months against the handy poem we use here in the US: Thirty days, has September, April, June and November; all the rest have thirty-one, except for February. It has 28 except in leap year it has 29.
If you started by writing this test…
TEST(Date, sept_has_30_days) { LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]); }
… you get this error:
DateTest.cpp:41: error: 'nonLeapYearDaysPerMonth' was not declared in this scope
Let’s get access to the hidden statics in the test case by including Date.c
instead
ofDate.h
in DateTest.cpp
.
The full test case file looks like this now:
#include "CppUTest/TestHarness.h" extern "C" { #include "Date.c" } TEST_GROUP(Date) { }; TEST(Date, sept_has_30_days) { LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]); }
With a little refactoring days per month vectors can be checked like this:
#include "CppUTest/TestHarness.h" extern "C" { #include "Date.c" } TEST_GROUP(Date) { const int * daysPerYearVector; void setup() { daysPerYearVector = nonLeapYearDaysPerMonth; } void itsLeapYear() { daysPerYearVector = leapYearDaysPerMonth; } void CheckNumberOfDaysPerMonth(int month, int days) { LONGS_EQUAL(days, daysPerYearVector[month]); } void ThirtyDaysHasSeptEtc() { CheckNumberOfDaysPerMonth(SEP, 30); CheckNumberOfDaysPerMonth(APR, 30); CheckNumberOfDaysPerMonth(JUN, 30); CheckNumberOfDaysPerMonth(NOV, 30); CheckNumberOfDaysPerMonth(OCT, 31); CheckNumberOfDaysPerMonth(DEC, 31); CheckNumberOfDaysPerMonth(JAN, 31); CheckNumberOfDaysPerMonth(MAR, 31); CheckNumberOfDaysPerMonth(MAY, 31); CheckNumberOfDaysPerMonth(JUL, 31); CheckNumberOfDaysPerMonth(AUG, 31); } void ExceptFebruaryHas(int days) { CheckNumberOfDaysPerMonth(FEB, days); } }; TEST(Date, non_leap_year_day_per_month_table) { ThirtyDaysHasSeptEtc(); ExceptFebruaryHas(28); } TEST(Date, leap_year_day_per_month_table) { itsLeapYear(); ThirtyDaysHasSeptEtc(); ExceptFebruaryHas(28); }
You have access to all the hidden stuff, so you can write the test for the static functions:
IsLeapYear()
, GetDaysInYear()
, FirstSetYearAndDayOfYear()
,ThenSetMonthAndDayOfMonth()
,
and FinallySetDayOfWeek()
.
If Date
been
an abstract data type, with its data structure hidden in the .c
file,
the tests would all have access to structure members hidden from the rest of the world.
There is a downside to this approach, which probably does not matter in this case, but could in others. You can only have one test file that can include a given .c
file.
In the next article we’ll solve that problem.
Have you heard of any interesting leap year bugs? Did you prevent your own?

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

c语言static的作用和用法:1、变量作用域;2、生命周期;3、函数内部;4、修饰全局变量;5、修饰函数;6、其他用途;详细介绍:1、变量作用域,当一个变量前有static关键字,那么这个变量的作用域被限制在声明它的文件内,也就是说,这个变量是“文件级作用域”,这对于防止变量的“重复定义”问题很有用;2、生命周期,静态变量在程序开始执行时初始化一次,并在程序结束时销毁等等。

data文件夹里面是系统及程序的数据,比如软件的设置和安装包等,Data文件夹中各个文件夹则代表的是不同类型的数据存放文件夹,无论Data文件指的是文件名Data还是扩展名data,都是系统或程序自定义的数据文件,Data是数据保存的备份类文件,一般可以用meidaplayer、记事本或word打开。

C语言中static关键字的实际应用场景及使用技巧一、概述static是C语言中的一个关键字,用于修饰变量和函数。它的作用是改变其在程序运行过程中的生命周期和可见性,使得变量和函数具有静态的特性。本文将介绍static关键字的实际应用场景及使用技巧,并通过具体的代码示例进行说明。二、静态变量延长变量的生命周期使用static关键字修饰局部变量可以将其生命周期

一、static 请先看下面这段程序:publicclassHello{publicstaticvoidmain(String[]args){//(1)System.out.println("Hello,world!");//(2)}}看过这段程序,对于大多数学过Java的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主

mysql load data乱码的解决办法:1、找到出现乱码的SQL语句;2、修改语句为“LOAD DATA LOCAL INFILE "employee.txt" INTO TABLE EMPLOYEE character set utf8;”即可。

修饰符abstract(抽象的)一、abstract可以修饰类(1)被abstract修饰的类称为抽象类(2)语法:abstractclass类名{}(3)特点:抽象类不能单独创建对象,但是可以声明引用抽象类类名引用名;(4)抽象类可以定义成员变量和成员方法(5)抽象类有构造方法,用于创建子类对象时,jvm默认创建一个父类对象;抽象的构造方法应用在jvm创建父类对象时应用。二、abstract可以修饰方法(1)被asbtract修饰的方法被称为抽象方法(2)语法:访问修饰符abstract返回值

区别有:1、xdata通常指的是自变量,data则是指整个数据集;2、xdata主要用于建立数据分析模型,data则是用于进行数据分析和统计;3、xdata通常用于回归分析、方差分析、预测建模,data则可以使用各种统计方法进行分析;4、xdata通常需要进行数据预处理,data则可以包含完整的原始数据。

static的作用:1、变量;2、方法;3、类;4、其他用途;5、多线程环境;6、性能优化;7、单例模式;8、常量;9、局部变量;10、内存布局优化;11、避免重复初始化;12、在函数中使用。详细介绍:1、变量,静态变量,当一个变量被声明为static时,它属于类级别,而不是实例级别,这意味着无论创建多少个对象,都只有一个静态变量存在,所有对象都共享这个静态变量等等。
