I'm a beginner on C and I don't understand all features of this beautiful language yet.
So, I have a very strange problem which doesn't affect my solution at all, I anyway get the right result.
So, the task is:
Given an array of integers.
Return an array, where the first element is the count of positives numbers and the second element is sum of negative numbers. 0 is neither positive nor negative.
If the input is an empty array or is null, return an empty array.
Example:
For input [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15], you should return [10, -65].
My solution:
void count_positives_sum_negatives(
int *values, size_t count, int *positivesCount, int *negativesSum)
{
while(count-- > 0 ? (*values > 0 ? ++*positivesCount : (*negativesSum += *values)) + values++ : 0);
}
'count' contains size of array
But it gives me this "error" or "warning", which strangely doesn't affect my program at all:
solution.c:6:94: warning: unsequenced modification and access to 'values' [-Wunsequenced]
while(count-- > 0 ? (*values > 0 ? ++*positivesCount : (*negativesSum += *values)) + values++ : 0);
~~~~~~ ^
1 warning generated.
How do I fix this?
FIXED:
while(count-- > 0 ? (*values > 0 ? ++*positivesCount : (*negativesSum += *values)) + 1 : 0) values++;
You write (*values...) + values++. There's no sequence point between the operands of +, so reading from (ie: using values in the expression *values) and writing to (ie: updating values in values++) is undefined behavior.
To fix, simply write the code more simply, using multiple statements and expressions rather than try to one-line it.
For example, I might write it like this:
typedef struct stats_s {
int positive_counts;
int64_t negative_sum;
} stats_s ;
stats_s get_stats(const int *xs, size_t count) {
stats_s s = {0, 0};
for (size_t i = 0; i < count; i++) {
if (xs[i] > 0) s.positive_counts++;
else s.negative_sum += xs[i];
}
return s;
}
On your question
"But it gives me this "error" or "warning", which strangely doesn't affect my program at all".
It's a warning. Usually when you get a warning, you have a problem in your code. In this case you have Undefined Behavior (UB on StackOverflow).
For your specific question ("How do I fix this?"), the answer is "don't write such monstrosity". Avoid one liners.
But, probably you want to know where the problem is. The problem is that in this sum
(*values > 0 ? ++*positivesCount : (*negativesSum += *values)) + values++
~~~~~~ ^
you can legally evaluate values++ before or after *values. So, depending on the compiler feeling for this, it can generate different machine code (and behavior) for the same source code. It's usually possible to observe this, by changing the optimization levels or in MSVC switching between Debug and Release mode.
What did you expect? values++ to happen after the first term is evaluated? Then put it in another statement.
On the problem statement
In C language you cannot "return an array". So it's impossible to fulfill the request. Your code lacks all the checks required by the problem statement. You also didn't reset the accumulator variables.
If we assume that "return an array" = "return an allocated piece of memory able to contain two numbers or NULL to indicate an empty array" (which is totally arbitrary), this could be a solution:
int *count_positives_sum_negatives(int *values, size_t count)
{
if (values == NULL || count == 0) {
return NULL;
}
int *ret = calloc(2 * sizeof(int));
for (size_t i = 0; i < count; ++i) {
if (values[i] > 0) {
ret[0] += 1;
}
else {
ret[1] += values[i];
}
}
return ret;
}
I found the solution, just move values++ to the body of the loop:
#include <stddef.h>
void count_positives_sum_negatives(
int *values, size_t count, int *positivesCount, int *negativesSum)
{
while(count-- > 0 ? (*values > 0 ? ++*positivesCount : (*negativesSum += *values)) + 1 : 0) values++;
}
So no more undefined behaviour.
Related
I have done some experiments using expressions. When I am working with some code, I found this unusual thing.
First trial
int i = 3;
int j = 5;
j >= i;
printf("Output1 = %d\n", i);
printf("Output2 = %d", j);
when I compiled this code I got error messge.
Second trial
int i = 3;
int j >= i;
printf("Output1 = %d\n", i);
printf("Output2 = %d", j);
return 0;
This also give me an error message.
Third trial
int i = 3;
int j = 5 >= i;
printf("Output1 = %d\n", i);
printf("Output2 = %d", j);
1) If i <= 5 output will be
Output1 = 3
Output2 = 1
2) If i > 5 output will be
Output1 = 3
Output2 = 0
Why my first trial and second trials give errors and third one compiled unharmly?
I need some explanation.
Converting comments into an answer.
In the first trial, the statement j >= i; has no effect on the computation so it is ignored by the compiler. Your compiler settings might convert that warning into an error, which is a Good Thing™.
The second trial is an outright syntax error. You can't use a comparison in place of an initializer.
The third trial is fine and gives the expected results. The value of a comparison such as 5 >= i is either 0 if the comparison is false or 1 if the comparison is true.
But in the third one, there is also a comparison in the initializer but it didn't give an syntax error or compile error
In the third one, you have:
int j = …something…;
which is a valid initializer (because the …something… is a valid comparison). Initializers start with an = symbol. In the second, you have nt j >= i; — this does not start with = and hence is invalid. Note that trying int j == i; would also be invalid; the symbol is == and not =.
See C11 §6.7.9 Initialization and §6.7 Declarations for the syntax of intializers.
Then it means comparison which is in initializer is true it outputs 1. If false it output 0.
Yes, the comparison in the initializer generates either 1 (true) or 0 (false) — that is the value assigned to j, as your tests showed.
I am currently trying to do the CS50 course. I am trying to make a do-while loop for the 1st problem set, but i throws back an error. Help would be great, thanks!
#include <cs50.h>
#include <stdio.h>
int main(void)
{
do
{
printf("Enter a positive integer no greater than 23: \n");
int n = get_int();
}
while ( int n < 0 || int n > 23);
}
$clang mario.c
mario.c:12:13: error: expected expression
while ( int n < 0 || int n > 23);
^
mario.c:12:13: error: expected ')'
mario.c:12:11: note: to match this '('
while ( int n < 0 || int n > 23);
^
2 errors generated.
Declare n only once, outside the do-while loop:
int n = -1;
do
{
printf("Enter a positive integer no greater than 23: \n");
n = get_int();
}
while (n < 0 || n > 23);
we never define a variable inside
expressions like if or while and For loop
this is only possible in other languages like C++ |;
Your problem lies in the fact that you have:
Declared n inside your while loop (This is not allowed in C89, but is grudgingly allowed in later versions)
Declared n twice in the while section.
The idea of declaration is to show the compiler, that the variable name present is not garbage but is actually a variable. The compiler generally follows fixed rules and does not look for variable names within the while/for loops (this is for optimization purposes). Also the second time that you declared n the compiler is now confused as you have stated a variable named n already exists.
PS: I believe you wished to say that n lies within the bounds of 1 and 22 If you wished to say this the correct expression would involve AND (&&) and not OR (||) ie, while ( int n < 0 && int n > 23).
This is a recursive function that converts binary numbers (given as arrays of integers) into decimals.
double binaryToDecimalRecursive(int *binary, int len) {
if (len == 0)
return *binary;
if (len > 0) {
return (binary[0]*pow(2, len))+binaryToDecimalRecursive(binary+1, len-1);
}
}
The GCC compiler gives me the "control may reach end of non-void function" warning.
What should I fix to prevent that?
It sounds like from the comments you've changed your code to be something like this, correct?
double binaryToDecimalRecursive(int *binary, int len) {
if (len == 0)
return *binary;
if (len > 0) {
return (binary[0]*pow(2, len))+binaryToDecimalRecursive(binary+1, len-1);
}
if(len < 0) return -1;
}
The compiler doesn't recognize that it will always follow at least one of these branches. You can make it more explicit a few ways. Here is one way that should work:
double binaryToDecimalRecursive(int *binary, int len) {
if (len == 0)
return *binary;
else if (len > 0) { // Added an `else` statement
return (binary[0]*pow(2, len))+binaryToDecimalRecursive(binary+1, len-1);
}
else return -1; // Here too
}
If len < 0, the function return nothing.
You should add a clause with abort() if it's smaller than len or simply return a default value.
The compiler is complaining because you are using a non-void function, which must return a value. In C, you must have a value returned. Right now, if len is less than zero, the function would not return a value, which is not allowed.
Luckily, there is an easy fix. If there is no circumstance in which len will be less than zero, you can declare it as an unsigned int -- since an unsigned integer value cannot be less than zero, then your function will cover all possible input values of len. If len can be less than zero, then you need to abort() or return an error code to communicate to the caller of the function that invalid input was given.
The proper solution for your problem, judging from the additional details you provided in the comments, is to use an unsigned integer type and get rid of if (len > 0) check entirely
double binaryToDecimalRecursive(const unsigned *binary, unsigned len) {
return len == 0 ?
*binary :
binary[0] * pow(2, len) + binaryToDecimalRecursive(binary + 1, len - 1);
}
Use unsigned integer types whenever you can, use signed integer types only when you have to.
P.S. Why do you refer to binary[0] as *binary in one place and as binary[0] in another? Is there a reason for such stylistic mishmash in such a small function?
The compiler is complaining because you are using a non-void function and the compiler doesn't know ahead of time that either of two conditions if(len == 0) and if(len > 0) will always be fulfilled. Basically it wants you to have a default return value that doesn't depend on any conditionals, so if somehow you messed up your logic and it does pass both conditionals, the program still has something to return. i.e. you always need a return that does not depend on any conditionals.
A good fix might be to have if(len < 0){ abort() }, and take out if(len > 0) so that becomes the default condition.
double binaryToDecimalRecursive(int *binary, int len) {
if (len == 0) {
return *binary;
}
if (len < 0){
// I assume your program should never logically reach this condition, so this will be good for debugging. It will be more obvious that you reached an unwanted condition than if you returned -1
abort();
}
// since we have specified actions for len <= 0, this is the equivalent of saying len > 0 without letting the compiler complain about it
return (binary[0]*pow(2, len))+binaryToDecimalRecursive(binary+1, len-1);
}
After having done this kind of work now for almost 40 years, I would personally
do it in a simpler and more straight forward way:
Since we want an int output,
long BinarytoDecimal(int binary[], int len)
{
int i, index;
long result;
/**/ - I refer to this as the point of demarcation
result = 0;
for (i = 0, index = len; i < len; i++, index--)
{
result += (long) binary[i] * (long) pow(2,index);
}
return(result);
}
As it is difficult to tell, when someone uses recursion to implement a loop,
this should (and I emphasize should, I think) give you the same result.
matt95,
thought you might be interested. I went ahead and try both sets of code using
GCC, on windows7, under cygwin, and they both DO give identical results for the
same input. The biggest difference, of course, that the code I wrote could not
possibly cause a 'code reaches end of non-void function' error.
Blake Mitchell
I've just started learning C language. so I was doing some exercises and I had a problem with this one.
The code:
#include <stdio.h>
#include <math.h>
main()
{
double l[2];
double s, v, d;
int i;
for (i = 0; i < 3 && l[i] >= 0; i++)
{
scanf("%lf", &l[i]);
if ( l[i] < 0)
{
printf("Please type a positive value\n");
l[i+1]=-1;
}
}
if (l[0] >= 0 && l[1] >= 0 && l[2] >= 0)
{
s = 2 * ((l[0] * l[1]) + (l[0] * l[2]) + (l[1] * l[2]));
v = l[0] * l[1] * l[2];
d = sqrt(pow(l[0],2)+pow(l[1],2)+pow(l[2],2));
printf("%.2f\n%.2f\n%.2f\n", s, v, d);
}
}
The output gives right value for "s", but it changes l[2] value after "s" is stored, consequently it gives me wrong values for "v" and "d". I dont understand why l[2] is changing, anyone could help?
The definition
double l[2];
defines an array with space for 2 values of type double.
The array elements can be accessed with l[0] and l[1]. Accessing l[2] (as you are doing) is an error.
Accessing l[2] results in undefined behavior. Arrays in C are 0-indexed and l only contains two elements, i.e., l[0] and l[1]. Your loop should be:
for (i = 0; i < 2 && l[i] >= 0; i++)
And you should probably just store the size in a constant.
Your program invokes undefined behavior;
1. You are reading/writing to an unallocated memory location.
2. In your for loop you are reading uninitialized variable.
Yeah, the answer for why the value is changing would be that since the memory location at l[2] is not allocated to your program, it is probably allocated to some other program, which is changing it's value. Even when you are reading some stuff into it, some other program is trying to change it again.
Again, there is still a fair chance that the code would work fine if you are not running many processes. This is because, if the memory block containing l[2] is empty, it doesn't change over time.
However, accessing variables out of your scope is a bad practice and would give you errors in other compilers.
error is in accessing l[2]
if (l[0] >= 0 && l[1] >= 0 && l[2] >= 0)
Here is the program that I am trying to compile
#include<stdio.h>
int main()
{
int i = 0;
rec(i);
}
int rec(int i)
{
i = i + 1;
if (i == 10){return true;}
rec(i);
printf("i: %d\n", i);
}
I am getting this output
$ gcc -o one one.c
one.c: In function ‘rec’:
one.c:10:24: error: ‘true’ undeclared (first use in this function)
one.c:10:24: note: each undeclared identifier is reported only once for each function it appears in
As far I believe is Boolean true evaluates to 1 in c. If so why am I getting an error?
There are no true or false keywords in C. There are some library definitions for those values in stdbool.h (starting in C99, I think) but oft times most C programmers will just use 1 and 0.
If you don't want to use stdbool.h, or you're working on a compiler that doesn't support it, you can usually define the constants yourself with something like:
#define FALSE (1==0)
#define TRUE (1==1)
or you can use 1 and 0 directly - the method above is for those who don't want to have to remember which integer values apply to which truth values (a).
0 is false, any other value is true. So your code would look something like (fixing up the return value problem as well though I'm not sure why that's even there since it always returns true.):
#include <stdio.h>
int rec (int i) {
i = i + 1;
if (i == 10)
return 1;
printf ("i: %d\n", i);
return rec (i);
}
int main (void) {
int rv, i = 0;
rv = rec (i);
printf ("rv: %d\n", rv);
return 0;
}
which gives you:
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
rv: 1
I should also mention that recursion is a solution best used when the search space is reduced quickly, such as in a binary tree search where the search spaces halves on each recursive call. It's not usually a good fit for something where you just increment a value on each call although, in this case, it's probably okay since you limit it to about ten levels.
(a): Keep in mind the caveat that, although the given definition of TRUE will most be 1, any non-zero value will be treated so. That means that the two statements:
if (isValid)
if (isValid == TRUE)
do not mean the same thing. There are a large number of possible isValid values which will be pass the first test but fail the second. That's usually not a problem since it's almost always a bad idea to compare boolean variables with boolean constants anyway.
If you want to use true/false, you can put your compiler in C99 mode (std=c99 for GCC) (or C11), and include <stdbool.h>.
(Or you'll need to define true and false using preprocessor directives or global constants.)
You can put a simple 'if' statement in it
#include<stdio.h>
int main()
{
int i = 0;
rec(i);
}
int rec(int i)
{
i = i + 1;
if (i == 10){
return 1;
}
j = rec(i)
if (j == 1){
printf('true')
}
printf("i: %d\n", i);
}
c does not have a boolean proper. You could have a define for it though
#define true 1
#define false 0
If your C doesn't use the C99 standard, then there is neither true nor false. When designing a function that returns a boolean, i.e.
short isOdd(long);
I would use 1 and 0 to represent true and false respectively. This is ok because if you try the following snippet with i=1 (or any non-zero ints), it prints T; with i=0, it prints F.
if (i)
putch('T');
else
putch('F');
Here's the example of an function that tests if a number is odd:
short isOdd(long num)
{
return num%2; // evals to 1 if num is odd; 0 otherwise
}
Hope that helps.