Why wont this print any output? (c programming language) - c

Not sure why nothing is coming out, compiler finds no errors, it just runs and terminates a few seconds later, no output.
I'll give the source code and the input file's text.
Source code:
letterArray.c
#include <stdio.h>
/* displays a histogram of the frequencies
of letters in input */
main()
{
int c, i, nwhite, nother;
int nletter[26];
nwhite = nother = 0;
for (i = 0; i < 26; ++i)
nletter[i] = 0;
while ((c = getchar()) != EOF)
if (c == 'a' || c == 'b' || c ==
'c' || c == 'd' || c == 'e' || c == 'f' ||
c == 'g' || c == 'h' || c == 'i' || c ==
'j' || c == 'k' || c == 'l' || c == 'm' ||
c == 'n' || c == 'o' || c == 'p' || c ==
'q' || c == 'r' || c == 's' || c == 't' ||
c == 'u' || c == 'v' || c == 'w' || c ==
'x' || c == 'y' || c == 'z')
++nletter[c];
else if (c == ' ' || c == '\n' || c
== '\t')
++nwhite;
else
++nother;
printf("Letter: a b c d e f g h i j
k l m n o p q r s t u v w x y z
\nOccurences:");
for (i = 0; i < 26; ++i)
printf(" %d", nletter[i]);
printf("\nwhite space = %d, other =
%d\n", nwhite, nother);
}
Text input: input.txt
abcdefg hijklmnop qrstuv wxyzz
I'm pretty new to c, and to programming in general. Any help would be appreciated.

Your compiler should be warning you that the index for your nletter array will be out of bounds -- which will invoke Undefined Behavior..
The problem causing you to write beyond the bounds of nletter is your failure to insure the indexing you use runs 0-25. Specifically you attempt:
++nletter[c];
where c will contain the ASCII value from 'a' - 'z' (or 97 - 122) see: ASCIItable.com. That is well above the usable indexes available (0 - 25) resulting from your declaration of: int nletter[26]; (recall, arrays are indexed from 0 to n-1 in C).
To prevent writing beyond the bounds of your array, you need to normalize the indexes such that 'a' will correspond to 0 and 'z' will correspond to 25. You do this by simply subtracting 'a' from the value of c, e.g.
++nletter[c - 'a'];
although I prefer the post-increment operator here, so:
nletter[c - 'a']++;
(the choice of the increment operator is yours -- the result is the same)
Putting it altogether, and adding a few clean-ups as described in the comments below, you could do something like:
#include <stdio.h>
int main (void) /* main() is a function that return type int */
{
int c, i, nwhite, nother;
int nletter[26] = {0}; /* {0} will initialize nletter */
nwhite = nother = 0;
while ((c = getchar()) != EOF) {
if ('a' <= c && c <= 'z')
nletter[c - 'a']++; /* you want indexes from 0-25 */
else if (c == ' ' || c == '\n' || c== '\t')
nwhite++;
else
nother++;
}
printf ("Letter: a b c d e f g h i j k "
"l m n o p q r s t u v w x y z\n"
"Occurences:");
for (i = 0; i < 26; ++i)
printf ("%2d", nletter[i]); /* use the field-width modifier to */
/* insure output of 2 spaces per-int */
printf("\nwhite space = %d, other = %d\n", nwhite, nother);
return 0;
}
(Note: you can simplify further by including ctype.h and checking islower(c) instead of 'a' <= c && c <= 'z' and using isspace(c) instead of c == ' ' || c == '\n' || c== '\t', but since your Question didn't specify whether you could use anything but stdio.h I left the manual checks in place.)
Example Use/Output
$ echo "abcdefg hijklmnop qrstuv wxyzz" | ./bin/letterfreq
Letter: a b c d e f g h i j k l m n o p q r s t u v w x y z
Occurences: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
white space = 4, other = 0
(if testing on windows, omit the quotation marks or you will see other = 2)
If you are just getting started, then always compile with "warnings-enabled" and read and fix all warnings -- do not accept code until it compiles cleanly -- without a single warning. That will catch most problems before you end up scratching your head for hours. For gcc/clang, that means adding, at minimum, -Wall -Wextra to your compile string (you can add -pedantic for a few more, and adding -Wshadow is helpful). For VS, use /W3 -- the /Wall for VS contains quite a few non-code related suggestions as warnings.
A compile string for this code would look like:
gcc
gcc -Wall -Wextra -pedantic -Wshadow -std=c11 -Ofast -o letterfreq letterfreq.c
VS
cl /nologo /W3 /Ox /Feletterfreq /Tc letterfreq.c
In either case, the code should compile without warning.

Related

Problem while defining conditions for multiple variables in C

I'm trying to write a program in C that receives three integers and uses This Method
But I only want the inputs to be in the range of 1-100 (including 1 and 100)
#include <stdio.h>
int main(){
int a , b , c = 50;
do{
scanf("%d %d %d", &a, &b, &c);
}
while((a > 100 || a < 1)&& (b > 100 || b < 1) && (c > 100 || c < 1));
if (a+b > c && a+c>b && b+c>a){
printf("%d\n", a+b+c);
}
else {
printf("invalid\n");
}
And it somehow doesn't consider the conditions
for example if I enter something like 1000 -5 4 It won't reprompt me for a new input
But the weird thing is that if i only consider one of the conditions it will work fine with that one variable! for example if i put something like while((a > 100 || a < 1); in there, it will then reject an input like 1000 2 4
How do I make it that it considers the conditions for all the variables?
Any help would be appreciated!
To check for any of the variable for range, change
while((a > 100 || a < 1)&& (b > 100 || b < 1) && (c > 100 || c < 1));
^^
// Will produce a FALSE value if 'a' is within expected range, and due to
// short-circuit, it'll not evaluate other conditions.
to
while((a > 100 || a < 1) || (b > 100 || b < 1) || (c > 100 || c < 1));
That said, couple of other points:
int a , b , c = 50; only initializes c, other variables are uninitialised and contain indeterminate values. Be a little more explicit, declare and define each variable in separate line (not a technical requirement, sake of readability and maintainability while being technically correct).
Always check for the success of scanf(), before using the scanned values. Better, do away with scanf() for user inputs and use fgets() instead.
more readable :
while(!( 1 <= a && a <= 100 && 1 <= b && b <= 100 && 1 <= c && c <= 100));
Your loop will only continue if all three of a, b, and c are out of range - in your example of 1000 -5 4, the condition (c > 100 || c < 1) evaluates to false (0), so the whole expression evaluates to false and the loop exits.
You need to change the sense of the test, such that it will continue if any of a, b, or c are out of range. There are a couple of ways to do this:
while ( (a < 1 || a > 100) || (b < 1 || b > 100) || (c < 1 || c > 100) );
Changing the && to || means that the expression will evaluate to true and the loop will continue if any one of a, b, or c are out of range.
Another option:
while ( !(1 <= a && a <= 100) && (1 <= b && b <= 100) && (1 <= c && c <= 100 ) );
This will cause the loop to exit if all three of a, b, and c are in range.
You should also check the result of scanf to make sure you read all 3 inputs:
do
{
if ( scanf ( "%d %d %d", &a, &b, &c ) != 3 ) )
{
if ( feof( stdin ) || ferror( stdin ) )
{
fputs( "Error or EOF on input, exiting", stderr );
return EXIT_FAILURE;
}
else
{
fputs( "Bad input detected, clearing input stream", stderr );
/**
* Read and discard everything up to the next newline character
*/
while ( getchar() != '\n' )
; // empty loop
/**
* Unconditionally branch back to the beginning of the loop since
* at least one of a, b, and c wasn't read at all (i.e., don't
* rely on the test since what you're testing isn't valid).
*/
continue;
}
}
} while ( (a < 1 || a > 100) || (b < 1 || b > 100) || (c < 1 || c > 100 ) );

what does this function do that helps it to take input differently and how are the conditions in for loop executed

What does this function do that helps it to take input differently and how are the conditions in for loop executed?
void scanint(int &x)
{
int flag=0;
register int c = gc();
if(c == '-') flag=1;
x = 0;
for(;(c<48 || c>57);c = gc());//why is this used?
for(;c>47 && c<58;c = gc()) {x = (x<<1) + (x<<3) + c - 48;}//how is this executed ?
if(flag == 1)x=-x;
}
It's not c.
void scanint(int &x) {/* Whatever */}
// ^^
This defines a function accepting a reference to an int and there are no references in c, the arguments are passed by value to functions. You could of course use a pointer to an int, but then the body of the function should be changed accordingly, using *x instead of ant occurrences of x.
The following assumes that gc() stands for a function similar to getchar(), so that the posted code is a very bad way of extracting an int value from stdin:
void scanint(int &x) // Or 'int *x' in C
{
int c = gc(); // register was deprecated in C++17
bool is_negative = (c == '-'); // C has bool from C99 too
x = 0; // '*x = 0;' in C. The same applies to the following code
// Considering only ASCII, ignores non-digit characters
// E.g. from " 123" it ignores the first two spaces,
// but, given " -123", it will ignore the sign too. Bad, as I said.
for( ;
( c < '0' || c > '9');
c = gc() )
;
// Now computes the actual number using an old trick that should
// be left to the compiler to be exploited:
// 10 * x = (2 + 8) * x = 2 * x + 8 * x = x << 1 + x << 3 (bit shifts)
for( ;
'0' <= c && c <= '9';
c = gc() )
{
x = (x << 1) + (x << 3) + c - '0';
}
if ( is_negative )
x = -x;
}

Why does this following program print "Yes" instead of "No"?

Why does this following program print "Yes" instead of "No"?
None of the variables is initialized to 2.
bool hello = 0;
int a = 1;
int b = 3;
int c = 4;
int d = 5;
if (a || b || c || d == 2) {
hello = 1;
}
if (hello == 1) {
printf("Yes");
}
if (hello == 0) {
printf("No");
}
return 0;
}
The statement
if (a || b || c || d == 2)
is equivalent to:
if (a != 0 || b != 0 || c != 0 || d == 2)
The equality comparison does not automatically distribute across all the variables. If you want to do that, you need to perform all the comparisons explicitly:
if (a == 2 || b == 2 || c ==2 || d == 2)
The expression (a || b || c || d == 2) evalutates to true because it treats a, b, c as booleans, and any non-zero integer is true.
You have given logical operator in the expression It means that if non zero value came then the expression is true. Then hello=1 is set and in next f statement it prints YES
You just meet the short circuit behavior of logical expressions OR.
The order of evaluation of logical OR || is left to right.
So in the following expression:
left || right
if left = true then right will never going to be executed (short circuit). In your code exactly same happened.
As you know, any non zero value treated as true in C, hence, a which is 1 is true. So, take a look:
if (a || b || c || d == 2)
if (true || bla bla bla) //rights are not even checked!
if (true)
hello = 1;
Tada! So the program print "Yes"!
None of the variables is initialized to 2.
Yes of course! But your if condition is not going to check that. To do so, try this:
if (a == 2 || b == 2 || c ==2 || d == 2) {
//...
because if judge num is not zero , if think this is true. so your code
if (a || b || c || d == 2)
like
if ( true || true || true || false)
the result is true, programe print "YES"

understanding entab example code in k&r

I'm teaching myself C and working through K&R. I'm doing exercise 1-21:
Write a Program entab that replaces strings of blanks by the minimum number of tabs and blanks to acheive the same spacing. Use the same tab stops as for detab.
I was having trouble doing this exercise so I found a solution online but I do not understand it. Can someone explain how this code works?
#include<stdio.h>
#define TABINC 8
int main(void)
{
int nb,nt,pos,c;
nb = 0;
nt = 0;
for(pos=1;(c=getchar())!=EOF;++pos)
if( c == ' ')
{
if((pos % TABINC) != 0)
++nb;
else
{
nb = 0;
++nt;
}
}
else
{
for( ; nt > 0 ; --nt)
putchar('\t');
if( c == '\t')
nb = 0;
else
for( ; nb > 0; --nb)
putchar(' ');
putchar(c);
if(c == '\n')
pos = 0;
else if ( c == '\t')
pos = pos + ( TABINC - (pos -1) % TABINC) - 1;
}
return 0;
}
The code is to
put a tab ('\t') when a space () is encountered at a position that is a multiple of TABINC, i.e, the size of the tab.
when a tab is encountered, the number of spaces is reset. This is to prefer a tab to reach a tabstop
To illustrate:
- a space shown in *
- a tab is shown in
column: 1 2 3 4 5 6 7 8 9 10
input : * * * * h e l * * w
output: <tab>hel<tab>*w
In this example, when a space is encountered at every column position which is divisible by the TABINC, i.e 4 (in this case), a tab '\t' is inserted. Otherwise space.
Hope this will give you some idea.

How to display the symbol's type like the nm command?

I'm studying the ELF format right now. I have to code a simple nm fonction (without the options).
I'm already printing on the output the symbol's value and the symbol's name.
Here's the nm output :
value type name
0000000000600e30 D __DTOR_END__
I have the same one, but without the 'type'. I am using the ELF64_Sym structure, as follow :
typedef struct {
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
I know that I have to use the st_info variable and this macro :
#define ELF64_ST_TYPE(info) ((info) & 0xf)
to get the symbol's type. But, the symbol type can be a macro as follow :
NAME VALUE
STT_NOTYPE 0
STT_OBJECT 1
STT_FUNC 2
STT_SECTION 3
STT_FILE 4
STT_LOPROC 13
STT_HIOPROC 15
And I would like to know is how can I get from these macros the letters printed by nm, example:
U, u, A, a, T, t, R, r, W, w
Ok I did some researchs and here's my function to get the correct character depending on the symbol. Feel free to add / edit some characters.
char print_type(Elf64_Sym sym, Elf64_Shdr *shdr)
{
char c;
if (ELF64_ST_BIND(sym.st_info) == STB_GNU_UNIQUE)
c = 'u';
else if (ELF64_ST_BIND(sym.st_info) == STB_WEAK)
{
c = 'W';
if (sym.st_shndx == SHN_UNDEF)
c = 'w';
}
else if (ELF64_ST_BIND(sym.st_info) == STB_WEAK && ELF64_ST_TYPE(sym.st_info) == STT_OBJECT)
{
c = 'V';
if (sym.st_shndx == SHN_UNDEF)
c = 'v';
}
else if (sym.st_shndx == SHN_UNDEF)
c = 'U';
else if (sym.st_shndx == SHN_ABS)
c = 'A';
else if (sym.st_shndx == SHN_COMMON)
c = 'C';
else if (shdr[sym.st_shndx].sh_type == SHT_NOBITS
&& shdr[sym.st_shndx].sh_flags == (SHF_ALLOC | SHF_WRITE))
c = 'B';
else if (shdr[sym.st_shndx].sh_type == SHT_PROGBITS
&& shdr[sym.st_shndx].sh_flags == SHF_ALLOC)
c = 'R';
else if (shdr[sym.st_shndx].sh_type == SHT_PROGBITS
&& shdr[sym.st_shndx].sh_flags == (SHF_ALLOC | SHF_WRITE))
c = 'D';
else if (shdr[sym.st_shndx].sh_type == SHT_PROGBITS
&& shdr[sym.st_shndx].sh_flags == (SHF_ALLOC | SHF_EXECINSTR))
c = 'T';
else if (shdr[sym.st_shndx].sh_type == SHT_DYNAMIC)
c = 'D';
else
c = '?';
if (ELF64_ST_BIND(sym.st_info) == STB_LOCAL && c != '?')
c += 32;
return c;
}
I'm missing the s, n, p and i.
I'm quite sure the 'R' is not good. I'll will edit this when I find it.
The ELF64_ST_TYPE does not map directly into the letters that nm prints.
In order to perform the mapping, you need to pay attention to both ELF64_ST_BIND, and the section that the symbol refers to. For example:
bool weak = (ELF64_ST_BIND(sym) == STB_WEAK);
bool unresolved = (sym->st_shndx == SHN_UNDEF);
if (unresolved) {
printf(" %c ", weak ? 'w' : 'U');
}
For t vs T, you'll want to look at ELF64_ST_BIND(sym) == STB_LOCAL or ELF64_ST_BIND(sym) == STB_GLOBAL, and you'll want to find out whether the section referenced by st_shndx is a "text" one (has SHF_EXECINSTR in its flags).
P.S. As far as I know, there is no u. If your nm man page does list u, I am curious to know what type of symbol that is.

Resources