Professional Documents
Culture Documents
C - Abstract Data Types and Information Hiding
C - Abstract Data Types and Information Hiding
A
bstractDataTypesandInformationHiding
1.1DataTypes
Datatypesareanintegralpartofeveryprogramminglanguage.ANSI-Chasint,
doubleandchartonamejustafew.Programmersarerarelycontentwithwhat’s
availableandaprogramminglanguagenormallyprovidesfacilitiestobuildnewdata
typesfromthosethatarepredefined.Asimpleapproachistoformaggregates
suchasarrays,structures,orunions.Pointers,accordingtoC.A.R.Hoare‘‘astep
fromwhichwemayneverrecover,’’permitustorepresentandmanipulatedataof
essentiallyunlimitedcomplexity.
Whatexactlyisadatatype?Wecantakeseveralpointsofview.Adatatype
isasetofvalues—chartypicallyhas256distinctvalues,inthasmanymore;both
areevenlyspacedandbehavemoreorlesslikethenaturalnumbersorintegersof
mathematics.doubleonceagainhasmanymorevalues,buttheycertainlydonot
behavelikemathematics’realnumbers.
Alternatively,wecandefineadatatypeasasetofvaluesplusoperationsto
workwiththem.Typically,thevaluesarewhatacomputercanrepresent,andthe
operationsmoreorlessreflecttheavailablehardwareinstructions.intinANSI-C
doesnotdotoowellinthisrespect:thesetofvaluesmayvarybetweenmachines,
andoperationslikearithmeticrightshiftmaybehavedifferently.
Morecomplicatedexamplesdonotfaremuchbetter.Typicallywewould
defineanelementofalinearlistasastructure
typedefstructnode{
structnode*next;
...information...
}node;
andfortheoperationswespecifyfunctionheaderslike
node*head(node*elt,constnode*tail);
Thisapproach,however,isquitesloppy.Goodprogrammingprinciplesdictate
thatweconcealtherepresentationofadataitemanddeclareonlythepossible
Manipulations.
1.2AbstractDataTypes
Wecalladatatypeabstract,ifwedonotrevealitsrepresentationtotheuser.Ata
theoreticallevelthisrequiresustospecifythepropertiesofthedatatypeby
mathematicalaxiomsinvolvingthepossibleoperations.Forexample,wecan
removeanelementfromaqueueonlyasoftenaswehaveaddedonepreviously,
andweretrievetheelementsinthesameorderinwhichtheywereadded.
Abstractdatatypesoffergreatflexibilitytotheprogrammer.Sincethe
representationisnotpartofthedefinition,wearefreetochoosewhateveriseasiestormostefficientto
implement.Ifwemanagetodistributethenecessaryinformationcorrectly,useofthedatatypeandour
choiceofimplementationaretotally
independent.
Abstractdatatypessatisfythegoodprogrammingprinciplesofinformationhidinganddivideand
conquer.Informationsuchastherepresentationofdataitemsis
givenonlytotheonewithaneedtoknow:totheimplementerandnottotheuser.
Withanabstractdatatypewecleanlyseparatetheprogrammingtasksofimplementationandusage:we
arewellonourwaytodecomposealargesysteminto
smallermodules.
2
1 .3AnExample
Sohowdoweimplementanabstractdatatype?Asanexampleweconsideraset
ofelementswiththeoperationsadd,find,anddrop.*Theyallapplytoasetandan
elementandreturntheelementaddedto,foundin,orremovedfromaset.find
canbeusedtoimplementaconditioncontainswhichtellsuswhetheranelement
isalreadycontainedinaset.
Viewedthisway,setisanabstractdatatype.Todeclarewhatwecandowith
aset,westartaheaderfileSet.h:
#ifndefSET_H
#defineSET_H
externconstvoid*Set;
void*add(void*set,constvoid*element);
void*find(constvoid*set,constvoid*element);
void*drop(void*set,constvoid*element);
intcontains(constvoid*set,constvoid*element);
#endif
Thepreprocessorstatementsprotectthedeclarations:nomatterhowmanytimes
weincludeSet.h,theCcompileronlyseesthedeclarationsonce.Thistechniqueof
protectingheaderfilesissostandard,thattheGNUCpreprocessorrecognizesitand
doesnotevenaccesssuchafilewhenitsprotectingsymbolisdefined.
Set.hiscomplete,butisituseful?Wecanhardlyrevealorassumeless:Set
willhavetosomehowrepresentthefact,thatweareworkingwithsets;add()
takesanelement,addsittoaset,andreturnswhateverwasaddedoralready
presentintheset;find()looksforanelementinasetandreturnswhateveris
presentinthesetoranullpointer;drop()locatesanelement,removesitfroma
set,andreturnswhateverwasremoved;contains()convertstheresultoffind()
intoatruthvalue.
*Unfortunately,removeisanANSI-Clibraryfunctiontoremoveafile.Ifweusedthisnameforaset
function,wecouldnolongerincludestdio.h.
1.4MemoryManagement
Thegenericpointervoid*isusedthroughout.Ontheonehanditmakesit
impossibletodiscoverwhatasetlookslike,butontheotherhanditpermitsusto
passvirtuallyanythingtoadd()andtheotherfunctions.Noteverythingwillbehave
likeasetoranelement—wearesacrificingtypesecurityintheinterestofinformationhiding.
However,wewillseeinchapter8thatthisapproachcanbemade
completelysecure.
1.4MemoryManagement
Wemayhaveoverlookedsomething:howdoesoneobtainaset?Setisapointer,
notatypedefinedbytypedef;therefore,wecannotdefinelocalorglobalvariables
oftypeSet.Instead,weareonlygoingtousepointerstorefertosetsandelements,andwedeclare
sourceandsinkofalldataitemsinnew.h:
void*new(constvoid*type,...);
voiddelete(void*item);
JustlikeSet.hthisfileisprotectedbyapreprocessorsymbolNEW_H.Thetextonly
showstheinterestingpartsofeachnewfile,thesourcediskettecontainsthecompletecodeofall
examples.
new()acceptsadescriptorlikeSetandpossiblymoreargumentsforinitializationandreturnsapointer
toanewdataitemwitharepresentationconformingto
thedescriptor.delete()acceptsapointeroriginallyproducedbynew()andrecycles
3
theassociatedresources.
new()anddelete()presumablyareafrontendtotheANSI-Cfunctionscalloc()
andfree().Iftheyare,thedescriptorhastoindicateatleasthowmuchmemoryis
Required.
1.5Object
Ifwewanttocollectanythinginterestinginaset,weneedanotherabstractdata
typeObjectdescribedbytheheaderfileObject.h:
externconstvoid*Object;/*new(Object);*/
intdiffer(constvoid*a,constvoid*b);
differ()cancompareobjects:itreturnstrueiftheyarenotequalandfalseifthey
are.Thisdescriptionleavesroomforthefunctionalityofstrcmp():forsomepairs
ofobjectswemightchoosetoreturnanegativeorpositivevaluetospecifyanordering.
Reallifeobjectsneedmorefunctionalitytodosomethinguseful.Forthe
moment,werestrictourselvestothebarenecessitiesformembershipinaset.If
webuiltabiggerclasslibrary,wewouldseethataset—andinfacteverything
else—isanobject,too.Atthispoint,alotoffunctionalityresultsmoreorlessfor
free.
1.6AnApplication
Withtheheaderfiles,i.e.,thedefinitionsoftheabstractdatatypes,inplacewecan
writeanapplicationmain.c:
#include<stdio.h>
#include"new.h"
#include"Object.h"
#include"Set.h"
intmain()
{void*s=new(Set);
void*a=add(s,new(Object));
void*b=add(s,new(Object));
void*c=new(Object);
if(contains(s,a)&&contains(s,b))
puts("ok");
if(contains(s,c))
puts("contains?");
if(differ(a,add(s,a)))
puts("differ?");
if(contains(s,drop(s,a)))
puts("drop?");
delete(drop(s,b));
delete(drop(s,c));
return0;
}
Wecreateasetandaddtwonewobjectstoit.Ifalliswell,wefindtheobjectsin
thesetandweshouldnotfindanothernewobject.Theprogramshouldsimply
printok.
Thecalltodiffer()illustratesasemanticpoint:amathematicalsetcanonly
containonecopyoftheobjecta;anattempttoadditagainmustreturntheoriginal
objectanddiffer()oughttobefalse.Similarly,onceweremovetheobject,it
shouldnolongerbeintheset.
4
Removinganelementnotinasetwillresultinanullpointerbeingpassedto
delete().Fornow,westickwiththesemanticsoffree()andrequirethistobe
acceptable.
1.7AnImplementation
main.cwillcompilesuccessfully,butbeforewecanlinkandexecutetheprogram,
wemustimplementtheabstractdatatypesandthememorymanager.Ifanobject
storesnoinformationandifeveryobjectbelongstoatmostoneset,wecan
representeachobjectandeachsetassmall,unique,positiveintegervaluesusedas
indicesintoanarrayheap[].Ifanobjectisamemberofaset,itsarrayelementcontainstheinteger
valuerepresentingtheset.Objects,therefore,pointtotheset
containingthem.
Thisfirstsolutionissosimplethatwecombineallmodulesintoasinglefile
Set.c.Setsandobjectshavethesamerepresentation,sonew()paysnoattention
tothetypedescription.Itonlyreturnsanelementinheap[]withvaluezero:
#if!definedMANY||MANY<1
#defineMANY10
#endif
staticintheap[MANY];
void*new(constvoid*type,...)
{int*p;/*&heap[1..]*/
for(p=heap+1;p<heap+MANY;++p)
if(!*p)
break;
assert(p<heap+MANY);
*p=MANY;
returnp;
}
Weusezerotomarkavailableelementsofheap[];therefore,wecannotreturna
referencetoheap[0]—ifitwereaset,itselementswouldcontaintheindexvalue
zero.
Beforeanobjectisaddedtoaset,weletitcontaintheimpossibleindexvalue
MANYsothatnew()cannotfinditagainandwestillcannotmistakeitasamember
ofanyset.
new()canrunoutofmemory.Thisisthefirstofmanyerrors,that‘‘cannot
happen’’.WewillsimplyusetheANSI-Cmacroassert()tomarkthesepoints.A
morerealisticimplementationshouldatleastprintareasonableerrormessageor
useageneralfunctionforerrorhandlingwhichtheusermayoverwrite.Forourpurposeofdeveloping
acodingtechnique,however,weprefertokeepthecode
uncluttered.Inchapter13wewilllookatageneraltechniqueforhandlingexceptions.
delete()hastobecarefulaboutnullpointers.Anelementofheap[]isrecycled
bysettingittozero:
voiddelete(void*_item)
{int*item=_item;
if(item)
{assert(item>heap&&item<heap+MANY);
*item=0;
}
}
Weneedauniformwaytodealwithgenericpointers;therefore,weprefixtheir
nameswithanunderscoreandonlyusethemtoinitializelocalvariableswiththe
5
desiredtypesandwiththeappropriatenames.
Asetisrepresentedinitsobjects:eachelementpointstotheset.IfanelementcontainsMANY,itcan
beaddedtotheset,otherwise,itshouldalreadybein
thesetbecausewedonotpermitanobjecttobelongtomorethanoneset.
void*add(void*_set,constvoid*_element)
{int*set=_set;
constint*element=_element;
assert(set>heap&&set<heap+MANY);
assert(*set==MANY);
assert(element>heap&&element<heap+MANY);
if(*element==MANY)
*(int*)element=set—heap;
else
assert(*element==set—heap);
return(void*)element;
}
assert()takesoutabitofinsurance:wewouldonlyliketodealwithpointersinto
heap[]andthesetshouldnotbelongtosomeotherset,i.e.,itsarrayelementvalue
oughttobeMANY.
Theotherfunctionsarejustassimple.find()onlylooksifitselementcontains
theproperindexfortheset:
void*find(constvoid*_set,constvoid*_element)
{constint*set=_set;
constint*element=_element;
assert(set>heap&&set<heap+MANY);
assert(*set==MANY);
assert(element>heap&&element<heap+MANY);
assert(*element);
return*element==set—heap?(void*)element:0;
}
contains()convertstheresultoffind()intoatruthvalue:
intcontains(constvoid*_set,constvoid*_element)
{
returnfind(_set,_element)!=0;
}
drop()canrelyonfind()tocheckiftheelementtobedroppedactuallybelongsto
theset.Ifso,wereturnittoobjectstatusbymarkingitwithMANY:
void*drop(void*_set,constvoid*_element)
{int*element=find(_set,_element);
if(element)
*element=MANY;
returnelement;
}
Ifwewerepickier,wecouldinsistthattheelementtobedroppednotbelongto
anotherset.Inthiscase,however,wewouldreplicatemostofthecodeoffind()
indrop().
Ourimplementationisquiteunconventional.Itturnsoutthatwedonotneed
differ()toimplementaset.Westillneedtoprovideit,becauseourapplication
usesthisfunction.
6
intdiffer(constvoid*a,constvoid*b)
{
returna!=b;
}
Objectsdifferexactlywhenthearrayindicesrepresentingthemdiffer,i.e.,asimple
pointercomparisonissufficient.
Wearedone—forthissolutionwehavenotusedthedescriptorsSetand
ObjectbutwehavetodefinethemtokeepourCcompilerhappy:
constvoid*Set;
constvoid*Object;
Wedidusethesepointersinmain()tocreatenewsetsandobjects.
1.8AnotherImplementation
WithoutchangingthevisibleinterfaceinSet.hwecanchangetheimplementation.
Thistimeweusedynamicmemoryandrepresentsetsandobjectsasstructures:
structSet{unsignedcount;};
structObject{unsignedcount;structSet*in;};
countkeepstrackofthenumberofelementsinaset.Foranelement,count
recordshowmanytimesthiselementhasbeenaddedtotheset.Ifwedecrement
counteachtimetheelementispassedtodrop()andonlyremovetheelement
oncecountiszero,wehaveaBag,i.e.,asetwhereelementshaveareference
count.
Sincewewillusedynamicmemorytorepresentsetsandobjects,weneedto
initializethedescriptorsSetandObjectsothatnew()canfindouthowmuch
memorytoreserve:
staticconstsize_t_Set=sizeof(structSet);
staticconstsize_t_Object=sizeof(structObject);
constvoid*Set=&_Set;
constvoid*Object=&_Object;
new()isnowmuchsimpler:
void*new(constvoid*type,...)
{constsize_tsize=*(constsize_t*)type;
void*p=calloc(1,size);
assert(p);
returnp;
}
delete()canpassitsargumentdirectlytofree()—inANSI-Canullpointermaybe
passedtofree().
add()hastomoreorlessbelieveitspointerarguments.Itincrementsthe
element’sreferencecounterandthenumberofelementsintheset:
void*add(void*_set,constvoid*_element)
{structSet*set=_set;
structObject*element=(void*)_element;
assert(set);
assert(element);
if(!element—>in)
element—>in=set;
else
assert(element—>in==set);
++element—>count,++set—>count;
7
returnelement;
}
find()stillchecks,iftheelementpointstotheappropriateset:
void*find(constvoid*_set,constvoid*_element)
{conststructObject*element=_element;
assert(_set);
assert(element);
returnelement—>in==_set?(void*)element:0;
}
contains()isbasedonfind()andremainsunchanged.
Ifdrop()findsitselementintheset,itdecrementstheelement’sreference
countandthenumberofelementsintheset.Ifthereferencecountreacheszero,
theelementisremovedfromtheset:
void*drop(void*_set,constvoid*_element)
{structSet*set=_set;
structObject*element=find(set,_element);
if(element)
{if(——element—>count==0)
element—>in=0;
——set—>count;
}
returnelement;
}
Wecannowprovideanewfunctioncount()whichreturnsthenumberofelementsinaset:
unsignedcount(constvoid*_set)
{conststructSet*set=_set;
assert(set);
returnset—>count;
}
Ofcourse,itwouldbesimplertolettheapplicationreadthecomponent.count
directly,butweinsistonnotrevealingtherepresentationofsets.Theoverheadof
afunctioncallisinsignificantcomparedtothedangerofanapplicationbeingableto
overwriteacriticalvalue.
Bagsbehavedifferentlyfromsets:anelementcanbeaddedseveraltimes;it
willonlydisappearfromtheset,onceitisdroppedasmanytimesasitwasadded.
Ourapplicationinsection1.6addedtheobjectatwicetotheset.Afteritis
droppedfromthesetonce,contains()willstillfinditinthebag.Thetestprogram
nowhastheoutput
ok
drop?
8
REFERENCES
[ANSI]AmericanNationalStandardforInformationSystems—Programming
LanguageCX3.159-1989.
[AWK88]A.V.Aho,B.W.KernighanundP.J.WeinbergerTheawkProgramming
LanguageAddison-Wesley1988,ISBN0-201-07981-X.
AUTHOR
GuruprasadDavangave
SoftwareEngineer
ComputerScienceandEngineering.[2021]