Wednesday, May 6, 2009
Example of object oriented C
I have previously written an entry stating that c can be used as object oriented language. Now I decided to write an example demostrating this. So here it is, a program drawing lottery numbers, written in ansi c, using object oriented approach.
I know the code looks horrible. It is. Object oriented code often is. And this example could have been written so much easier with plain procedural c. But this shows how basic requirements of object orientation are fullfilled. Lottery machine is (virtual) base class, and basic & viking lottery are derived classes. If i had more enthusiasm, i would have written joker class too.--
And as an explanation, in finland we have two different types of lottery, viking lotto and regular lotto. Theres different amount of numbers drawn, but i cant remember the accurate amounts. Theres also so called joker, but i cant remember the amount of numbers in it either.
And as a final note, i know rand() is not perfect function to use, but since this was not meant to be real lottery machine, i could not care less :)
So here's the lottery source. Compile with
gcc -Wall -o lotteryExe lotto.c lottoTest.c regularlotto.c vikinglotto.c
Labels:
code,
coding,
example,
object oriented C
Thursday, April 2, 2009
What I learned today.
I've noticed that SW designer's job is constant learning. If you stop learning, tou stop advancing. For a newish guy on the field like me, most of the learning is related to existing systems - but even the old chaps need to keep making progress, new technologies keep coming, and falling back means giving advantage to others.
So, I guess I'll start writing down this "What I learned today" post, in which I try to add new comments ehen I have learned something new. Just short sentences, with no long background, but with some information that was new to me.
But since I started this now, not at my first day in the job, I'll write down a couple of things in this post too :)
Last December.
Untill the really latest glibc versions, there's a bug in glibc's timer_create() function implementation, which may cause a crash under certain conditions. (When timer expires, attempt to read the internal list where timers are kept in glibc, may access to invalid memory location)
A few months ago.
Helgrind is yet another great tool built on valgrind.
(It can be used to detect synchronization issues in multithreaded applications - although it has it's limitations)
A few weeks ago.
In some glibc versions for powerpc have a bug, which makes valgrind yelling a lot of false alarms!
A few days ago.
pthread's stack size cannot be easily (or portably) changed after the thread is created.
(It can be set during the thread initialization. It is not really common to have real need for changing the default stack size [actually this sounds like terribly failed design], but there may be cases where one needs to create many [read some hundreds of] threads with limited virtual address space [*shrugs*]. On linux at least, the real memory is not used more than really required, but virtual address space will be reserved for whole threads stack (no matter how much the thread really needs the stack).
Today: (02.04.2009)
In most linuxes, there's a program called "yes" included, which will repeatedly print the text given as argument. My co-worker told that to me today, and I used it to generate some CPU load when I needed to do some performance analysis. I used:
yes Matti > /dev/null
renice -19
So, I guess I'll start writing down this "What I learned today" post, in which I try to add new comments ehen I have learned something new. Just short sentences, with no long background, but with some information that was new to me.
But since I started this now, not at my first day in the job, I'll write down a couple of things in this post too :)
Last December.
Untill the really latest glibc versions, there's a bug in glibc's timer_create() function implementation, which may cause a crash under certain conditions. (When timer expires, attempt to read the internal list where timers are kept in glibc, may access to invalid memory location)
A few months ago.
Helgrind is yet another great tool built on valgrind.
(It can be used to detect synchronization issues in multithreaded applications - although it has it's limitations)
A few weeks ago.
In some glibc versions for powerpc have a bug, which makes valgrind yelling a lot of false alarms!
A few days ago.
pthread's stack size cannot be easily (or portably) changed after the thread is created.
(It can be set during the thread initialization. It is not really common to have real need for changing the default stack size [actually this sounds like terribly failed design], but there may be cases where one needs to create many [read some hundreds of] threads with limited virtual address space [*shrugs*]. On linux at least, the real memory is not used more than really required, but virtual address space will be reserved for whole threads stack (no matter how much the thread really needs the stack).
Today: (02.04.2009)
In most linuxes, there's a program called "yes" included, which will repeatedly print the text given as argument. My co-worker told that to me today, and I used it to generate some CPU load when I needed to do some performance analysis. I used:
yes Matti > /dev/null
renice -19
Wednesday, March 25, 2009
Linux virtuall address space.
Today I spotted a question about ability to read process data after process freeds the memory it has used. (It was on a one of the best C programming related forums I know, at CBoard )
So I started writing an answer, which accidentaly became lenghty. After I first read it I felt that there's no hope it could clarify a thing to anyone. But after a few beers, I started to feel pretty damn proud about that babbling. So I decided to copy it to here too. So enjoy.
About the reading memory after freeing it etc. I'll try to shed some light on this topic, and I hope if the more experienced guys spot a problem in my explanation, they will further teach both me and you

I will be talking in linux point of view, but I assume that most modern systems do it in somewhat similar manner. Of course there's some realtime systems (like OSE), in which all memory is available to all brogram blocks (Eg. processes), but I assume you're talking about some desktop system.
So when we simplify enough we can say following. . Most modern OSes do map the real physical memory into virtual memory addresses, and give to each process their own virtual address space. Actually, in most modern computer systems, we have a hardware memory management unit which handles conversions from virtual addresses to physical memory and vice versa. So basically, at user space (which is basically all applications that aren't either part of the kernel, or a kernel module) it is not possible to access straight to hardware (memory or other hardware). Hardware is invisible for userspace processes, it can only be accessed via drivers written in kernel space. (drivers offer interface for user space applications). [[There is a workaround, but let's not mess with it now]].
So process cannot directly access to RAM.
When we launch a process, certain virtual address space is given to it (and certain physical ran corresponding to it, but the process has no means to get direct HW addresses). The process has no access outside of this sandbox. When we launch another process, it will have it's own virtual address space, and it's own corresponding physical ram. If process is running out of ram, kernel has means to increase it.
If another process tries to access outside of it's sandbox, Eg. requests reading from / writing to location which is not in it's address space, MMU will wake up the kernel, which will check out what is happening. If request was illegal, kernel will generate signal SIGSEGV, and send it to your program. => segmentation fault.
However physical ram is not cleaned, and the amount assigned to each process may change. There's also possibility of swapping, where some memory pages are written temporarily to hard disk, to free up some ram for other purposes.
So basically, it indeed is possible that there is leftowers of data written by your program in the memory. But there should be no traces where in the physical ram your data was, so knowing which bytes belonged to what program - not to mention what were these bytes used for - is quite close to impossible. I say quite close because I've never considered that, and I do not have any accurate information how possible it could be.
If I go a little further off topic to see if I have understood it correctly (I believe there is far more experienced fellows amongst us - who will correct me if I go wrong), there is at least following benefits of having physical addresses hidden:
1. Security. No user space process has access to go and mess the HW as they want - they need to use drivers, which should be written in such a manner that hugest hazards are not possible...
2. Per process memory mapping. If memory would be accessible between processes... Oh joy. If you have ever attempted to build huge and complicated multithreaded software you know what I mean. It really is a mess. Use of one uninitialized pointer may crash anything anywhere, or just make generally bizarre things to happen.
I guess steps 1 and 2 could be implemented even if the applications could access ram directly - but I am not sure how easy / difficult it would be.
3. Shared libraries. Shared libraries are loaded to memory during runtime. When each process has their own virtual address space, shared libraries can be downloaded to physical memory only once. The same physical memory block containing the library can then be mapped for each process' address space.
Finally, the inter process communication often uses "creature" called shared memory. processes can request a shared memory block from kernel, which will then map some physical memory chunk to more than one process' virtual address space. Note however that virtual addresses in different processes need not to be the same, even though they point at same physical ram. Hence shared memory is usually accessed according to the offsets from the start of the memory pool.
However, note that when you quit your program, the shared memory is not automatically freed. (thanks to brewbuck for confirmation
). It will stay accessible (if it was created to be accessible).And finally the [[]] which I wrote at the beginning. It is possible to write a driver, which can map some real hardware addresses to shared memory, which can then furthermore be mapped by a user space process. That way the HW can be accessed directly by a user space application.
Labels:
Linux,
shared memory,
virtual memory
Tuesday, March 24, 2009
C can be used as object oriented language!
I am sick of C++ coders, who tend to yell their OOP terms to the world, and claim that OOP is the only way, and C++ is the best... Actually they're wrong when they say that C is a bad language, because it is not object oriented. They're wrong in two things.
1. C is far from bad language. Some of the most significant, most efficient, and most important things are still written in C. Linux, loads of embedded SW, (did you know that there is probably almost same amount of code in a modern car that there is in Windows XP? Scary thought.) and so on.
2. C can be seen as object oriented language, and sometimes it actually benefits to use it as such.
Actually, there is only a few things in C++, that you cannot do with C (not with same syntax of course, but with similar idea behind). The most important of those are probably destructors, templates, and overloading.
Did I hear lil C++ coder screaming about classes, virtual classes and inheritance? Allright. Maybe I'll explain a little further.
The most critical characters of object are:
1. It binds the data, and functionality (note, I avoided the "method" word... ehh.. Obviously I didnt :D )
2. It can be instantiated.
Yeah, how do you do that with C - you rat. I heard you, and don't worry, I'll explain.
I guess that even though you're so full of your fancy classes, you must've seen reserved word struct somewhere. At least when you did try to use it as a variable name last week. (Yes, the structs are in Cpp too). Then a bit more difficult thing, function pointer. If that's not a familiar concept to you (that exists in C++ too - although it is not as easy to use them there. Especially not for someone like me, who has not written C++ in... ... long time.) you can chek out my blog post here (in Finnish though).
So how to do a C representative for
const int AGE_STOMACH_RATIO 5
class dog
{
private:
EColour furColour;
int age;
unsigned int stomach_state;
bool check_condition();
int calculateStomachCapacity();
void die();
public:
dog(EColour colour);
void feed(int food_amount);
};
dog::dog(EColour colour)
{
furColour=colour;
}
bool dog::check_condition()
{
if(calculateStomachCapacity() < 0 ...
Yeah, do that with C you say...
struct Sdog;
int dog_check_condition(Sdog *_this);
typedef int (*check_conditionF)(Sdog *_this);
typedef int (*calculateStomachCapacityF)(Sdog *_this);
typedef void (*dieF)(void);
typedef void (*dogF)(Sdog *_this, EColour colour);
typedef void (*feedF)(Sdog *_this);
typedef struct Sdog
{
EColour furColour;
int age;
unsigned int stomach_state;
check_conditionF check_condition;
calculateStomachCapacityF calculateStomachCapacity;
dieF die;
dogF dog;
feedF feed;
}Sdog;
int dog_check_condition(Sdog *_this)
{
//starvation //overeating
if(_this->stomach_state == 0 || _this->calculateStomachCapacity(_this) <>stomach_state )
return 0;
return 1;
}
int dog_
void initDog(Sdog *_this, EColour colour)
{
_this->furColour=colour;
_this->age=1;
_this->stomach_state=3;
_this->check_condition=&dog_check_condition;
.
.
.
and so on..
}
I hope you can see the analogy.
Okay okay. How about the second important thing in allmighty OOP:
The instantiation.
There can be several instances of dog ... blaa blaa blaa...
int main()
{
Sdog *lassie;
lassie=malloc(sizeof(Sdog));
lassie->dog=&initDog;
*(lassie->dog)(lassie,EColor_brown);
*(lassie->feed)(lasie,2);
.
.
// or as a local variable:
/*
Sdog lassie;
*lassie.dog=&initDog;
*lassie.dog(&lassie,EColor_brown);
*lassie.feed(&lassie,2);
.
.
*/
What's next? Data encapsulation? Allright.
This requires some "cheating", but what was the point of this blog is - it is doable.
Now we divide our Sdog into two different structs. Public Sdog, and private S_dog for example
typedef struct Sdog
{
dogF dog;
feedF feed;
char internalData[1];
}Sdog;
typedef struct S_dog
{
//As abowe.
}S_dog;
Now we place this struct Sdog and it's functions into a public header, but keep S_dog and private functions in private headers. The .c file (implementation of functions) then includes both headers, but only public one is given to dog users. When we initialize Sdog, we'll allocate space for S_dog too (in the placeholder)(by using realloc, or then we can make the struct allocation in dogInit() function, and change it to return the pointer to created object). And when we use internal functions, we'll just cast the placeholder (Eg. char internalData[1]) to S_dog * type.
Inheritance you say
Okay. This is rather simple. We can for example define structure Sdoberman, which has structure Sdog as it's first part, and append doberman specific data and functions at the end. Then when we wish to only use generic data, we'll handle the pointer to struct as Sdog, and when we want to use doberman features, we'll cast struct to Sdoberman.
Virtual classes someone whispers...
Yeah. Now what? If we have virtual class representative Sdog, and "classes" Sdoberman, Schihuahua and Smixed derived from virtual class. Now we can use aforementioned idea, and place Sdog struct as first item. We can also add a "dogType" variable in Sdog struct, which is initialized correctly when doberman, chihuahua or mixed is created. Then in virtual functions we can simply check the dogType variable, perform correct cast to _this pointer, and call correct frunction from Sdobermann, Schihuahua or Smixed.
Another approach (which also sounds more natural to C), is to have the "dogType" as first member in Sdog struct, use void * pointers, to the struct, dereference first sizeof(dogType) bytes and then perform the cast to correct dog struct.
I know. All C++ coders are terrified. But when a C coder looks at this, he/she notices the idea (I hope), and when we look the interface this creates, we'll really see the basic OOP structures.
A note about destructors and C
Furthermore, it is also possible to create a destructor type thing for your objects - when they're dynamically allocated with malloc() type call. It involves quite simple steps:
1. build your own malloc() wrapper (MyAlloc() for example), which allocates the space user requested + some extra for some internal structures. Then return pointer to this "user data area". Allow registration of callback function, which will be executed when memory is freed (the destructor), and store pointer to that in the extra space (which is invisible for user) you allocated. You can also create some additional debug things, like add some space after the user requested block, and write somekind of "end mark" to it. End mark which will be destroyed, if user overwrites the reserved memory... (This naturally requires storing the allocated blocks size in internal "header" too)
2. build your own free() wrapper, which executes the callback (destructor) and checks the endmark validity (if such was implemented), and frees the (internal + user) memory.
Final Words...
Okay. I think I have trolled enough now, and most of the C++ coders are so upset by now, that they have propably stopped reading ;) So I guess it is good time to tell what I really think. I think that C++ is great language, providing many things that are better than plain C. However C++ is not really usable in all environments, or at least C++'s best parts aren't. For example, in an embedded project where I was working I did one test using C++ and STL. Required stack size was tripled compared to C implementation. You may tell that it is because I used buggy compiler/buggy STL implementation. You can say this, you can say that. However fact is, that with C++, you'll easily get bloated code, larger executables, increased resource usage. You may be able to avoid these (partially), by limiting the C++ toolset you use - but then you cannot keep yapping how great the C++'s all cool features are, since you cannot really use all cool features. C++ is great for many purposes, but there is things where C is far better option.
I'll see if I can later show you some simple implementation of C++ style C - I have really sometimes used it, and it may be a good approach to some problems. Actually, I described one such situation in my Finnish blog here (In finnish blog => it is written in Finnish.)
Friday, March 20, 2009
Answer to pitfall III (I forgot to post this earlier)
The answer is simple. I bet the bells start ringing when I say this. Usually with 32 bit systems, memory address is 32 bit wide. With 64 bit systems it often is 64 bits wide...
...
...Now, look at the pitfall III again, and you'll see that we do pinter arithmetics by handling pointers as integers. This means that half of the address will be cropped, and remaining address is most propably something pointing at invalid locations. After you use it, MMU will detect your process accessing out of the virtual address space mapped for the process. It'll kick up kernel, which most propably decides to send SIGSEGV signal to your program. And if youre writing anything more critical than snake game, you haven't registered handler which forgives SIGSEGV. Eg. result is segmentation fault. Adios. Bye. Game over. Sommoro ja Soronoo.
...
...Now, look at the pitfall III again, and you'll see that we do pinter arithmetics by handling pointers as integers. This means that half of the address will be cropped, and remaining address is most propably something pointing at invalid locations. After you use it, MMU will detect your process accessing out of the virtual address space mapped for the process. It'll kick up kernel, which most propably decides to send SIGSEGV signal to your program. And if youre writing anything more critical than snake game, you haven't registered handler which forgives SIGSEGV. Eg. result is segmentation fault. Adios. Bye. Game over. Sommoro ja Soronoo.
Labels:
C,
pitfall,
portability,
porting,
porting 32 bit to 64 bit
My first C quiz at 2009
Hi dee Ho peeps!
It has been looong since I wrote last pitfall/quiz in here, so I guess it is about the time to continue :)
So at this time I'll just remind you of one nastyish C specific bug - which is easy to avoid by reading the compilers warnings. I've tested it on gcc.
So following code will work on 32 bit machine, but on 64 bit machine you are allowed to expect sudden death... What's the cause?
And as always, you can use comment section to give answers :)
file main.c
#include
#include
#include
int main()
{
int data[32];
int i;
int *copydata;
for(i=0;i<32;i++) copydata="allocateMyMate(sizeof(data));" copydata="="NULL)" i="0;i<32;i++)" d="%d," d="%d\n">myalloc.c
#include
#include
void * allocateMyMate(size_t size)
{
return malloc(size);
}
So where's the bug?
And even though the free() corresponding to malloc() is missing, do not consider it as a bug at this time. (Naturally it is a bug, but it was not the bug I wrote this question for).
HINTS BELoW! Do Not Look YET ;)
1. With c++ youre safe from this bug.
2. Try compiling and check the compiler warnings!
It has been looong since I wrote last pitfall/quiz in here, so I guess it is about the time to continue :)
So at this time I'll just remind you of one nastyish C specific bug - which is easy to avoid by reading the compilers warnings. I've tested it on gcc.
So following code will work on 32 bit machine, but on 64 bit machine you are allowed to expect sudden death... What's the cause?
And as always, you can use comment section to give answers :)
file main.c
#include
#include
#include
int main()
{
int data[32];
int i;
int *copydata;
for(i=0;i<32;i++) copydata="allocateMyMate(sizeof(data));" copydata="="NULL)" i="0;i<32;i++)" d="%d," d="%d\n">myalloc.c
#include
#include
void * allocateMyMate(size_t size)
{
return malloc(size);
}
So where's the bug?
And even though the free() corresponding to malloc() is missing, do not consider it as a bug at this time. (Naturally it is a bug, but it was not the bug I wrote this question for).
HINTS BELoW! Do Not Look YET ;)
1. With c++ youre safe from this bug.
2. Try compiling and check the compiler warnings!
Labels:
C,
C quiz,
coding,
pitfall,
portability
Tuesday, September 16, 2008
C - Explode.
I decided to write also ANSI C version of php's explode() function. Why? Well, because it seems to me that most of the visitors here have been searching for C/C++ implementation for php's explode() function. So here it is my loyal readers, cxplode() written in C. a.k.a Cexplode() by Maz :)
(I'll also place it in downloadable Cexplode.tar.gz when I'll find the time.)
Cexplode.h
Cexplode.c
Cexplode_example.c
(I'll also place it in downloadable Cexplode.tar.gz when I'll find the time.)
Cexplode.h
/* ******************************************************** */
/* *
* Implementation of php's explode written in C *
* Written by Maz (2008) *
* http://maz-programmersdiary.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */
#ifndef CEXPLODE_H
#define CEXPLODE_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct CexplodeStrings
{
int amnt;
char **strings;
}CexplodeStrings;
typedef enum ECexplodeRet
{
ECexplodeRet_InternalFailure = -666,
ECexplodeRet_InvalidParams = -667
}ECexplodeRet;
int Cexplode
(
const char *string,
const char *delim,
CexplodeStrings *exp_obj
);
char *Cexplode_getNth(int index,CexplodeStrings exp_obj);
char *Cexplode_getfirst(CexplodeStrings exp_obj);
void Cexplode_free(CexplodeStrings exp_obj);
#endif
Cexplode.c
/* ******************************************************** */
/* *
* Implementation of php's explode written in C *
* Written by Maz (2008) *
* http://maz-programmersdiary.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */
#include "Cexplode.h"
int Cexplode
(
const char *string,
const char *delim,
CexplodeStrings *exp_obj
)
{
int stringL = 0;
int delimL = 0;
int index;
int pieces=0;
int string_start=0;
char **tmp=NULL;
//Sanity Checks:
if(NULL==string || NULL==delim || NULL == exp_obj)
{
printf("Invalid params given to Cexplode!\n");
return ECexplodeRet_InvalidParams;
}
stringL = strlen(string);
delimL = strlen(delim);
if(delimL>=stringL)
{
printf("Invalid params given to Cexplode!\n");
return 0;
}
for(index=0;index<stringL-delimL;index++)
{
if(string[index]==delim[0])
{
//Check if delim was actually found
if( !memcmp(&(string[index]),delim,delimL) )
{
//token found
//let's check if token was at the beginning:
if(index==string_start)
{
string_start+=delimL;
index+=delimL-1;
continue;
}
/*
if token was not at start, then we
should add it in CexplodeStrings
*/
pieces++;
if(NULL==tmp)
tmp=malloc(sizeof(char *));
else
tmp=realloc(tmp,sizeof(char *)*pieces);
if(NULL==tmp)
{
printf("Cexplode: Malloc failed!\n");
return ECexplodeRet_InternalFailure;
}
//alloc also for \0
tmp[pieces-1]=malloc
(
sizeof(char *)*(index-string_start+1)
);
if(NULL==tmp[pieces-1])
{
printf("Cexplode: Malloc failed!\n");
return ECexplodeRet_InternalFailure;
}
memcpy(
tmp[pieces-1],
&(string[string_start]),
index-string_start
);
tmp[pieces-1][index-string_start]='\0';
string_start=index+delimL;
index+=(delimL-1);
}//delim found
}//first letter in delim found from string
}//for loop
if(memcmp(&(string[index]),delim,delimL))
index+=delimL;
if(index!=string_start)
{
pieces++;
if(NULL==tmp)
tmp=malloc(sizeof(char *));
else
tmp=realloc(tmp,sizeof(char *)*pieces);
if(NULL==tmp)
{
printf("Cexplode: Malloc failed!\n");
return ECexplodeRet_InternalFailure;
}
tmp[pieces-1]=malloc
(
sizeof(char *)*(index-string_start+1)
);
if(NULL==tmp[pieces-1])
{
printf("Cexplode: Malloc failed!\n");
return ECexplodeRet_InternalFailure;
}
memcpy
(
tmp[pieces-1],
&(string[string_start]),
index-string_start
);
tmp[pieces-1][index-string_start+1]='\0';
}
exp_obj->amnt=pieces;
exp_obj->strings=tmp;
return pieces;
}
char *Cexplode_getNth(int index,CexplodeStrings exp_obj)
{
if(exp_obj.amnt<index)
{
return NULL;
}
return exp_obj.strings[index-1];
}
char *Cexplode_getfirst(CexplodeStrings exp_obj)
{
return Cexplode_getNth(1,exp_obj);
}
void Cexplode_free(CexplodeStrings exp_obj)
{
int i=0;
for(;i<exp_obj.amnt;i++)
free(exp_obj.strings[i]);
free(exp_obj.strings);
}
Cexplode_example.c
/* ******************************************************** */
/* *
* Implementation of php's explode written in C *
* Written by Maz (2008) *
* http://maz-programmersdiary.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */
#include "stdio.h"
#include "Cexplode.h"
int main(int argc,char *argv[])
{
char *string;
char *delim;
int retval;
int index=0;
char *token;
CexplodeStrings expString;
if(argc!=3)
{
printf("Test Command should be:\n");
printf
(
"<testExe> \"original string\" \"delimiter string\""
);
return -1;
}
string=argv[1];
delim=argv[2];
printf("TestString is \"%s\"\n",string);
printf("Test Delimiter is \"%s\"\n",delim);
if(0>(retval=Cexplode(string,delim,&expString)))
{
printf("CexplodeFailed!\n");
return -1;
}
else
{
//Way 1, use expString straight away:
printf("Way 1, use expString straight away:\n");
for(index=0;index<expString.amnt;index++)
{
printf
(
"token %d = %s\n",
index+1,
expString.strings[index]
);
}
/*
Way 2, you can use Cexplode_getNth,
or Cexplode_getfirst()
*/
printf(
"Way 2 use Cexplode_getNth, or Cexplode_getfirst():\n"
);
token=Cexplode_getfirst(expString);
printf("first token %s\n",token);
index=1;
while(NULL!=(token=Cexplode_getNth(++index,expString)))
{
printf("token %d = %s\n",index,token);
}
}
Cexplode_free(expString);
return 0;
}
Subscribe to:
Posts (Atom)