I am having trouble getting to grips with the definition and uses of symbolic and literal constants and I was wondering if you anyone could explain them and highlight their differences. Thanks!
A literal constant is a value typed directly into your program wherever it is needed. For example
int tempInt = 10;
tempInt is a variable of type int; 10 is a literal constant. You can't assign a value to 10, and its value can't be changed. A symbolic constant is a constant that is represented by a name, just as a variable is represented. Unlike a variable, however, after a constant is initialized, its value can't be changed.
If your program has one integer variable named students and another named classes, you could compute how many students you have, given a known number of classes, if you knew there were 15 students per class:
students = classes * 15;
A symbol is something that the compiler deals with. The compiler treats a const pretty much the way it treats a variable. On the other hand, a #define is something the compiler is not even aware of, because the precompiler transforms it into its value. It's like search-and-replace. If you do
#define A 5
and then
b += A;
The precompiler translates it into
b += 5;
and all the compiler sees is the number 5.
(Borrowing from earlier posts)
A literal constant is a value typed directly into your program wherever it is needed. For example
int breakpoint = 10;
The variable breakpoint is an integer (int); 10 is a literal constant. You can't assign a value to 10, and its value can't be changed. Unlike a variable, a constant can't be changed after it is assigned a value (initialized).
A symbol is something that the compiler deals with. In this example, TEN is a symbolic constant created using the #define function. A #define is something the compiler is not even aware of, because the precompiler transforms it into its assigned (defined) value. The precompiler searches out and replaces every symbol constant inside your program with a value.
#define TEN 10 /* These two lines of code become one... */
breakpoint += TEN; /* after running through the precompiler */
The precompiler translates it into
Breakpoint += 10;
The compiler never sees TEN but only its assigned value, 10. Why is this useful? What if the breakpoint is changed to 11. Rather than looking through the entire program and changing every variable definition to the new value that was set using a literal constant, 10, change the definition of a single symbol constant... TEN to 11 and let the precompiler do the changes for you.
I think what you mean is that a literal constant is a primitive expression like "string" or 2 or false, while a symbolic one is when you give it a name, like const int MagicNumber = 42. Both can be used as expressions, but you can refer to the latter with a name. Useful if you use the same constant from many places.
Related
world. I'm a novice in CS field and learning C language with a book called 'C Primer Plus'.
I have a question regarding C data types (Chapter 3 of the book).
In the book, it is written that
Data Variable and Constants
[...] Some types of data are preset before a program is used and keep their values unchanged throughout the life of the programs. These are constants. Other types of data may change or be assigned values as the program runs; these are variables. In the sample program, weight is a variable and 14.5833 is a constant. [...] The difference between a variable and a constant is that a variable can have its value assigned or changed while the program is running, and a constant can't.
the below is the sample program.
/* platinum.c -- your weight in platinum */
#include <stdio.h>
int main(void)
{
float weight;
float value;
printf("Are you worth your weight in platinum?\n");
printf("Let's check it out.\n);
printf("Please enter your weight in pounds: ");
scanf("%f", &weight);
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value);
printf("You are easily worth that! If platinum prices drop,\n");
printf("eat more to maintain your value.\n);
return 0;
}
and the next,
Data: Data-type Keywords
Beyond the distinction between variable and constant is the distinction between different types of data. [...] If a datum is a constant, the compiler can usually tell its type just by the way it looks. [...] A variable , however, needs to have its type announced in a declaration statement. [...]
Everything made sense to me until I read this below.
Initializing a Variable
To initilize a variable means to assign it a starting, or initial, value. [...] Here are some examples:
int hogs = 21;
int cows = 32, goats = 14
int dogs, cats = 94; /* valid, but poor, form */
and the following is,
Type int Constants
The various integers (21, 32, 14 and 94) in the last example are integer constants, also called integer literals.
It confuses me because how I understood is that constants and variables are different. And one of the differences is to be declared w/ its type or not to. but the value of the initialized variable in declare statements are called integer constants.
Now my questions are
1.why is there a difference between data constant and integer constant/literal?
2.How are they different?
3.What am I missing in this?
Thank you for reading.
why is there a difference between data constant and integer constant/literal?
There isn't. An integer constant is a type of data constant. Those declaration statements are initializing the variables with the values of the constants - given
int hogs = 21;
the variable hogs will contain the value 21 - we've copied the value of the constant into the variable.
Here are some more examples:
double d = 1.234; // copy the value 1.234 into d
char c = 'a'; // copy the character value 'a' into c
char str[] = "foo"; // copy the contents of the string "foo" into the array str - size of the array is taken from the size of the initializer.
Integer, floating point, and string constants can have suffixes that tell the compiler to use a specific type instead of assuming int or double or whatever. 1234U means "treat 1234 as an unsigned integer", 3.1415f means "treat 3.1415 as a float, not a double", etc. You probably don't need to worry about that quite yet, though.
So, why does type matter?
Different types have different representations in memory - the bit pattern for the integer value 1234 looks nothing like the bit pattern for the floating point value 1234.0, which looks nothing like the bit pattern for the string "1234". If we try to store a string value in an integer variable and try to use it as an integer, our program would not behave as expected. So the compiler has rules that prevent us from assigning values of incompatible types to variables.
Let's go back to our variable initialization:
int hog = 21;
We're telling the compiler that the variable hog has type int, and we're initializing it with a constant expression. For the compiler to accept this, the constant expression also needs to have type int (or a type compatible with int). If we write
int hog = "21";
the compiler will complain that we're attempting to initialize hog with a value of the wrong type.
main()
{
int a = 5, b = a, sum;
sum = a + b;
printf("sum is %d",sum);
}
In this C Program, will variable b be initialized at compile time or at run time?
Is it compile time initialization?
(C language)
No, variables should not always be initialised with literals, although some folk like to insure that variables are initialised at the point of declaration (and some firms insist on it) in order to avoid the reading of uninitialised variables in poorly crafted code.
As for any run-time behaviour: the as-if rule applies. Your source code merely describes what you want the behaviour to be, not the machine code that will be generated. Your variables will probably not exist in the compiled binary, which will be equivalent to
int main()
{
printf("sum is %d", 10);
}
(The expression int a = 5, b = a is well-defined since , is a sequencing point, so a is initialised at the time its value is read to assign to b.)
depends on whether the compiler/interpreter implemented the algorithm of constant propagation or not.
C standard does not impose not to use constant propagation. If one detects that that variable is not mutated it can be replaced with the precomputed value. The as-if rule says that one can do whatever optimization we want as time as the result is the expected one.
I have a function that returns a float number:
float function(enum value)
I then have an enum
typedef enum
{
a = 0,
b,
c
} myenum;
I want to do the following:
function(a+1);
And I wonder if there are any risks other than the risk of unexpected behaviour if the enum changes. My question might seem dumb but I have to make sure that there are no risks of what I'm doing.
Please don't ask questions on why it's done like this. Because I don't know. I just need to know if it's safe or not.
This is safe. Moreover, the standard guarantees that a+1 is b and a+2 is c in the scenario that you describe:
C99 standard, section 6.7.2.2, part 3: If the first enumerator has no =, the value of its enumeration constant is 0. Each subsequent enumerator with no = defines its enumeration constant as the value of the constant expression obtained by adding 1 to the value of the previous enumeration constant.
It's safe. As you seem to recognise yourself, it's really working against the way enums are intended to work, which is as arbitrary labels. However sometimes you want ordering such that a < b < c. If a = 0 and b = 1 and c = 2 in some firm sense, then you don't want an enum, however, you want a variable of type int.
I know the following is valid code:
#define SOMEMACRO 10
int arr[SOMEMACRO];
which would result as int arr[10].
If I wanted to make an array 2x size of that (and still need the original macro elsewhere), is this valid code?
#define SOMEMACRO 10
int arr[2 * SOMEMACRO];
which would be int arr[2 * 10] after precompilation. Is this still considered as constant expression by the compiler?
After a quick look it seems to work, but is this defined behavior?
Yes it will work.MACRO will be placed as it is at compilation so a[2*SOMEMACRO] will become a[2*10] which is perfectly valid.
To check what is preprocessed you can use cc -E foo.c option
Is this still considered as constant expression by the compiler?
Yes. That's the difference between a constant expression and a literal: a constant expression need not be a single literal, bit it can be any expression of which the value can be computed at compile time (i. e. a combination of literals or other constant expressions).
(Just for the sake of clarity: of course literals are still considered constant expressions.)
However, in C, the size of the array need not be a compile-time constant. C99 and C11 supports variable-length arrays (VLAs), so
size_t sz = // some size calculated at runtime;
int arr[sz];
is valid C as well.
Yes you can use this expression. It will not result in UB.
Note that an array subcript may be an integer expression:
#define i 5
#define j 4
int a[i+j*10] = 0;
The value of of subscript i+j*10 will be calculated during compilation.
yes, as long as it a valid number it's a constant expression.
and if you say it worked then you know the compiler worked just fine with it.
as you know we can't do
int x;
scanf("%d", &x);
int arr[2 * x];
because that's no a constant number. but what you've written is a constant number, so you're good to go
Consider this code;
#define A 5
#define B 3
int difference = A - B;
does value of "difference" is hardcoded as "2" in compile time, or does it get calculated on runtime?
The A and B macros are a bit of a distraction. This:
#define A 5
#define B 3
int difference = A - B;
is exactly equivalent to this:
int difference = 5 - 3;
so let's discuss the latter.
5 - 3 is a constant expression, which is an expression that "can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be". It's also an *integer constant expression". For example, a case label must be an integer constant expression, so you could write either this:
switch (foo) {
case 2: /* this is a constant */
...
}
or this:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
But note that the definition says that it can be evaluated during translation, not that it must be. There are some contexts that require constant expressions, and in those contexts the expression must be evaluated at compile time.
But assuming that difference is declared inside some function, the initializer is not one of those contexts.
Any compiler worth what you pay for it (even if it's free) will reduce 5 - 3 to 2 at compile time, and generate code that stores the value 2 in difference. But it's not required to do so. The C standard specifies the behavior of programs; it doesn't specify how that behavior must be implemented. But it's safe to assume that whatever compiler you're using will replace 5 - 3 by 2.
Even if you write:
int difference = 2;
a compiler could legally generate code that loads the value 5 into a register, subtracts 3 from it, and stores the contents of the register into difference. That would be a silly thing to do, but the language standard doesn't exclude it.
As long as the final result is that difference has the value 2, the language standard doesn't care how it's done.
On the other hand, if you write:
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
then the compiler must compute the result so it can diagnose the error (you can't have two case labels with the same value.
Finally, if you define difference at file scope (outside any function), then the initial value does have to be constant. But the real distinction in that case is not whether 5 - 3 will be evaluated at compile time, it's whether you're allowed to use a non-constant expression.
Reference: The latest draft of the 2011 C standard is N1570 (large PDF); constant expressions are discussed in section 6.6.
The standard does not specify this sort of thing. It says nothing about potential optimizations like this (and for good reason. A standard defines semantics, not implementation).
Why not look at the disassembly for your compiler? That will give you a definitive answer.
...
So let's do that.
Here is the output from VC++ 10:
#include <iostream>
#define A 5
#define B 3
int main() {
int x = A - B;
std::cout << x; // make sure the compiler doesn't toss it away
010A1000 mov ecx,dword ptr [__imp_std::cout (10A2048h)]
010A1006 push 2
010A1008 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]
return 0;
010A100E xor eax,eax
As you can see, it just replaced the occurrence of x with a static value of 2 and pushed it onto the stack for the call to cout. It did not evaluate the expression at runtime.