How to count assignment operators in a text file? - c++

My task is to create a program in C ++ that processes a text file in sequential mode. The data must be read from the file one line at a time. Do not back up the entire contents of the file to RAM. The text file contains syntactically correct C++ code and I have to count how many assignment operators are there.
The only thing I could think of was making a function that searches for patterns and then counts how many times they appear. I insert every assignment operator as a pattern and then sum all the counts together. But this does not work because if I insert the pattern "=" many operators such as "%=" or "+=" also get counted in. And even operators like "!=" or "==" get counted, but they shouldn't because they are comparison operators.
My code gives the answer 7 but the real answer should be 5.
#include <iostream>
#include <fstream>
using namespace std;
int patternCounting(string pattern, string text){
int x = pattern.size();
int y = text.size();
int rez = 0;
for(int i=0; i<=y-x; i++){
int j;
for(j=0; j<x; j++)
if(text[i+j] !=pattern[j]) break;
if(j==x) rez++;
}
return rez;
}
int main()
{
fstream file ("test.txt", ios::in);
string rinda;
int skaits=0;
if(!file){cout<<"Nav faila!"<<endl; return 47;}
while(file.good()){
getline(file, rinda);
skaits+=patternCounting("=",rinda);
skaits+=patternCounting("+=",rinda);
skaits+=patternCounting("*=",rinda);
skaits+=patternCounting("-=",rinda);
skaits+=patternCounting("/=",rinda);
skaits+=patternCounting("%=",rinda);
}
cout<<skaits<<endl;
return 0;
}
Contents of the text file:
#include <iostream>
using namespace std;
int main()
{
int z=3;
int x=4;
for(int i=3; i<3; i++){
int f+=x;
float g%=3;
}
}
Note that as a torture test, the following code has 0 assignments on older C++ standards and one on newer ones, due to the abolition of trigraphs.
// = Torture test
int a = 0; int b = 1;
int main()
{
// The next line is part of this comment until C++17 ??/
a = b;
struct S
{
virtual void foo() = 0;
void foo(int, int x = 1);
S& operator=(const S&) = delete;
int m = '==';
char c = '=';
};
const char* s = [=]{return "=";}();
sizeof(a = b);
decltype(a = b) c(a);
}

There are multiple issues with the code.
The first, rather mundane issue, is your handling of file reading. A loop such as while (file.good()) … is virtually always an error: you need to test the return value of getline instead!
std::string line;
while (getline(file, line)) {
// Process `line` here.
}
Next, your patternCounting function fundamentally won’t work since it doesn’t account for comments and strings (nor any of C++’s other peculiarities, but these seem to be out of scope for your assignment). It also doesn’t really make sense to count different assignment operators separately.
The third issue is that your test case misses lots of edge cases (and is invalid C++). Here’s a better test case that (I think) exercises all interesting edge cases from your assignment:
int main()
{
int z=3; // 1
int x=4; // 2
// comment with = in it
"string with = in it";
float f = 3; // 3
f = f /= 4; // 4, 5
for (int i=3; i != 3; i++) { // 6
int f=x += z; // 7, 8
bool g=3 == 4; // 9
}
}
I’ve annotated each line with a comment indicating up to how many occurrences we should have counted by now.
Now that we have a test case, we can start implementing the actual counting logic. Note that, for readability, function names generally follow the pattern “verb subject”. So instead of patternCounting a better function name would be countPattern. But we won’t count arbitrary patterns, we will count assignments. So I’ll use countAssignments (or, using my preferred C++ naming convention: count_assignments).
Now, what does this function need to do?
It needs to count assignments (incl. initialisations), duh.
It needs to discount occurrences of = that are not assignments:
inside strings
inside comments
inside comparison operators
Without a dedicated C++ parser, that’s a rather tall order! You will need to implement a rudimentary lexical analyser (short: lexer) for C++.
First off, you will need to represent each of the situations we care about with its own state:
enum class state {
start,
comment,
string,
comparison
};
With this in hand, we can start writing the outline of the count_assignments function:
int count_assignments(std::string const& str) {
auto count = 0;
auto state = state::start;
auto prev_char = '\0';
for (auto c : str) {
switch (state) {
case state::start:
break;
case state::comment:
break;
case state::string:
break;
case state::comparison:
break;
}
prev_char = c;
}
// Useful for debugging:
// std::cerr << count << "\t" << str << "\n";
return count;
}
As you can see, we iterate over the characters of the string (for (c : str)). Next, we handle each state we could be currently in.
The prev_char is necessary because some of our lexical tokens are more than one character in length (e.g. comments start by //, but /= is an assignment that we want to count!). This is a bit of a hack — for a real lexer I would split such cases into distinct states.
So much for the function skeleton. Now we need to implement the actual logic — i.e. we need to decide what to do depending on the current (and previous) character and the current state.
To get you started, here’s the case state::start:
switch (c) {
case '=':
++count;
state = state::comparison;
break;
case '<': case '>': case '!':
state = state::comparison;
break;
case '"' :
state = state::string;
break;
case '/' :
if (prev_char == '/') {
state = state::comment;
}
break;
}
Be very careful: the above will over-count the comparison ==, so we will need to adjust that count once we’re inside case state::comparison and see that the current and previous character are both =.
I’ll let you take a stab at the rest of the implementation.
Note that, unlike your initial attempt, this implementation doesn’t distinguish the separate assignment operators (=, +=, etc.) because there’s no need to do so: they’re all counted automatically.

The clang compiler has a feature to dump the syntax tree (also called AST). If you have syntactically correct C++ code (which you don't have), you can count the number of assignment operators for example with the following command line (on a unixoid OS):
clang++ -Xclang -ast-dump -c my_cpp_file.cpp | egrep "BinaryOperator.*'='" | wc -l
Note however that this will only match real assigments, not copy initializations, which also can use the = character, but are something syntactically different (for example an overloaded = operator is not called in that case).
If you want to count the compound assignments and/or the copy initializations as well, you can try to look for the corresponding lines in the output AST and add them to the egrep search pattern.

In practice, your task is incredibly difficult.
Think for example of C++ raw string literals (you could have one spanning dozen of source lines, with arbitrary = inside them). Or of asm statements doing some addition....
Think also of increment operators like (for some declared int x;) a x++ (which is equivalent to x = x+1; for a simple variable, and semantically is an assignment operator - but not syntactically).
My suggestion: choose one open source C++ compiler. I happen to know GCC internals.
With GCC, you can write your own GCC plugin which would count the number of Gimple assignments.
Think also of Quine programs coded in C++...
NB: budget months of work.

Related

Unexpected output in C++

This is not a problem with programming contest but with the language C++.
There is an old programming problem on codeforces. The solution is with C++. I already solved in Python but I don't understand this behavior of C++. In my computer and on onlinegdb's C++ compiler, I get expected output but on codeforces judge, I get a different output.
If interested in the problem : http://codeforces.com/contest/8/problem/A
It's very simple and a small read. Though Reading it is not required for the question.
Task in Short:
Print("forward") if string a is found in string s and string b is also found in s
Print("backward") if string a is found in reverse of string s and string b is also found in reverse of s
Print("both") if both of above are true
Print("fantasy") if both of above are false
#include<bits/stdc++.h>
using namespace std;
#define int long long
//initializing all vars because blogs said uninitialized vars sometimes give unexpected result
string s="", a="", b="";
bool fw = false;
bool bw = false;
string now="";
string won="";
int pa=-1, pb=-1, ra=-1, rb=-1;
signed main()
{
//following 2 lines can be ignored
ios_base::sync_with_stdio(false);
cin.tie(NULL);
//taking main input string s and then two strings we need to find in s are a & b
cin >> s >> a >> b;
//need reverse string of s to solve the problem
string r = s;
reverse(r.begin(), r.end());
//pa is index of a if a is found in s else pa = -1 if not found
pa = s.find(a);
//if a was a substring of s
if (pa != -1) {
//now is substring of s from the next letter where string a was found i.e. we remove the prefix of string till last letter of a
now = s.substr(pa + a.size(), s.size() - (pa + a.size()));
//pb stores index of b in remaining part s i.e. now
pb = now.find(b);
//if b is also in now then fw is true
if (pb != -1) {
fw = true;
}
}
//same thing done for the reverse of string s i.e. finding if a and b exist in reverse of s
ra = r.find(a);
if (ra != -1) {
won = r.substr(ra + a.size(), r.size() - (ra + a.size()));
rb = won.find(b);
if (rb != -1) {
bw = true;
}
}
if (fw && bw) {
cout << "both" << endl;
}
else if (fw && !bw) {
cout << "forward" << endl;
}
else if (!fw && bw) {
cout << "backward" << endl;
}
else {
cout << "fantasy" << endl;
}
return 0;
}
For input
atob
a
b
s="atob", a="a", b="b"
Here reverse of atob is bota.
a is in atob.
So, string now = tob.
b is in tob so fw is true.
Now a is in bota.
So, string won = "" (empty because nothing after a). So, b is not in won.
So, rw is false.
Here answer is to print forward and in C++14 on my PC and onlinegdb, the output is forward but on codeforces judge, it's both.
I did many variations of the code but no result.
Finally I observed that if I run my program on PC and don't give any input and terminate the program in terminal with Ctrl-C, it prints both which is strange as both should only be printed when both fw and rw are true.
What is this behavior of C++?
Let's dissect this code and see what problems we can find. It kind of tips over into a code review, but there are multiple problems in addition to the proximate cause of failure.
#include<bits/stdc++.h>
Never do this. If you see it in an example, you know it's a bad example to follow.
using namespace std;
Fine, we're not in a header and brevity in sample code is a reasonable goal.
#define int long long
Oh no, why would anyone ever do this? The first issue is that preprocessor replacement is anyway prohibited from replacing keywords (like int).
Even without that prohibition, this later line
int pa=-1, pb=-1, ra=-1, rb=-1;
is now a deliberate lie, as if you're obfuscating the code. It would have cost nothing to just write long long pa ... if that's what you meant, and it wouldn't be deceptive.
//initializing all vars because blogs said uninitialized vars sometimes give unexpected result
string s="", a="", b="";
But std::string is a class type with a default constructor, so it can't be uninitialized (it will be default-initialized, which is fine, and writing ="" is just extra noise).
The blogs are warning you about default initialization of non-class types (which leaves them with indeterminate values), so
bool fw = false;
is still sensible.
NB. these are globals, which are anyway zero-initialized (cf).
signed main()
Here are the acceptable faces of main - you should never type anything else, on pain of Undefined Behaviour
int main() { ... }
int main(int argc, char *argv[]) { ... }
Next, these string positions are both (potentially) the wrong type, and compared to the wrong value:
ra = r.find(a);
if (ra != -1) {
could just be
auto ra = r.find(a);
if (ra != std::string::npos) {
(you could write std::string::size_type instead of auto, but I don't see much benefit here - either way, the interface, return type and return values of std::string::find are well-documented).
The only remaining objection is that none of now, won or the trailing substring searches correspond to anything in your problem statement.
The above answer and comments are way more enough information for your question. I cannot comment yet so I would like to add a simplified answer here, as I'm also learning myself.
From the different outputs on different compilers you can trackback the logic and found the flow of code is differ in this line:
if (rb != -1) {
Simply adding a log before that line, or using a debugger:
cout << "rb:" << rb << endl;
You can see that on your PC: rb:-1
But on codeforces: rb:4294967295
won.find(b) return npos, which mean you have an assignment: rb = npos;
This is my speculation, but a possible scenario is:
On your PC, rb is compiled as int (keyword), which cannot hold 4294967295, and assigned to -1.
But on codeforces, rb is compiled as long long, follow the definition, and 4294967295 was assigned instead.
Because you redefine the keyword int, which is advised again by standard of C++ programming language, different compiler will treat this line of code differently.

R G B element array swap

I'm trying to create this c++ program to perform the description below. I am pretty certain the issue is in the recursive, but uncertain how to fix it. I'm guessing it just keeps iterating through to infinity and crashes. I do not even get an output. I figured I could just compare the previous and current pointer and perform a 3-piece temp swap based on lexicography. I would use a pointer to iterate through the array and decrement it after each swap, then recursively call with that ptr as the parameter. Didn't work, I'm here, help me please :). If there is a simpler solution that would work too, but prefer to understand where I went wrong with this code.
#include <string>
#include <iostream>
using namespace std;
// Given an array of strictly the characters 'R', 'G', and
// 'B', segregate the values of the array so that all the
// Rs come first, the Gs come second, and the Bs come last.
// You can only swap elements of the array.
char* RGBorder(char* c_a)
{
size_t sz = sizeof(c_a)/sizeof(*c_a);
char* ptr_ca = c_a;
char* prv_ptr = ptr_ca;
ptr_ca++;
char temp;
while(*ptr_ca)
{
switch(*ptr_ca)
{
case 'R' :
if( *prv_ptr < *ptr_ca ) {
temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
} else if( *prv_ptr == *ptr_ca ) {
continue;
} else { ptr_ca--; RGBorder(ptr_ca); }
case 'G' :
if( *prv_ptr < *ptr_ca ) {
temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
} else if( *prv_ptr == *ptr_ca ) {
continue;
} else { ptr_ca--; RGBorder(ptr_ca); }
default:
ptr_ca++;
continue;
}
ptr_ca++;
cout << *ptr_ca;
}
return c_a;
}
int main()
{
char ca[] = {'G', 'B', 'R', 'R', 'B', 'R', 'G'};
char *oca =RGBorder(ca);
char *pca = oca;
while(*pca)
{
cout << *pca << endl;
pca++;
}
}
There are many issues with your code.
1) You call the function RGBorder with a character pointer, and then attempt to get the number of characters using this:
size_t sz = sizeof(c_a)/sizeof(*c_a);
This will not get you the number of characters. Instead this will simply get you the
sizeof(char *) / sizeof(char)
which is usually 4 or 8. The only way to call your function using a char array is either provide a null-terminated array (thus you can use strlen), or you have to pass the number of characters in the array as a separate argument:
char *RGBorder(char *c_a, int size)
2) I didn't go through your code, but there are easier ways to do a 3-way partition in an array. One popular algorithm to do this is one based on the Dutch National Flag problem.
Since you want the array in RGB order, you know that the series of G will always come in the middle (somewhere) of the sequence, with R on the left of the sequence, and B always on the right of the sequence.
So the goal is to simply swap R to the left of the middle, and B to the right of the middle. So basically you want a loop that incrementally changes the "middle" when needed, while swapping R's and B's to their appropriate position when they're detected.
The following code illustrates this:
#include <algorithm>
char *RGBorder(char *c_a, int num)
{
int middle = 0; // assume we only want the middle element
int low = 0; // before the G's
int high = num - 1; // after the G's
while (middle <= high)
{
if ( c_a[middle] == 'R' ) // if we see an 'R' in the middle, it needs to go before the middle
{
std::swap(c_a[middle], c_a[low]); // swap it to a place before middle
++middle; // middle has creeped up one spot
++low; // so has the point where we will swap when we do this again
}
else
if (c_a[middle] == 'B') // if we see a 'B' as the middle element, it needs to go after the middle
{
std::swap(c_a[middle], c_a[high]); // place it as far back as you can
--high; // decrease the back position for next swap that comes here
}
else
++middle; // it is a 'G', do nothing
}
return c_a;
}
Live Example
Here is another solution that uses std::partition.
#include <algorithm>
#include <iostream>
char *RGBorder(char *c_a, int num)
{
auto iter = std::partition(c_a, c_a + num, [](char ch) {return ch == 'R';});
std::partition(iter, c_a + num, [](char ch) {return ch == 'G';});
return c_a;
}
Live Example
Basically, the first call to std::partition places the R's to the front of the array. Since std::partition returns an iterator (in this case, a char *) to the end of where the partition occurs, we use that as a starting position in the second call to std::partition, where we partition the G values.
Note that std::partition also accomplishes its goal by swapping.
Given this solution, we can generalize this for an n-way partition by using a loop. Assume we want to place things in RGBA order (4 values instead of 3).
#include <algorithm>
#include <iostream>
#include <cstring>
char *RGBorder(char *c_a, int num, char *order, int num2)
{
auto iter = c_a;
for (int i = 0; i < num2 - 1; ++i)
iter = std::partition(iter, c_a + num, [&](char ch) {return ch == order[i];});
return c_a;
}
int main()
{
char ca[] = "AGBRRBARGGARRBGAGRARAA";
std::cout << RGBorder(ca, strlen(ca), "RGBA", 4);
}
Output:
RRRRRRRGGGGGBBBAAAAAAA
Sorry to put it blunt, but that code is a mess. And I don't mean the mistakes, those are forgivable for beginners. I mean the formatting. Multiple statements in one line make it super hard to read and debug the code. Short variable names that carry no immediate intrinsic meaning make it hard to understand what the code is supposed to do. using namespace std; is very bad practise as well, but I can imagine you were taught to do this by whoever gives that course.
1st problem
Your cases don't break, thus you execute all cases for R, and both G and default for G. Also your code will never reach the last 2 lines of your loop, as you continue out before in every case.
2nd problem
You have an endless loop. In both cases you have two situations where you'll end up in an endless loop:
In the else if( *prv_ptr == *ptr_ca ) branch you simply continue; without changing the pointer.
In the else branch you do ptr_ca--;, but then in default you call ptr_ca++; again.(Note that even with breaks you would still call ptr_ca++; at the end of the loop.)
In both cases the pointer doesn't change, so once you end up in any of those conditions your loop will never exit.
Possible 3rd problem
I can only guess, because it is not apparent from the name, but it seems that prv_ptr is supposed to hold whatever was the last pointer in the loop? If so, it seems wrong that you don't update that pointer, ever. Either way, proper variable names would've made it more clear what the purpose of this pointer is exactly. (On a side note, consistent usage of const can help identify such issues. If you have a variable that is not const, but never gets updated, you either forgot to add const or forgot to update it.)
How to fix
Format your code:
Don't use using namespace std;.
One statement per line.
Give your variables proper names, so it's easy to identify what is what. (This is not 1993, really, I'd rather have a thisIsThePointerHoldingTheCharacterThatDoesTheThing than ptr_xy.)
Fix the aforementioned issues (add breaks, make sure your loop actually exits).
Then debug your code. With a debugger. While it runs. With breakpoints and stepping through line by line, inspecting the values of your pointers as the code executes. Fancy stuff.
Good luck!
just count the number of 'R', 'G' and 'B' letters and fill the array from scratch.
much easier, no recursions.

Function isalnum(): unexpected results

For an assignment, I am using std::isalnum to determine if the input is a letter or a number. The point of the assignment is to create a "dictionary." It works well on small paragraphs, but does horrible on pages of text. Here is the code snippet I am using.
custom::String string;
std::cin >> string;
custom::String original = string;
size_t size = string.Size();
char j;
size_t i = 0;
size_t beg = 0;
while( i < size)
{
j = string[i];
if(!!std::isalnum(static_cast<unsignedchar>(j)))
{
--size;
}
if( std::isalnum( j ) )
{
string[i-beg] = tolower(j);
}
++i;
}//end while
string.SetSize(size - beg, '\0');
The code presented as I write this, does not make sense as a whole.
However, the calls to isalnum, as shown, would only work for plain ASCII, because
the C character classification functions require non-negative argument, or else EOF as argument, and
in order to work for international characters,
the encoding must be single-byte per character, and
setlocale should have been called prior to using the functions.
Regarding the first of these three points, you can wrap std::isalnum like this:
using Byte = unsigned char;
auto is_alphanumeric( char const ch )
-> bool
{ return !!std::isalnum( static_cast<Byte>( ch ) ); }
where the !! is just to silence a sillywarning from Visual C++ (warning about "performance", of all things).
Disclaimer: code untouched by compiler's hands.
Addendum: if you don't have a C++11 compiler, but only C++03,
typedef unsigned char Byte;
bool is_alphanumeric( char const ch )
{
return !!std::isalnum( static_cast<Byte>( ch ) );
}
As Bjarne remarked, C++11 feels like a whole new language! ;-)
I was able to create a solution to the problem. I noticed that isalnum did take care of some non alpha-numerics, but not all the time. Since the code above is part of a function, I called it multiple times with refined results given each time. I then came up with a do while loop that stores the string's size, calls the function, stores the new size, and compares them. If they are not the same it means that there is a chance that it needs to be called again. If they are the same, then the string has been fully cleaned. I am guessing that the reason isalnum was not working well was because I was reading in several chapters of a book into the string. Here is my code:
custom::string abc;
std::cin >> abc;
size_t first = 0;
size_t second = 0;
//clean the word
do{
first = abc.Size();
Cleanup(abc);
second = abc.Size();
}while(first != second);

grabbing data sets from a file with an arbitrary amount of spaces

**No direct answers or code examples please, this is my homework which i need to learn from. I'm looking for help concerning the algorithm i need to develop.
I seem to be having a logic error in coming up with a solution for a portion of my class work, the program involves multiple files, but here is the only relevant portion:
I have a file PlayerStats that holds the stats for a basketball player in:
rebounds
points
assists
uniform #
my initial reaction would be to create a while loop and read these into a temporary struct that holds these values, then create a merge function that merges the values of the temp struct with the inital array of records, simple enough?
struct Baller
{
//other information on baller
int rebounds;
int assists;
int uniform;
int points;
void merge(Baller tmp); //merge the data with the array of records
}
//in my read function..
Baller tmp;
int j = 0;
inFile << tmp.uniform << tmp.assists << tmp.points << tmp.rebounds
while(inFile){
ArrayRecords[j].merge(tmp);
j++;
//read in from infile again
}
The catch:
The file can have an arbitrary number of spaces between the identifiers, and the information can be in any order(leaving out the uniform number, that is always first). e.g.
PlayerStats could be
11 p3 a12 r5 //uniform 11, 3 points 12 assists 5 rebounds
//other info
OR
11 p 3 r 5 a 12 //same exact values
What I've come up with
can't seem to think of an algorithm to grab these values from the file in the correct order, i was thinking of something along these lines:
inFile << tmp.uniform; //uniform is ALWAYS first
getline(inFile,str); //get the remaining line
int i = 0;
while(str[i] == " ") //keep going until i find something that isnt space
i++;
if(str[i] == 'p') //heres where i get stuck, how do i find that number now?
else if(str[i] == 'a')
eles if(str[i] = 'r'
If you're only going to check one letter, you could use a switch statement instead of if / else, that would make it easier to read.
You know where the number starts at that point, (hint: str[i+1]), so depending on what type your str[] is, you can either use atoi if its a char array, or std::stringstream if it's an std::string.
I'm tempted to give you some code, but you said not too. If you do want some, let me know and I'll edit the answer with some code.
Instead of using a 'merge' function, try using an std::vector so you can just push_back your structure instead of doing any 'merging'. Besides, your merge function is basically a copy assignment operator, which is created by the compiler by default (you don't need to create a 'merge' function), you just need to use = to copy the data across. If you wanted to do something special in your 'merge' function, then you should overload the copy assignment operator instead of a 'merge' function. Simples.
Do something like that:
int readNumber () {
while isdigit (nextchar) -> collect in numberstring or directly build number
return that number;
}
lineEater () {
Read line
skip over spaces
uniform=readNumber ();
haveNum=false;
haveWhat=false;
Loop until eol {
skip over spaces
if (isdigit)
number=readNumber ();
skip over spaces
haveNum=true;
else
char=nextChar;
haveWhat=true;
if (haveChar and haveNum) {
switch (char) {
case 'p' : points=number; break;
...
}
haveNum=false;
haveWhat=false;
}
}
or, if you are more ambitous, write a grammar for your input and use lex/yacc.

Why are empty expressions legal in C/C++?

int main()
{
int var = 0;; // Typo which compiles just fine
}
How else could assert(foo == bar); compile down to nothing when NDEBUG is defined?
This is the way C and C++ express NOP.
You want to be able to do things like
while (fnorble(the_smurf) == FAILED)
;
and not
while (fnorble(the_smurf) == FAILED)
do_nothing_just_because_you_have_to_write_something_here();
But! Please do not write the empty statement on the same line, like this:
while (fnorble(the_smurf) == FAILED);
That’s a very good way to confuse the reader, since it is easy to miss the semicolon, and therefore think that the next row is the body of the loop. Remember: Programming is really about communication — not with the compiler, but with other people, who will read your code. (Or with yourself, three years later!)
I'm no language designer, but the answer I'd give is "why not?" From the language design perspective, one wants the rules (i.e. the grammar) to be as simple as possible.
Not to mention that "empty expressions" have uses, i.e.
for (i = 0; i < INSANE_NUMBER; i++);
Will dead-wait (not a good use, but a use nonetheless).
EDIT: As pointed out in a comment to this answer, any compiler worth its salt would probably not busy wait at this loop, and optimize it away. However, if there were something more useful in the for head itself (other than i++), which I've seen done (strangely) with data structure traversal, then I imagine you could still construct a loop with an empty body (by using/abusing the "for" construct).
OK, I’ll add this to the worst case scenario that you may actually use:
for (int yy = 0; yy < nHeight; ++yy) {
for (int xx = 0; xx < nWidth; ++xx) {
for (int vv = yy - 3; vv <= yy + 3; ++vv) {
for (int uu = xx - 3; uu <= xx + 3; ++uu) {
if (test(uu, vv)) {
goto Next;
}
}
}
Next:;
}
}
I honestly don't know if this is the real reason, but I think something that makes more sense is to think about it from the standpoint of a compiler implementer.
Large portions of compilers are built by automated tools that analyze special classes of grammars. It seems very natural that useful grammars would allow for empty statements. It seems like unnecessary work to detect such an "error" when it doesn't change the semantics of your code. The empty statement won't do anything, as the compiler won't generate code for those statements.
It seems to me that this is just a result of "Don't fix something that isn't broken"...
Obviously, it is so that we can say things like
for (;;) {
// stuff
}
Who could live without that?
When using ;, please also be aware about one thing. This is ok:
a ? b() : c();
However this won't compile:
a ? b() : ; ;
There are already many good answers but have not seen the productive-environment sample.
Here is FreeBSD's implementation of strlen:
size_t
strlen(const char *str)
{
const char *s;
for (s = str; *s; ++s)
;
return (s - str);
}
The most common case is probably
int i = 0;
for (/* empty */; i != 10; ++i) {
if (x[i].bad) break;
}
if (i != 10) {
/* panic */
}
while (1) {
; /* do nothing */
}
There are times when you want to sit and do nothing. An event/interrupt driven embedded application or when you don't want a function to exit such as when setting up threads and waiting for the first context switch.
example:
http://lxr.linux.no/linux+v2.6.29/arch/m68k/mac/misc.c#L523