Shorthand Way of Conditional Assignments - c

Is there a shorthand way of writing equivalent code to:
if (variable > max){
variable = max;
}else if (variable < -max){
variable = -max;
}
I know I can use ternary operators to get code like this:
variable = variable > max ? max : variable;
variable = variable < -max ? -max : variable;
Question:
But is there an even shorter version of this code?
Note: Not nested ternary operators, either.
P.S.: This isn't necessarily a language-specific question, but I'm writing my code in C so I'd like to have answers that can work in that language.

You could probably use this construct:
if (abs(variable) > max) {
variable = sgn(variable) * max;
}
There is no sgn(x) function in standard C library, but it is easy enough to implement it using arithmetic operations:
#define sgn(x) (((x) > 0) - ((x) < 0))
Here is an working example:
#include <stdio.h>
#include <math.h>
#define sgn(x) (((x) > 0) - ((x) < 0))
int main(void)
{
int variable = -100, max = 20;
if (abs(variable) > max) {
variable = sgn(variable) * max;
}
printf("%d\n", variable);
return 0;
}
Note, that it will not work for INT_MIN on systems, that use two's complement representation, because -INT_MIN cannot be represented as int (kudos for #M.M).

You can nest conditional operators:
variable = variable > max ? max : (variable < -max ? -max : variable);
IMHO this is almost unreadable, so I don't recommend it.

Related

Very strange mistake in C when working with arrays

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.

Why the global variable gets doubled twice?

#include <stdio.h>
#define abs(x) (x < 0 ? -x : x)
int x;
int doubleGlobalX()
{
x*=2;
return x;
}
int main()
{
scanf("%d",&x);//5
printf("%d\n",abs(doubleGlobalX()));//20
return 0;
}
When the input is 5 the output of this code is 20. When input is 15 the output is 60. Don't know why it doubles the global twice.
#define abs(x) (x < 0 ? -x : x)
Macros perform textual substitution. Defining an abs() macro like this is a well-known trap because it ends up evaluating x twice. If it were a function it'd be fine, but as a macro it's a problem. abs(doubleGlobalX()) expands to:
doubleGlobalX() < 0 ? -doubleGlobalX() : doubleGlobalX()
It ends up calling doubleGlobalX() twice which causes x to be doubled twice.

Do While Loop in C (CS50)

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).

Defining equations with a constant

I am trying to define a constant that would have the following in it
ch[co].gold < 10000*100
how can I make it, something like
define x = ch[co].gold < 10000*100;
so that every time I write
if (x) {say(cn,"You need 10 000 gold coins");}
Or that is not possible?
Function:
int x(int val) {
return (val < 10000 * 100);
}
Usage
// ...
if (x(ch[co].gold)) {
printf("You need 10 000 gold coins.\n");
}
// ...
Well, here is my solution
struct s {
int gold;
};
const int co = 2;
struct s ch[] = {112,2321,3234};
#define x() ch[co].gold < 10000*100
int main(){
if (x()) {
}
return 0;
}
Is this what you are expecting?
#define x (ch[co].gold < 10000*100)
Add this line of code before the place you use it, usually it's just below the #includes.
Conventionally, we use capital letters with clearer meanings instead of x.
#define is simply text substition performed by the preprocessor.
To do what you say you want use the following #define:
#define x ch[co].gold < 10000*100
Every time the preprocessor encounters the symbol x it will replace it with ch[co].gold < 10000*100.
I think that what you really want is to make it a proper function as suggested by pmg. It is a wiser choice.

Why the "if constraint" not working in the code?

The first if statement is not working and code is running without considering the constraint.
#include <stdio.h>
int main(void)
{
int with;
int inacbal;
float acleft;
scanf("%d",&with);
scanf("%d",&inacbal);
if(0<=with<=2000&&0<=inacbal<=2000) //this statement not working
{
if((with%5)==0)
{
if(inacbal>with)
{
acleft=(float)inacbal-(float)with-0.50;
printf("%.2f",acleft);
}
else
printf("%d",inacbal);
}
else
printf("%d",inacbal);
}
return 0;
}
Even on inputting value greater than the constraint relation the loop is running.
While mathematicians sometimes use the shorthand a < b < c, the C language is a bit more strict.
You must rewrite:
if(0<=with<=2000&&0<=inacbal<=2000)
to be something like:
if((0 <= with) && (with <= 2000) && (0 <= inacbal) && (inacbal <= 2000))
What you have actually is valid C but it does not do what you would normally expect. The expression 1 < 2 < 3 actually means: calculate 1 < 2 (giving the integral truth value 0 or 1 for false and true respectively) then comparing that against 3.
If you want to use a shorter form, you can use something like:
#define between(a,b,c) (((a) <= (b)) && ((b) <= (c)))
:
if (between (0, with, 2000) && between (0, inacbal, 2000))
though you need to watch out for duplicated side effects if you use terms such as a++ when using it. A safer approach may be replacing the macro with something along the lines of:
int between (int a, int b, int c) {
return (a <= b) && (b <= c);
}
You cannot compare avariable to two other values like that. You should separate the conditions:
if ((0<=with) && (with<=2000) && ( 0<=inacbal) && (inacbal<=2000))
The statement
if(0<=with<=2000&&0<=inacbal<=2000)
is not checking variables between the given range. These comparisons doesn't have mathematical meaning in C.
Compiler should raise a warning
[Warning] comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses]
To check whether a value is between a given range or not you need to use && operator. You need to rewrite it as
if(0 <= with && with <=2000 && 0 <= inacbal && inacbal <=2000)

Resources