Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
Is this type of expression in C valid (on all compilers)?
If it is, is this good C?
char cloneString (char clone[], char string[], int Length)
{
if(!Length)
Length=64
;
int i = 0
;
while((clone[i++] = string[i]) != '\0', --Length)
;
clone[i] = '\0';
printf("cloneString = %s\n", clone);
};
Would this be better, worse, indifferent?
char *cloneString (char clone[], char string[], int Length)
{
if(!Length)
Length=STRING_LENGTH
;
char *r = clone
;
while
( //(clone[i++] = string[i]) != '\0'
*clone++ = *string++
, --Length
);
*clone = '\0';
return clone = r
;
printf("cloneString = %s\n", clone);
};
Stackoverflow wants me to add more text to this question!
Okay! I'm concerned about
a.) expressions such as c==(a=b)
b.) performance between indexing vs pointer
Any comments?
Thanks so much.
Yes, it's syntactically valid on all compilers (though semantically valid on none), and no, it isn't considered good C. Most developers will agree that the comma operator is a bad thing, and most developers will generally agree that a single line of code should do only one specific thing. The while loop does a whole four and has undefined behavior:
it increments i;
it assigns string[i] to clone[i++]; (undefined behavior: you should use i only once in a statement that increments/decrements it)
it checks that string[i] isn't 0 (but discards the result of the comparison);
it decrements Length, and terminates the loop if Length == 0 after being decremented.
Not to mention that assuming that Length is 64 if it wasn't provided is a terrible idea and leaves plenty of room for more undefined behavior that can easily be exploited to crash or hack the program.
I see that you wrote it yourself and that you're concerned about performance, and this is apparently the reason you're sticking everything together. Don't. Code made short by squeezing statements together isn't faster than code longer because the statements haven't been squeezed together. It still does the same number of things. In your case, you're introducing bugs by squeezing things together.
The code has Undefined Behavior:
The expression
(clone[i++] = string[i])
both modifies and accesses the object i from two different subexpressions in an unsequenced way, which is not allowed. A compiler might use the old value of i in string[i], or might use the new value of i, or might do something entirely different and unexpected.
Simple answer no.
Why return char and the function has no return statement?
Why 64?
I assume that the two arrays are of length Length - Add documentation to say this.
Why the ; on a new line and not after the statement?
...
Ok so I decided to evolve my comments into an actual answer. Although this doesn’t address the specific piece of code in your question, it answers the underlying issue and I think you will find it illuminating as you can use this — let’s call it guide — on your general programming.
What I advocate, especially if you are just learning programming is to focus on readability instead of small gimmicks that you think or was told that improve speed / performance.
Let’s take a simple example. The idiomatic way to iterate through a vector in C (not in C++) is using indexing:
int i;
for (i = 0; i < size; ++i) {
v[i] = /* code */;
}
I was told when I started programming that v[i] is actually computed as *(v + i) so in generated assembler this is broken down (please note that this discussion is simplified):
multiply i with sizeof(int)
add that result to the address of v
access the element at this computed address
So basically you have 3 operations.
Let’s compare this with accessing via pointers:
int *p;
for (p = v; p != v + size; ++p) {
*p = /*..*/;
}
This has the advantage that *p actually expands to just one instruction:
access the element at the address p.
2 extra instructions don’t seam much but if your program spends most of it’s time in this loop (either extremely large size or multiple calls to (the functions containing this) loop) you realise that the second version makes your program almost 3 times faster. That is a lot. So if you are like me when I started, you will choose the second variant. Don’t!
So the first version has readability (you explicitly describe that you access the i-th element of vector v), the second one uses a gimmick in detriment of readability (you say that you access a memory location). Now this might not be the best example for unreadable code, but the principle is valid.
So why do I tell you to use the first version: until you have a firm grasp on concepts like cache, branching, induction variables (and a lot more) and how they apply in real world compilers and programs performance, you should stay clear of these gimmicks and rely on the compiler to do the optimizations. They are very smart and will generate the same code for both variants (with optimization enabled of course). So the second variant actually differs just by readability and is identical performance-wise with the first.
Another example:
const char * str = "Some string"
int i;
// variant 1:
for (i = 0; i < strlen(str); ++i) {
// code
}
// variant 2:
int l = strlen(str);
for (i = 0; i < l; ++i) {
// code
}
The natural way would be to write the first variant. You might think that the second improves performance because you call the function strlen on each iteration of the loop. And you know that getting the length of a string means iterating through all the string until you reach the end. So basically a call to strlen means adding an inner loop. Ouch that has to slow the program down. Not necessarily: the compiler can optimize the call out because it always produces the same result. Actually you can do harm as you introduce a new variable which will have to be assigned a different register from a very limited registry pool (a little extreme example, but nevertheless a point is to be made here).
Don’t spend your energy on things like this until much later.
Let me show you something else that will illustrate further more that any assumptions that you make about performance will be most likely be false and misleading (I am not trying to tell you that you are a bad programmer — far from it — just that as you learn, you should invest your energy in something else than performance):
Let’s multiply two matrices:
for (k = 0; k < n; ++k) {
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
r[i][j] += a[i][k] * b[k][j];
}
}
}
versus
for (k = 0; k < n; ++k) {
for (j = 0; j < n; ++j) {
for (i = 0; i < n; ++i) {
r[i][j] += a[i][k] * b[k][j];
}
}
}
The only difference between the two is the order the operations get executed. They are the exact same operations (number, kind and operands), just in a different order. The result is equivalent (addition is commutative) so on paper they should take the EXACT amount of time to execute. In practice, even with optimizations enable (some very smart compilers can however reorder the loops) the second example can be up to 2-3 times slower than the first. And even the first variant is still a long long way from being optimal (in regards to speed).
So basic point: worry about UB as the other answers show you, don’t worry about performance at this stage.
The second block of code is better.
The line
printf("cloneString = %s\n", clone);
there will never get executed since there a return statement before that.
To make your code a bit more readable, change
while
(
*clone++ = *string++
, --Length
);
to
while ( Length > 0 )
{
*clone++ = *string++;
--Length;
}
This is probably a better approach to your problem:
#include <stdio.h>
#include <string.h>
void cloneString(char *clone, char *string)
{
for (int i = 0; i != strlen(string); i++)
clone[i] = string[i];
printf("Clone string: %s\n", clone);
}
That been said, there's already a standard function to to that:
strncpy(const char *dest, const char *source, int n)
dest is the destination string, and source is the string that must be copied. This function will copy a maximum of n characters.
So, your code will be:
#include <stdio.h>
#include <string.h>
void cloneString(char *clone, char *string)
{
strncpy(clone, string, strlen(string));
printf("Clone string: %s\n", clone);
}
Related
This is a general issue I've run into, and I've yet to find a solution to it that doesn't feel very "hack-y". Suppose I have some array of elements xs = {a_1, a_2, ..., a_n}, where I know some x is in the array. I wish to loop through the array, and do something with each element, up to and including the element x. Here's a version that does almost that, except it leaves out the very last element. Note that in this example the array happens to be a sorted list of integers, but in the general case this might not necessarily be true.
int xs[] = {1,2,3,4,5};
for (int i = 0; xs[i] != 4; i++) {
foo(xs[i]);
}
The only solutions I've seen so far are:
Just add a final foo(xs[i]); statement after the for-loop. This is first of all ugly and repetitious, especially in the case where foo is not just a function call but a list of statements. Second, it requires i to be defined outside the scope of the for-loop.
Manually break the loop, with an if-statement inside an infinite loop. This again seems ugly to me, since we're not really using the for and while constructs to their full extent. The problem is almost archetypal of what you'd use a for-loop for, the only difference is that we just want it to go through the loop one more time.
Does anyone know of a good solution to this problem?
In C, the for loop is a "check before body" operation, you want the "check after body" variant, a do while loop, something like:
int xs[] = {1,2,3,4,5};
{
int i = 0;
do {
foo(xs[i]);
} while (xs[i++] != 4);
}
You'll notice I've enclosed the entire chunk in its own scope (the outermost {} braces). This is just to limit the existence of i to make it conform more with the for loop behaviour.
In terms of a complete program showing this, the following code:
#include <stdio.h>
void foo(int x) {
printf("%d\n", x);
}
int main(void) {
int xs[] = {1,2,3,4,5};
{
int i = 0;
do {
foo(xs[i]);
} while (xs[i++] != 4);
}
return 0;
}
outputs:
1
2
3
4
As an aside, like you, I'm also not that keen of the two other solutions you've seen.
For the first solution, that won't actually work in this case since the lifetime of i is limited to the for loop itself (the int in the for statement initialisation section makes this so).
That means i will not have the value you expect after the loop. Either there will be no i (a compile-time error) or there will be an i which was hidden within the for loop and therefore unlikely to have the value you expect, leading to insidious bugs.
For the second, I will sometimes break loops within the body but generally only at the start of the body so that the control logic is still visible in a single area. I tend to do that if the for condition would be otherwise very lengthy but there are other ways to do this.
Try processing the loop as long as the previous element (if available) is not 4:
int xs[] = {1,2,3,4,5};
for (int i = 0; i == 0 || xs[i - 1] != 4; i++) {
foo(xs[i]);
}
This may not be a direct answer to the original question, but I would strongly suggest against making a habit of parsing arrays like that (it's like a ticking bomb waiting to explode at a random point in time).
I know you said you already know x is a member of xs, but when it is not (and this can accidentally happen for a variety of reasons) then your loop will crash your program if you are lucky, or it will corrupt irrelevant data if you are not lucky.
In my opinion, it is neither ugly nor "hacky" to be defensive with an extra check.
If the hurdle is the seemingly unknown length of xs, it is not. Static arrays have a known length, either by declaration or by initialization (like your example). In the latter case, the length can be calc'ed on demand within the scope of the declared array, by sizeof(arr) / sizeof(*arr) - you can even make it a reusable macro.
#define ARRLEN(a) (sizeof(a)/sizeof(*(a)))
...
int xs[] = {1,2,3,4,5};
/* way later... */
size_t xslen = ARRLEN(xs);
for (size_t i=0; i < xslen; i++) {
if (xs[i] == 4) {
foo( xs[i] );
break;
};
}
This will not overrun xs, even when 4 is not present in the array.
EDIT:
"within the scope of the declared array" means that the macro (or its direct code) will not work on an array passed as a function parameter.
// This does NOT work, because sizeof(arr) returns the size of an int-pointer
size_t foo( int arr[] ) {
return( sizeof(arr)/sizeof(*arr) );
}
If you need the length of an array inside a function, you can pass it too as a parameter along with the array (which actually is just a pointer to the 1st element).
Or if performance is not an issue, you may use the sentinel approach, explained below.
[end of EDIT]
An alternative could be to manually mark the end of your array with a sentinel value (a value you intentionally consider invalid). For example, for integers it could be INT_MAX:
#include <limits.h>
...
int xs[] = {1,2,3,4,5, INT_MAX};
for (size_t i=0; xs[i] != INT_MAX; i++) {
if (xs[i] == 4) {
foo( xs[i] );
break;
};
}
Sentinels are quite common for parsing unknown-length dynamically-allocated arrays of pointers, with the sentinel being NULL.
Anyway, my main point is that preventing accidental buffer overruns probably has a higher priority compared to code prettiness :)
I am new to SO - I have a question which I was asked in interview and which for life of me I am not able to wrap my head around. I can solve it with while/for loop but interviewer specifically asked not to use them I even discussed with few friends of mine but unable to solve it. If someone can provide pointers.
Question is:
for given array
s[] = {5,1,0,4,2,3}
length of array is not given.
If length of array is 5 content
is guaranteed to be between 0 to 5.
There is no repetition of
numbers.
Sample example length(s, 3)
- a[3] = 4 , a[4] = 2, a[2] = 0, a[0] = 5, a[5] =3 returns length of 4 .
For given condition write subroutine int length (s, 3) - to find the number of steps it takes to find given value -
Additional conditions
You cannot use any loop statements like for, while and so on -
You cannot use any global or static variables.
You cannot call other routines inside this routine
You cannot modify given function parameters - it stays length (s, n) only
You cannot change original array too
Alternative solution that does not modify the array at all, but hides an extra parameter inside top 16 bits of x:
int length(int *s, int x){
int start = (x >> 16) - 1;
if (start < 0)
start = x;
if (s[x] == start)
return 0;
return 1 + length(s, ((start + 1) << 16) + s[x]);
}
This will fail if there are too many elements in the array, but I suspect any other recursive solution is likely to hit a stack overflow by that point in any case.
I think I found the answer eventually no I didnt crack it but found it online :) .. Here is the solution
int length(int * s, int x){
if(s[x] < 0){
return -1;
}
else{
s[x] = -s[x];
int len = length(s, -s[x]);
s[x] = -s[x];
return len + 1;
}
}
i don't think it contradicts with any of your conditions. i just didn't use the array as a parameter (that isn't a problem actually, you can modify it yourself)
int s[] = {5,1,0,4,2,3};
bool col[100]; ///to check cycle
int rec(int n)
{
if(col[n])return 0;
col[n]=true;
int sum=0;
sum = 1+rec(s[n]);
return sum;
}
The interviewer is probing your understanding of algorithms and programming paradigms, trying to understand your training, background, and depth. The interviewer has a challenging task; identifying capable developers with minimal evidence. Thus the interviewer presents a constructed problem that (they hope) elicits the desired knowledge (does candidate X know how to solve problem Y, or understand concept Z) perhaps because the interviewer believes the desired answer indicates the candidate knows the expected body of knowledge.
Modern languages provide several repetition structures (commands, statements), some which pre-test (check condition before entering statement-block), and some which post-test (check condition after performing statement block at least once). Here are examples,
Pre-test
while(condition) statement-block
for(initializer;condition;step) statement-block
Post-test
do statement-block while(condition)
repeat statement-block until(condition)
do statement-block until(condition)
These can all be written as conditional (choice) structures with branching (goto),
Pre-test
label:
if(condition)
statement-block;
goto label;
else
nil;
endif
Post-test
label:
statement-block;
if(condition)
goto label;
endif
You can also use recursion, where you call the same function as long as condition holds (or until condition met, depending upon positive or negative logic),
Pre-test
function recurse(args)
if(condition)
statement-block
recurse(revised args);
endif
return
end #function
Post-test
function recurse(args)
statement-block
if(condition)
recurse(revised args);
endif
return;
end
You would learn about recursion in an algorithms, or perhaps a computability course. You would learn about conditional branching in a compiler, high performance computing, or systems class. Your compiler course might examine techniques for detecting 'tail-recursion', and how to rewrite the function call into a loop.
Here is the problem, restated,
given array, s[] = {5,1,0,4,2,3}
array length unknown
content between [0 .. length], not repeated, no duplicates
write subroutine which provides the number of steps to find given value
That is,
int length( array s, int member ) --> position
Examine the conditions (constraints) on the problem,
Array length unknown - Solution must work for variable range of inputs
Cannot use loop statements (for, while, etc) - This suggests either the interviewer wants conditional branch or recursion.
Cannot use global or static variables - Does this suggest interviewer wants a recursive/functional-programming solution? Conditional-branch also provides this.
Cannot call other routines inside this routine - Does interviewer mean functions other than same function, or call any function (what does interviewer mean by 'other').
Cannot modify function parameters, stays length(s,n) - Declaring local (stack) variables is allowed. This could mean pass by value, make a local copy, etc. But not destructive modifications.
Cannot change original array - Definitely no destructive modifications. Possible 'hint' (ok to make local copy?), or further indication that you should use conditional-branch?
Here are two solutions, and a test driver (note, I have named them lengthi, iterative, and lengthr, recursive).
#include <stdio.h>
/* conditional branch */
int lengthi( int s[], int member )
{
int position=0;
AGAIN:
if( s[position] == member ) return(position);
++position;
goto AGAIN;
return(-1);
}
/* recursive */
int lengthr( int s[], int member )
{
if( s[0] == member ) return(0);
return( 1+length(s+1,member) );
}
int
main(int argc,char* argv[])
{
int s1[] = {0,1,2,3,4,5,6,7,8,9};
int s2[] = {1,2,3,4,9,8,7,6,0,5};
int s3[] = {2,4,6,8,0,1,3,5,7,9};
printf("%d at %d\n",3,lengthr(s1,3));
printf("%d at %d\n",3,lengthr(s2,3));
printf("%d at %d\n",3,lengthr(s3,3));
printf("%d at %d\n",3,lengthi(s1,3));
printf("%d at %d\n",3,lengthi(s2,3));
printf("%d at %d\n",3,lengthi(s3,3));
}
Since we are supposed to find the number of steps (iterations, function calls), that is asking for the ordinal position in the list, not the C index (zero based) position.
This is an interview question, and not a programming problem (per se), so probably better suited for the Programmers.stackexchange site. I might give the interviewer an entertaining answer, or their desired answer.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I am a new student to C programming and i was trying to see if i can understand well the do while loop. I have a tiny bit memory of the for loop but nothing on the do while loop.
I want to see examples and also learn on how to use them. I will
really love it if you dont bash me because i am new and have no other
platform to ask from. Our teacher said we should read ahead and that
is exactly what i am trying to do. Thank you.
I have tried online videos but i am not good in them. Thanks
Well: Hope the following helps you.
Unlike for and while loops, which test the loop condition at the top
of the loop, the do...while loop in C programming language checks its
condition at the bottom of the loop
A do...while loop is similar to a while loop, except that a do...while loop is guaranteed to execute at least one time.
A sample syntax would be:
do{
statement(x);
}while(condition);
Notice that the conditional expression appears at the end of the loop, so the statement(x) in the loop execute once before the condition is tested.
If the condition is true, the flow of control jumps back up to do, and the statement(x) in the loop execute again. This process repeats until the given condition becomes false.
Try this code and you will be good:
#include <stdio.h>
int main ()
{
// local variable definition
int x = 5;
// do loop execution
do
{
printf("value of x: %d\n", x);
x = x + 1;
}while( x < 10 );
return 0;
}
Here is a great way to understand the difference between a while loop and a do / while loop.
(But first, a side note: do / while are actually pretty rare in practice. You don't usually need them. The good, clean algorithms that you usually want to write, that naturally do the right thing for any input, and that properly do nothing when they're given no input, usually end up being written in terms of while loops. "while (there's something to do) { do something; }". But there are times when a do / while loop is exactly what you want.)
Suppose you're trying to convert an integer to a string, that is, construct a string containing the base-10 representation of an integer, basically just like printf %d. This is a standard exercise. Here is the standard solution:
void itoa(int n, char buf[], int size)
{
char *p = buf;
while(n > 0)
{
if(p >= &buf[size-1]) abort();
*p++ = n % 10 + '0';
n /= 10;
}
*p = '\0';
strrev(buf);
}
We use the % operator to generate digits, filling them into a destination array. The standard additional difficulty with this technique is that it generates digits in the wrong order; the standard way to compensate for that is with a string-reverse function. Anyway, here's the string-reverse function and a little test driver so you can try it out:
void strrev(char str[])
{
char *p1 = str, *p2;
for(p2 = str; *p2 != '\0'; p2++)
;
p2--;
while(p1 < p2)
{
char tmp = *p1;
*p1 = *p2; *p2 = tmp;
p1++; p2--;
}
}
So go ahead and type that code in, and compile it, and play with it. You should discover that it works fine for just about any number you can type into it, except...
It does not work for zero! If you type in 0, the test while(n > 0) fails immediately, and we make zero trips through the loop, and we compute no digits, and we generate an empty string.
So: suppose you want to generate the string "0" for zero. In other words, suppose we always want to make at least one trip through the loop, even when n is initially 0. This sounds like a job for... do / while! Just rewrite the loop like this -- same body, same condition (n > 0), but with the condition tested at the bottom of the loop instead of at the top:
do {
if(p >= &buf[size-1]) abort();
*p++ = n % 10 + '0';
n /= 10;
} while(n > 0);
Try this, and you'll see that it generates the same output for positive numbers, and now it works as expected for 0, too.
(But, in case you were wondering, the other thing this simple code doesn't handle, and the do/while modification does nothing to address, is negative numbers.)
For years, I've gotten in to the habit of not using the value of a for loop iterator after the loop exits. I could have sworn that I did this, because it used to produce a compiler warning, but after I was challenged in a recent code review, I was proven wrong.
For example, I always did this (NOTE: our code standards prohibit the use of the "break" keyword):
int i, result;
bool done = false;
for (i=0; i<10 && !done; i++) {
if (some_condition) {
result = i;
done = true;
}
}
// Value of i may be undefined here
Now, obviously the result variable could be removed, if I can rely on the value of i. I thought that because of compiler optimization, you could not rely on the value of the loop iterator. Am I just remembering a phantom teaching? Or is this the standard (specifically regarding GNU C)?
There is nothing wrong in C89, C99, or C11 to access the iteration variable after the for statement.
int i;
for (i = 0; i < 10; i++) {
/* Some code */
}
printf("%d\n", i); // No magic, the value is 10
From C99, you can use also a declaration as the first clause of the for statement, and in that case of course the declared variable cannot be used after the for statement.
Different languages have different rules. In Pascal, the compiler is allowed to optimize away storing the loop index after the final increment, so it might be the first loop-terminating value or it might be the last valid value.
There are plenty of usage cases where the for loop is used for nothing else but advancing the iterator. This can be seen in some implementations of strlen (though admittedly there are other ways to do strlen), and other sorts of functions whose goal it is to find a certain limit:
/*find the index of the first element which is odd*/
for (ii = 0; ii < nelem && arry[ii] % 2 == 0; ii++);
As mentioned, the point of confusion may come from constructs where the iterator itself is defined within the for statement.
In general for statements are very very powerful, and it's unfortunate that they're usually never utilized to their full potential.
For example, a different version of the same loop can be written as follows (though it wouldn't demonstrate the safety of using the iterator):
#include <stdio.h>
int main(void)
{
int cur, ii = 0, nelem, arry [] = { 1, 2, 4, 6, 8, 8, 3, 42, 45, 67 };
int sum = 0;
nelem = sizeof(arry) / sizeof(int);
/* Look mom! no curly braces! */
for (
ii = 0;
ii < nelem && ((cur = arry[ii]) %2 == 0 ||
((printf("Found odd number: %d\n", cur)||1)));
ii++, sum += cur
);
printf("Sum of all numbers is %d\n", sum);
return 0;
}
In this particular case, it seems like a lot of work for this specific problem, but it can be very handy for some things.
Even though the value of that for loop's control variable is well defined, you might have been told to avoid using the for loop's control variable after the for loop because of the way scoping of that variable is handled, and especially because the handling has changed of the history of C++ (I know this question is tagged "C", but I think the rationale for avoiding using for loop control variable after the loop may have origins in this C++ history).
For example, consider the following code:
int more_work_to_do(void)
{
return 1;
}
int some_condition(void)
{
return 1;
}
int foo()
{
int i = 100;
while (more_work_to_do()) {
int done = 0;
for (int i = 0; i < 10 && !done; i++) {
if (some_condition()) {
done = 1;
}
}
if (done) return i; // which `i`?
}
return 1;
}
Under some old rules of scoping for the i declared in the for loop, the value returned on the statement marked with the comment "which i" would be determined by the for loop (VC++ 6 uses these rules). Under the newer, standard rules for scoping that variable, the value returned will be the i declared at the start of the function.
While I can't possibly know how your habit came to be, I can tell you how my habit to do the same did. It was by seeing code like this:
for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
if (someCondition) found = TRUE;
}
foundIndex = i-1;
Basically, code like this is written when the break keyword is disallowed by some coding rules, e.g. based on MISRA. If you don't break out of the loop though, the loop will usually leave you with an "i" which is off by one from what you care for.
Sometimes, you can even find this:
for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
if (someCondition) found = TRUE;
}
foundIndex = i;
This is just semantically wrong and can be found when the "forbid break keyword rule" is introduced into an existing code base which is not sufficiently covered by unit tests. May sound surprising, but it's all out there...
gprof is not working properly on my system (MinGW) so I'd like to know which one of the following snippets is more efficient, on average.
I'm aware that internally C compilers convert everything into pointers arithmetic, but nevertheless I'd like to know if any of the following snippets has any significant advantage over the others.
The array has been allocated dynamically in contiguous memory as 1d array and may be re-allocated at run time (its for a simple board game, in which the player is allowed to re-define the board's size, as often as he wants to).
Please note that i & j must get calculated and passed into the function set_cell() in every loop iteration (gridType is a simple struct with a few ints and a pointer to another cell struct).
Thanks in advance!
Allocate memory
grid = calloc( (nrows * ncols), sizeof(gridType) );
Snippet #1 (parse sequentially as 1D)
gridType *gp = grid;
register int i=0 ,j=0; // we need to pass those in set_cell()
if ( !grid )
return;
for (gp=grid; gp < grid+(nrows*ncols); gp++)
{
set_cell( gp, i, j, !G_OPENED, !G_FOUND, value, NULL );
if (j == ncols-1) { // last col of current row has been reached
j=0;
i++;
}
else // last col of current row has NOT been reached
j++;
}
Snippet #2 (parse as 2D array, using pointers only)
gridType *gp1, *gp2;
if ( !grid )
return;
for (gp1=grid; gp1 < grid+nrows; gp1+=ncols)
for (gp2=gp1; gp2 < gp1+ncols; gp2++)
set_cell( gp2, (gp1-grid), (gp2-gp1), !G_OPENED, !G_FOUND, value, NULL );
Snippet #3 (parse as 2D, using counters only)
register int i,j; // we need to pass those in set_cell()
for (i=0; i<nrows; i++)
for (j=0; j<ncols; j++)
set_cell( &grid[i * ncols + j], i, j, !G_OPENED, !G_FOUND, value, NULL);
Free memory
free( grid );
EDIT:
I fixed #2 form gp1++) to gp1+=ncols), in the 1st loop, after Paul's correction (thx!)
For anything like this, the answer is going to depend on the compiler and the machine you're running it on. You could try each of your code snippets, and calculating how long each one takes.
However, this is a prime example of premature optimization. The best thing to do is to pick the snippet which looks the clearest and most maintainable. You'll get much more benefit from doing that in the long run than from any savings you'd make from choosing the one that's fastest on your machine (which might not be fastest on someone else's anyway!)
Well, snippet 2 doesn't exactly work. You need different incrementing behavior; the outer loop should read for (gp1 = grid; gp1 < grid + (nrows * ncols); gp1 += ncols).
Of the other two, any compiler that's paying attention will almost certainly convert snippet 3 into something equivalent to snippet 1. But really, there's no way to know without profiling them.
Also, remember the words of Knuth: "Premature optimization is the ROOT OF ALL EVIL. I have seen more damage done in the name of 'optimization' than for all other causes combined, including sheer, wrongheaded stupidity." People who write compilers are smarter than you (unless you're secretly Knuth or Hofstadter), so let the compiler do its job and you can get on with yours. Trying to write "clever" optimized code will usually just confuse the compiler, preventing it from writing even better, more optimized code.
This is the way I'd write it. IMHO it's shorter, clearer and simpler than any of your ways.
int i, j;
gridType *gp = grid;
for (i = 0; i < nrows; i++)
for (j = 0; j < ncols; j++)
set_cell( gp++, i, j, !G_OPENED, !G_FOUND, value, NULL );
gprof not working isn't a real
excuse. You can still set up a
benchmark and measure execution
time.
You might not be able to measure any
difference on modern CPUs until
nrows*ncols is getting very
large or the reallocation happens
very often, so you might optimize the wrong part of your code.
This certainly is micro-optimization as the most runtime will most probably be spent in set_cell and everything else could be optimized to the same or very similar code by the compiler.
You don't know until you measure it.
Any decent compiler may produce the same code, even if it doesn't the effects of caching, pilelining, predictive branching and other clever stuff means that simply guessing the number of instructions isn't enough