Is there a function in C that returns the maximum value of a variable like this (I will name the function \"maxvalue\" in example below)?
int a;
printf(\"%d\", m
You cannot create a function that does this, but you can create some macros that do this.
If you have C11 you can use _Generic:
#define maxvalue(x) \
_Generic(x, \
char: 127, short: 32767, int: INT_MAX, \
unsigned char: 255, unsigned short: 65535, unsigned int: UINT_MAX)
If you need C89, you can do it if you can differentiate between signed/unsigned:
#define maxvalue_unsigned(x) ((1<<(8*sizeof(x)))-1)
#define maxvalue_signed(x) ((1<<((8*sizeof(x)-1)))-1)
If you're willing to require the typename (or use the GCC-specific typename
) you could use strings:
#define maxvalue_type(x) maxvalue_helper(#x "----------")
unsigned long long maxvalue_helper(const char *s) {
switch(*s){
char 'c': /* char */ return 127;
char 's': /* short */ return 32767;
char 'i': /* int */ return INT_MAX;
/* ... */
case 'u': /* unsigned */
switch(9[s]) {
case 'c': /* unsigned char */ return 255;
char 's': /* unsigned short */ return 65535;
char 'i': /* unsigned int */ return UINT_MAX;
/* ... */
Such a function is not defined by the C standard library. You can try defining a macro that calculates it:
#define MAX_VALUE(a) (((unsigned long long)1 << (sizeof(a) * CHAR_BIT)) - 1)
When using it, be careful it is assigned to a type large enough. For example:
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#define IS_TYPE_SIGNED(a) ((a-1) < 0)
#define MAX_VALUE_UNSIGNED(a) (((unsigned long long)1 << \
(sizeof(a) * CHAR_BIT)) - 1)
#define MAX_VALUE_SIGNED(a) (MAX_VALUE_UNSIGNED(a) >> 1)
#define MAX_VALUE(a) (IS_TYPE_SIGNED(a) ? \
MAX_VALUE_SIGNED(a) : MAX_VALUE_UNSIGNED(a))
int main(void)
{
unsigned int i = 0;
signed int j = 0;
printf("%llu\n", MAX_VALUE(i));
printf("%llu\n", MAX_VALUE(j));
return EXIT_SUCCESS;
}
This prints out:
4294967295
2147483647
Could easily be done using ANSI C 89:
#include<stdio.h>
#include<limits.h>
int main(void) {
printf("Max value of char: %d\n", CHAR_MAX);
printf("Min value of char: %d\n", CHAR_MIN);
printf("Max value of short: %d\n", SHRT_MAX);
printf("Min value of short: %d\n", SHRT_MIN);
printf("Max value of int: %d\n", INT_MAX);
printf("Min value of int: %d\n", INT_MIN);
printf("\n\n");
return 0;
}
Notice that you can include float.h and then use:
printf("Max value of Double: %d\n", DBL_MAX);
Though, it is less recommended.
Good luck, Ron
Looks like I have been able to come up with a relatively easy-to-use macro, SIGNED_VAR(VAR)
, to test whether the given integer variable is signed by modifying, comparing and restoring the variable's value (all that is only necessary for types smaller than int
) while avoiding undefined behavior, specifically the kinds related to signed overflow and sequence points. Or so it seems. At least, gcc (invoked with -Wall
) doesn't complain about me doing crazy things between the &&
and ||
operators, although it didn't like the same kind of things around the ternary ?:
operator.
The good thing about this macro is that it should work with C89 and C99 compilers (1LL
can be replaced with 1L
and long long
can be replaced with just long
(and "%ll"
with "%l"
, of course) if your C89 compiler does not have the extended long long
type from C99) and it also correctly supports types smaller than int
(char
and short
).
Once we know whether the variable is signed or not, constructing the minimum and maximum values is trivial and many have show how to do it. The macros VAR_MAX()
and VAR_MIN()
construct these values and return them as the longest C99 integer type, long long
. I chose to return the signed type to avoid potential overflow/UB problems when converting unsigned values to signed. Since the returned type long long
cannot represent the maximum value of unsigned long long
(ULLONG_MAX
) directly as a signed value, if that value needs to be returned, a -1 is returned instead, which after a cast to unsigned long long
will produce ULLONG_MAX
. You need to be a little careful here.
Here goes the ugliness. Hope, I didn't miss a bug.
Oh, and, of course, it's expected that the entire asymmetric range of 2's complement values is supported in signed types (e.g. min=-128, max=+127).
EDIT: I've forgot to mention that SIGNED_VAR()
expects the variable to be initialized. Otherwise, reading it can result in undefined behavior.
// file: IntVarMinMax.c
// compile: gcc -Wall -std=c99 -O2 IntVarMinMax.c -o IntVarMinMax.exe
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int SignTestTestVal;
unsigned char SignTestOriginalXchar;
unsigned short SignTestOriginalXshort;
signed char SignTestRestoreOriginalXchar(void)
{
if (SignTestOriginalXchar < SCHAR_MAX + 1u)
return (signed char)SignTestOriginalXchar;
return (signed char)(SignTestOriginalXchar - SCHAR_MAX - 1) - SCHAR_MAX - 1;
}
short SignTestRestoreOriginalXshort(void)
{
if (SignTestOriginalXshort < SHRT_MAX + 1u)
return (short)SignTestOriginalXshort;
return (short)(SignTestOriginalXshort - SHRT_MAX - 1) - SHRT_MAX - 1;
}
#define IFELSE(E1,E2,E3) (((E1) && (E2)) || (!(E1) && (E3)))
#define SEQ(E1,E2) (((E1) && (E2)) || (E2))
#define SIGNED_VAR(VAR) \
( \
IFELSE \
( \
sizeof(VAR) >= sizeof(int), \
((VAR) - (VAR) - 1 < 0), \
IFELSE \
( \
sizeof(VAR) == sizeof(short), \
SEQ(SignTestOriginalXshort = (VAR), \
SEQ(SignTestTestVal = (VAR) = -1, \
SEQ((VAR) = SignTestRestoreOriginalXshort(), \
SignTestTestVal < 0))), \
IFELSE \
( \
sizeof(VAR) == sizeof(char), \
SEQ(SignTestOriginalXchar = (VAR), \
SEQ(SignTestTestVal = (VAR) = -1, \
SEQ((VAR) = SignTestRestoreOriginalXchar(), \
SignTestTestVal < 0))), \
(fprintf(stderr, "unsupported type!"), exit(-1), 0) \
) \
) \
) \
)
#define VAR_MAX(SIGNED,VAR) \
( \
SIGNED ? \
((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 2))) : \
( \
(sizeof(VAR) < sizeof(long long)) ? \
((1ll << (sizeof(VAR) * CHAR_BIT - 1)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 1))) : \
( \
(sizeof(VAR) == sizeof(long long)) ? \
-1ll : \
(fprintf(stderr, "unsupported type!"), exit(-1), 0) \
) \
) \
)
#define VAR_MIN(SIGNED,VAR) \
( \
SIGNED ? \
(-((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 2))) - 1) : \
0 \
)
int main(void)
{
signed char sc = 1; char c = 2; unsigned char uc = 3;
short ss = 4; unsigned short us = 5;
int si = 6; unsigned int ui = 7;
long sl = 8; unsigned long ul = 9;
long long sll = 10; unsigned long long ull = 11;
#define PRINT_VARS() \
printf("sc=%hhd, c=%hhu, uc=%hhu, " \
"ss=%hd, us=%hu, si=%d, ui=%u, " \
"sl=%ld, ul=%lu, sll=%lld, ull=%llu\n", \
sc, c, uc, \
ss, us, si, ui, \
sl, ul, sll, ull)
#define TEST_VAR(VAR) \
{ \
int varIsSigned = SIGNED_VAR(VAR); \
if (varIsSigned) \
printf("%lld <= " #VAR " <= %lld\n", \
VAR_MIN(varIsSigned,VAR), \
VAR_MAX(varIsSigned,VAR)); \
else \
printf("%lld <= " #VAR " <= %llu\n", \
VAR_MIN(varIsSigned,VAR), \
(unsigned long long)VAR_MAX(varIsSigned,VAR)); \
}
PRINT_VARS();
TEST_VAR(sc);
TEST_VAR(c);
TEST_VAR(uc);
TEST_VAR(ss);
TEST_VAR(us);
TEST_VAR(si);
TEST_VAR(ui);
TEST_VAR(sl);
TEST_VAR(ul);
TEST_VAR(sll);
TEST_VAR(ull);
PRINT_VARS();
return 0;
}
Output (ideone):
sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11
-128 <= sc <= 127
-128 <= c <= 127
0 <= uc <= 255
-32768 <= ss <= 32767
0 <= us <= 65535
-2147483648 <= si <= 2147483647
0 <= ui <= 4294967295
-2147483648 <= sl <= 2147483647
0 <= ul <= 4294967295
-9223372036854775808 <= sll <= 9223372036854775807
0 <= ull <= 18446744073709551615
sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11
Here are macros from my library that work for types, rather than variables:
/* min and max integer values. T is a signed or unsigned integer type. */
/* Returns 1 if T is signed, else 0. */
#define INTTYPE_SIGNED(T) ((T)-1 < (T)0)
/*
* Returns (T)(maximum value of a T).
*
* Pains are taken (perhaps unnecessarily) to avoid integer overflow
* with signed types.
*/
#define INTTYPE_MAX(T) \
(((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1)) - 1 + \
((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1)))
/*
* Returns (T)(minimum value of a T).
* Pains are taken (perhaps unnecessarily) to avoid integer overflow
* with signed types.
* assert: twos complement architecture
*/
#define INTTYPE_MIN(T) ((T)(-INTTYPE_MAX(T)-1))
Edit: Adapting these to the question:
/* min and max integer values. V is a signed or unsigned integer value. */
/* Returns 1 if V has signed type, else 0. */
#define INT_VALUE_SIGNED(V) ((V)-(V)-1 < 0)
/*
* Returns maximum value for V's type.
*
* Pains are taken (perhaps unnecessarily) to avoid integer overflow
* with signed types.
*/
#define INT_VALUE_MAX(V) \
(((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1)) - 1 + \
((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1)))
/*
* Returns minimum value for V's type.
* Pains are taken (perhaps unnecessarily) to avoid integer overflow
* with signed types.
* assert: twos complement architecture
*/
#define INT_VALUE_MIN(V) (-INT_VALUE_MAX(V)-1)
Afterthought: These invoke UB if V is a variable, or an expression containing variables, that have not been assigned a value ... which is the case in the question as asked. They are likely to work on many implementations, but the C standard doesn't guarantee it, and they will certainly fail on an implementation that initializes uninitialized variables with trap values.
You can do it quite easily with a C11 type-generic expression:
#define maxvalue(type) _Generic(type, int: INT_MAX, \
unsigned int: UINT_MAX)
It's not a function, but I think it does what you want. Here's a simple example program:
#include <stdio.h>
#include <limits.h>
#define maxvalue(type) _Generic(type, int: INT_MAX, \
unsigned int: UINT_MAX)
int main(void)
{
int i;
unsigned int ui;
printf("%u\n", maxvalue(i));
printf("%u\n", maxvalue(ui));
return 0;
}
And its output:
$ clang -Wall -Werror -Wextra -pedantic -std=c11 example.c -o example
$ ./example
2147483647
4294967295
My answers are larger than yours because my system has 32-bit integers. You appear to have a 16-bit machine.