I was doing some exercises on codewars, and had to make a digital_root function (recursively add all digits of a number together, up untill there's only one digit left).
I was fairly confident that I did it right, but for some reason my while-loop never broke, even though my prints showed that len was 1.
#include <stdio.h>
#include <string.h>
int digital_root(int n) {
char number[10];
sprintf(number, "%d", n);
int len = strlen(number);
printf("Outer print: %s %d %d\n", number, n, len);
int sum = 0;
while(len > 1)
{
sum = 0;
for(int i = 0; i<len; i++)
{
sum += number[i] - '0';
}
sprintf(number, "%d", sum);
int len = strlen(getal); //!!!
printf("Inner print: %s %d %d\n", number, sum, len);
}
return sum;
}
It took me a long time to figure out what was wrong. I noticed that I copy pasted the 'int' keyword when I recalculated the len in the while loop (line marked with !!!). When I removed that (because it was not needed to redefine it as an int, it already was), everything suddenly worked like it was supposed to.
This kinda confused me. Why would this matter? I understand that redefining it is bad practice, but I don't get how this would result in the while-loop not breaking?
The used compiler is Clan3.6/C11.
(Ps. When I tried the same code in TIO, it worked in both cases...)
You're not redefining an existing variable, you're defining a new variable.
Consider this example:
#include <stdio.h>
int main(void) {
int x = 42;
printf("Outside, start. x (%p) = %d\n", (void *)&x, x);
{
printf("Inner block, start. x (%p) = %d\n", (void *)&x, x);
int x = 123;
printf("Inner block, end. x (%p) = %d\n", (void *)&x, x);
}
printf("Outside, end. x (%p) = %d\n", (void *)&x, x);
return 0;
}
Sample output:
Outside, start. x (0x7ffd6e6b8abc) = 42
Inner block, start. x (0x7ffd6e6b8abc) = 42
Inner block, end. x (0x7ffd6e6b8ab8) = 123
Outside, end. x (0x7ffd6e6b8abc) = 42
[Live demo]
This program outputs the memory address and value of x. Most uses of x refer to the outer variable declared at the beginning of main. But within the inner block, after int x = 123;, all occurrences of x refer to a second, separate variable that happens to also be called x (but is otherwise independent).
When execution leaves the inner block, the outer x variable becomes visible again.
This is also referred to as shadowing.
In your code, the outer len is never modified, so while(len > 1) is always true.
By the way, shadowing is a very common concept in most languages that support block scoping:
Perl
JavaScript
Haskell
Common Lisp
Your second int len creates a second, parallel, variable that goes away at the end of the {} block. The original len then returns to life, completely unchanged. Without the second int the original variable is changed. With it the original len is effectively an unchanged constant and infinite loop.
Related
I programmed a simple clock and found that while hours and seconds are okay, minutes are not if I declare the variables on a single line (minutes starts at 16 and not at 0 as expected).
The problem is solved if I declare the variables on separated lines. I'm still curious though, anybody knows why?
Here's the code:
#include <stdio.h>
#include <windows.h>
//h=hours, m=minutes, s=seconds.
int main(){
int h, m, s = 0; //THIS IS THE LINE: WHY "m" STARTS AT 16 AND NOT 0?
int delay = 1000;
while(1){
s++;
if(s>59){
m++;
s=0;
}
if(m>59){
h++;
m=0;
}
if(h>12){
h=1;
}
printf("\n %02d:%02d:%02d", h, m, s);
Sleep(delay);
system("cls");
}
}
Your variables are uninitialized - they can take any value, and using them before initialization is (usually) indeterminate behavior.
A line of code like:
int h, m, s = 0;
does not define each variable to be zero - only the third one. It is equivalent to:
int h;
int m;
int s = 0;
To fix, initialize all of your variables as zero:
int h = 0, m = 0, s = 0;
You haven’t initialized h or m, so their initial values might whatever was in that memory already, or something else entirely. C does not automatically initialize variables to 0.
When you declare multiple variables on one line, the value only applies to the variable it comes after. So
int a, b, c = 5;
only sets c to 5, while
int a = 1, b = 2, c = 3;
initializes all of them. In your case, you just need to use
int h = 0, m = 0, s = 0;
You compiler likely has an option for warnings (-Wall in gcc), which should warn you about using an uninitialized variable.
int h, m, s = 0; // THIS IS THE LINE: WHY "m" STARTS AT 16 AND NOT 0?
Well, I guess, because C is not English. The "= 0" part applies just to that one variable name s, not to the whole line.
Although, you know, English doesn't necessarily apply modifiers to the whole line, either. If I say "There were three people standing on the streetcorner: A man, a woman, and a boy named Brad", you would not infer that the man and the woman were named Brad, too, would you?
[Footnote: Now I've got that old song "Me and you and a dog named Boo" stuck in my head. :-) ]
Can anyone explain this?
Consider this program. We write modify dest[10] intentionally in order to see j value modified.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char source[] = "Hello";
int j = 100;
char dest[10];
dest[12] = 'A';
printf("j = %d \n", j);
fflush(stdout);
printf("j = %d \n", j);
fflush(stdout);
printf("*j = %p \n", &j); // comment this line to get another result!
return 0;
}
output :
j = 4259940
j = 4259940
*j = 0x7ffcc4cdef74
But if we comment the line displaying j varibale address printf("*j = %p \n", &j); we get:
j = 100
j = 100
It is like j is stored elsewhere, not just after dest variable as in the first example.
Any explanation?
Where and whether to store the objects j and dest and how to handle the out-of-bounds access in dest[10] is the compiler’s choice. Modern compilers do many complicated things to optimize programs. When you omit the statement that prints the address of j, the compiler makes different choices, and these produce different results.
A variable is not required to have any storage address if the address is not taken.
The compiler is free to only hold the value in a register or completely remove it via optimization mechanisms and only use the constant value 100 directly.
You might check if you corrupted dest instead when j is not stored on the stack.
Please read the code below.
#include <stdio.h>
int main(void)
{
char* a[4];
int i=0;
while(i<3)
{
char b[50];
scanf(" %s",b);//Assume user enters one two three
a[i++]=b;
}
i=0;
while(i<3)
printf(" %s ",a[i++]);//Why does it always print three three three
return 0;
}
Clarify the following:
Is it that b gets allocated same 50 bytes in memory each time so that all the elements of array a point to same 50 bytes and thus we get only three printed three times(i.e. what's entered last)
Since after the completion of while, array b can be removed very well but no it remains there every single time printing only three's. Why?
Is it not at all a coincidence that this code prints only three's when it could print one two three, one three three as well. What's wrong?
I know the question is very wrongly put. Forgive me for that. I am new here.
QUESTION #1:
The variable b is a variable that is strictly local to the
while loop.
Therefore, do not reference via a pointer any memory formerly used by b outside (after) the while loop.
Storage for b will be reallocated 3 times.
At the end of the while loop, b will go out of scope.
QUESTION #2:
After the while loop, a is not a valid pointer anymore
because a was assigned to point to b,
and b has gone out of scope after the while loop.
NEVERTHELESS, the memory allocated to b may still
not have been modified. You cannot predict what the value of dereferencing a will be after the while loop - since a is only assigned based on b.
QUESTION #3:
(Please see #2) The code that is dereferencing a after the while loop is using a stale pointer - I would not rely on the output.
The code in the question exhibits undefined behaviour because the second loop attempts to access the data that was only valid in the first loop. Therefore, there is very little that can usefully be said.
The question title is "does a variable declared in a loop get the same address each time the loop executes". Here's a proof by counter-example that the address of the variables in a loop need not always be the same, even in a single invocation of a non-recursive function.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
srand(time(0));
for (int i = 0; i < 3; i++)
{
int n = rand() % 30 + 5;
char a[n];
for (int j = 0; j < n - 1; j++)
a[j] = rand() % 26 + 'a';
a[n-1] = '\0';
printf("a: %p %2d [%s]\n", (void *)a, n, a);
char b[50+n];
scanf("%s", b);
printf("b: %p %2d [%s]\n", (void *)b, n+50, b);
}
return 0;
}
On a Mac running macOS Sierra 10.12.4 (using GCC 7.1.0), one run of the program (lv83) produced:
$ ./lv83 <<< 'androgynous hermaphrodite pink-floyds-greatest-hits-dark-side-of-the-moon'
a: 0x7fff507e53b0 23 [sngrhgjganmgxvwahshvzt]
b: 0x7fff507e5360 73 [androgynous]
a: 0x7fff507e53c0 9 [wblbvifc]
b: 0x7fff507e5380 59 [hermaphrodite]
a: 0x7fff507e53b0 26 [pqsbfnnuxuldaactiwvjzvifb]
b: 0x7fff507e5360 76 [pink-floyds-greatest-hits-dark-side-of-the-moon]
$
The address at which the two arrays are allocated varies depending on how big they are. By using different formulae for the sizes of the two arrays, the base addresses could be tweaked. It looks as though the compiler rounds the base address of the arrays to a multiple of 16.
in learning quick union algorithm, i have met these two statements
for(i=p; i!=id[i]; i=id[i]);
for(j=q; j!=id[j]; j=id[j]);
since I only learned for loop to be something like
for(i=0; i<100; i++)
I don't know the difference between the two statements and the following statements
i=p; i=id[i];
j=q; j=id[j];
i have no idea why the results are different?
thanks
I want to ask why
#include <stdio.h>
#define N 10000
int main()
{
int i, j, p, q, id[N];
for(i=0; i<N; i++) id[i]=i;
while(scanf("%d %d\n", &p, &q)==2)
{
for(i=p; i!=id[i]; i=id[i]);
for(j=q; j!=id[j]; j=id[j]);
if(i==j) continue;
id[i]=j;
printf(" %d %d\n", p, q);
}
}
is different from
#include <stdio.h>
#define N 10000
int main()
{
int i, j, p, q, id[N];
for(i=0; i<N; i++) id[i]=i;
while(scanf("%d %d\n", &p, &q)==2)
{
i=p; i=id[i];
j=q; j=id[j];
if(i==j) continue;
id[i]=j;
printf(" %d %d\n", p, q);
}
}
I have tested the results, that's why I am confused
i=p;
Sets i to the value of a variable p (defined and initialized elsewhere in the program)
i=id[i]
Sets i to the value of the i th element of the array id (defined and initialized elsewhere in the program)
for(i=p; i!=id[i]; i=id[i]);
Loop initializes i to the value of p, executes the statements inside the for loop once if i is not equal to the i'th value of the array id, and then stops.
Further explaination:
assuming some values for the variables:
int p = 4;
int i;
int id[5] = {1,2,3,4,5};
for(i=p; i!=id[i]; i=id[i]) {
printf("Loop executed!\n");
}
Output:
Loop executed!
And then a segmentation fault.
What happens:
i is set to 4, then compared to id[4]. This is unequal, thus the loop is triggered. After that it sets i to 5. Now it tries to compare id[5] to i. This is disallowed because id only has space for 5 elements and indexes start at 0.
When looking at non-standard for "loops" like this, it helps to convert your for loops into while loops. (that is all they really are)
To do that, remember that a for loops consists of three parts separated by semicolons. The initialization part, the conditional part, and the update or increment part:
for(initialize statement; boolean loop conditional; update/increment statement);
The initialize statement is executed before your loop, the loop conditional is evaluated to determine whether the loop continues, and the update/increment statement is executed as the end of the loop.
Your first example:
for(i=p; i!=id[i]; i=id[i]);
As a while loop, looks like..
i = p
while(i!=id[i]) {
i = id[i];
}
Your second example:
for(j=q; j!=id[j]; j=id[j]);
As a while loop, looks like..
j = q
while(j!=id[j]) {
j = id[j];
}
Once they're written like this, it's easier to tell what's going on.
We're initializing the loop variable to one of two values, p or q.
Then, we're looking into the array "id" at the location specified by the loop variable and updating the loop variable with it. This has the effect of looking up in the array the next loop variable. In other words, each slot in the array contains the next value to jump to.
The conditional checks to see whether the destination is the same as the current location. That is to say, if we are "told" to jump to the location we're already in.
The difference between you two loops is only the initialization value. The first one initializes to p where the second initializes to q.
It may be helpful to manually jump through a simple cases such as..
p=0
q=1
id = {1,2,2}
p=2
q=1
id = {0,0,2}
I understand this is part of the basic stuff, but i am stuck :-(
Can someone please help me?
Program 1:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=1,b=2,c;
c=(a+b)++;
}
Why is the output an error? lvalue required?
Program 2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p1="name";
char *p2;
p2=(char*)malloc(20);
memset(p2,0,20);
while(*p2++=*p1++);
printf("%s\n",p2);
}
Why is the output, an empty string? And if i reverse the order of increment, that is: while(++*p2=++*p1);, why the lvalue error comes?
For the first question,(a+b)++ means "increment the value of a+b by 1".
You cannot increment a+b, though, for it's not a variable. What would you expect to happen in the following code?
int a = 1, b = 2;
printf("a = %d, b = %d, a+b = %d\n", a, b, a+b);
(a+b)++;
printf("a = %d, b = %d, a+b = %d\n", a, b, a+b);
Clearly the first printf should print
a = 1, b = 2, a+b = 3
But what about the second one?
a = ?, b = ?, a+b = 4
It's not clear what a or b should be if we increment the sum.
As for the second question, remember that you're changing p2 when you copy over the data, so when you ask to print out what it's pointing at, it's pointing at the end of the string, not the beginning.
An easier way to do the string copy would be to use strcpy, like so:
strcpy(p2, p1);
Note, this is only safe because you know that the size of the string in p1 isn't greater than the size of p2. If you're not sure about the size of the string (for instance, if you get the string from user input), you'll need to be careful, as is outlined on Wikipedia.
As for why while(++*p2=++*p1); doesn't work, while while(*p2++=*p1++); does:
Postfix-++ has higher precedence than *. This means, *p2++ means *(p2++).
So
*(p2++) = something;
is the same as
*p2 = something;
p2 += 1;
Meanwhile, ++*p2 means ++(*p2), or "whatever p2 points to, incremented by one".
Again, you get the problem, if you say:
int a = 5, *p2 = &a;
++*p2 = 10;
printf("a = %d\n", a);
What would you expect this to print? If anything, it should print 9, because you're telling the compiler that *p2+1 = 10.
You can't expect a C-compiler to solve that equation, however, so in order to keep the language simple and efficient, this sort of thing is forbidden.
c=(a+b)++;
a+b is not a lvalue - how would you want to assign something to the result of an addition (a rvalue) - and the ++/-- operators do assign the new value.
while(*p2++=*p1++);
You p2 points at the \0 at the end of the string. You need to store the original address p2 points to before your loop:
char *p3 = p2;
while(*p2++=*p1++)
;
printf("%s\n",p3);
c=(a+b)++;
The ++operator doesn't work with temporary variables. (a+b) forms a temporary.
while(*p2++=*p1++);
You're incrementing p2 here. After the loop, p2 no longer points to the beginning of the memory block returned by last malloc() call.
If you look at the while loop:
while(*p2++ = *p1++);
remember that an expression is "True" in C if it is non-zero, and false if it is zero. Even though the loop has no body, the flow of control is still:
check condition -> execute statement in body --> check condition
and here, "check condition" means "evaluate the statement and see if it is non-zero".
The while loop continues to operate until p1 points to zero, at which point control passes to printf.
e.g.
int main()
{
int array1[] = {2, 3, 1, 0, 3, 5};
int array2[20];
int * p2 = (int *)memset(array2, 0, 20*sizeof(int));
int * p1 = array1;
while(*p2++ = *p1++) {
printf("In while: p1 is %d\n", *p1);
}
printf("Out of while: %d\n",*p2);
return 0;
}
yields:
In while: p1 is 3
In while: p1 is 1
In while: p1 is 0
Out of while: 0