Power up C++ with STL: Part I (introduction, vector)

Perhaps you are already using C++ as your main programming language to solve TopCoder problems. This means that you have already used STL in a simple way, because arrays and strings are passed to your function as STL objects. You may have noticed, though, that many coders manage to write their code much more quickly and concisely than you.

Or perhaps you are not a C++ programmer, but want to become one because of the great functionality of this language and its libraries (and, maybe, because of the very short solutions you've read in TopCoder practice rooms and competitions).

Regardless of where you're coming from, this article can help. In it, we will review some of the powerful features of the Standard Template Library (STL) – a great tool that, sometimes, can save you a lot of time in an algorithm competition.

The simplest way to get familiar with STL is to begin from its containers.

Containers
Any time you need to operate with many elements you require some kind of container. In native C (not C++) there was only one type of container: the array.

The problem is not that arrays are limited (though, for example, it’s impossible to determine the size of array at runtime). Instead, the main problem is that many problems require a container with greater functionality.

For example, we may need one or more of the following operations:
  • Add some string to a container.
  • Remove a string from a container.
  • Determine whether a string is present in the container.
  • Return a number of distinct elements in a container.
  • Iterate through a container and get a list of added strings in some order.
Of course, one can implement this functionality in an ordinal array. But the trivial implementation would be very inefficient. You can create the tree- of hash- structure to solve it in a faster way, but think a bit: does the implementation of such a container depend on elements we are going to store? Do we have to re-implement the module to make it functional, for example, for points on a plane but not strings?

If not, we can develop the interface for such a container once, and then use everywhere for data of any type. That, in short, is the idea of STL containers.

Before we begin
When the program is using STL, it should #include the appropriate standard headers. For most containers the title of standard header matches the name of the container, and no extension is required. For example, if you are going to use stack, just add the following line at the beginning of your program:
 
 
#include  < stack >

Container types (and algorithms, functors and all STL as well) are defined not in global namespace, but in special namespace called “std." Add the following line after your includes and before the code begin:
 
 
using   namespace  std;

Another important thing to remember is that the type of a container is the template parameter. Template parameters are specified with the ‘<’/’>’ "brackets" in code. For example:
 
 
vector < int >  N;

When making nested constructions, make sure that the "brackets" are not directly following one another – leave a blank between them.
 
 
vector <  vector < int >   >  CorrectDefinition; 
vector
< vector < int >>  Wrong;  //  Wrong: compiler may be confused by 'operator >>'

Vector
The simplest STL container is vector. Vector is just an array with extended functionality. By the way, vector is the only container that is backward-compatible to native C code – this means that vector actually IS the array, but with some additional features.
 
 
 vector < int >  v( 10 ); 
 
for ( int  i  =   0 ; i  <   10 ; i ++
      v[i] 
= (i+1)*(i+1); 
 }
 
 
for ( int  i  =   9 ; i  >   0 ; i --
      v[i] 
-= v[i-1]; 
 }
 

Actually, when you type
 
 
 vector < int >  v; 

the empty vector is created. Be careful with constructions like this:
 
 
 vector < int >  v[ 10 ]; 

Here we declare 'v' as an array of 10 vector<int>’s, which are initially empty. In most cases, this is not that we want. Use parentheses instead of brackets here. The most frequently used feature of vector is that it can report its size.
 
 
  int  elements_count  =  v.size();  

Two remarks: first, size() is unsigned, which may sometimes cause problems. Accordingly, I usually define macros, something like sz(C) that returns size of C as ordinal signed int. Second, it’s not a good practice to compare v.size() to zero if you want to know whether the container is empty. You're better off using empty() function:
 
 
  bool  is_nonempty_notgood  =  (v.size()  >=   0 );  //  Try to avoid this
  bool  is_nonempty_ok  =   ! v.empty(); 

This is because not all the containers can report their size in O(1), and you definitely should not require counting all elements in a double-linked list just to ensure that it contains at least one.

Another very popular function to use in vector is push_back. Push_back adds an element to the end of vector, increasing its size by one. Consider the following example:
 
 
 vector < int >  v; 
 
for ( int  i  =   1 ; i  <   1000000 ; i  *=   2
      v.push_back(i); 
 }
 
 
int  elements_count  =  v.size(); 

Don’t worry about memory allocation -- vector will not allocate just one element each time. Instead, vector allocates more memory then it actually needs when adding new elements with push_back. The only thing you should worry about is memory usage, but at TopCoder this may not matter. (More on vector’s memory policy later.)

When you need to resize vector, use the resize() function:
 
 
 vector < int >  v( 20 ); 
 
for ( int  i  =   0 ; i  <   20 ; i ++
      v[i] 
= i+1
 }
 
 v.resize(
25 ); 
 
for ( int  i  =   20 ; i  <   25 ; i ++
      v[i] 
= i*2
 }
 

The resize() function makes vector contain the required number of elements. If you require less elements than vector already contain, the last ones will be deleted. If you ask vector to grow, it will enlarge its size and fill the newly created elements with zeroes.

Note that if you use push_back() after resize(), it will add elements AFTER the newly allocated size, but not INTO it. In the example above the size of the resulting vector is 25, while if we use push_back() in a second loop, it would be 30.
 vector < int >  v( 20 ); 
 
for ( int  i  =   0 ; i  <   20 ; i ++ ) ...
      v[i] 
= i+1
 }
 
 v.resize(
25 ); 
 
for ( int  i  =   20 ; i  <   25 ; i ++ ) ...
      v.push_back(i
*2); // Writes to elements with indices [25..30), not [20..25) !
 }
 

To clear a vector use clear() member function. This function makes vector to contain 0 elements. It does not make elements zeroes -- watch out -- it completely erases the container.

There are many ways to initialize vector. You may create vector from another vector:
 
 
 vector < int >  v1; 
 
//  ... 
 vector < int >  v2  =  v1; 
 vector
< int >  v3(v1); 

The initialization of v2 and v3 in the example above are exactly the same.

If you want to create a vector of specific size, use the following constructor:
 
 
 vector < int >  Data( 1000 ); 

In the example above, the data will contain 1,000 zeroes after creation. Remember to use parentheses, not brackets. If you want vector to be initialized with something else, write it in such manner:
 
 
 vector < string >  names( 20 , “Unknown”); 

Remember that you can create vectors of any type.

Multidimensional arrays are very important. The simplest way to create the two-dimensional array via vector is to create a vector of vectors.
 
 
 vector <  vector < int >   >  Matrix; 

It should be clear to you now how to create the two-dimensional vector of given size:
 
 
  int  N, N; 
 
//  ... 
 vector <  vector < int >   >  Matrix(N, vector < int > (M,  - 1 )); 

Here we create a matrix of size N*M and fill it with -1.

The simplest way to add data to vector is to use push_back(). But what if we want to add data somewhere other than the end? There is the insert() member function for this purpose. And there is also the erase() member function to erase elements, as well. But first we need to say a few words about iterators.

You should remember one more very important thing: When vector is passed as a parameter to some function, a copy of vector is actually created. It may take a lot of time and memory to create new vectors when they are not really needed. Actually, it’s hard to find a task where the copying of vector is REALLY needed when passing it as a parameter. So, you should never write:
  void  some_function(vector < int >  v) ... // Never do it unless you’re sure what you do! 
      
// ... 
 }
 

Instead, use the following construction:
 void some_function(const vector<int>& v) { // OK 
// ...
}
If you are going to change the contents of vector in the function, just omit the ‘const’ modifier.
 
 
  int  modify_vector(vector < int >&  v)  // Correct 
      V[0]++
 }
 

Pairs
Before we come to iterators, let me say a few words about pairs. Pairs are widely used in STL. Simple problems, like TopCoder SRM 250 and easy 500-point problems, usually require some simple data structure that fits well with pair. STL std::pair is just a pair of elements. The simplest form would be the following:
 
 
 template < typename T1, typename T2 >   struct  pair 
      T1 first; 
      T2 second; 
 }

In general pair<int,int> is a pair of integer values. At a more complex level, pair<string, pair<int, int> > is a pair of string and two integers. In the second case, the usage may be like this:
 
 
 pair < string , pair < int , int >   >  P; 
 
string  s  =  P.first;  //  extract string 
  int  x  =  P.second.first;  //  extract first int 
  int  y  =  P.second.second;  //  extract second int 

The great advantage of pairs is that they have built-in operations to compare themselves. Pairs are compared first-to-second element. If the first elements are not equal, the result will be based on the comparison of the first elements only; the second elements will be compared only if the first ones are equal. The array (or vector) of pairs can easily be sorted by STL internal functions.

For example, if you want to sort the array of integer points so that they form a polygon, it’s a good idea to put them to the vector< pair<double, pair<int,int> >, where each element of vector is { polar angle, { x, y } }. One call to the STL sorting function will give you the desired order of points.

Pairs are also widely used in associative containers, which we will speak about later in this article.

Iterators
What are iterators? In STL iterators are the most general way to access data in containers. Consider the simple problem: Reverse the array A of N int’s. Let’s begin from a C-like solution:
 
 
  void  reverse_array_simple( int   * A,  int  N) 
      
int first = 0, last = N-1// First and last indices of elements to be swapped 
      While(first < last) // Loop while there is something to swap 
           swap(A[first], A[last]); // swap(a,b) is the standard STL function 
           first++// Move first index forward 
           last--// Move last index back 
      }
 
 }
 

This code should be clear to you. It’s pretty easy to rewrite it in terms of pointers:
 
 
  void  reverse_array( int   * A,  int  N) 
      
int *first = A, *last = A+N-1
      
while(first < last) 
           Swap(
*first, *last); 
           first
++
           last
--
      }
 
 }
 

Look at this code, at its main loop. It uses only four distinct operations on pointers 'first' and 'last':
  • compare pointers (first < last),
  • get value by pointer (*first, *last),
  • increment pointer, and
  • decrement pointer
Now imagine that you are facing the second problem: Reverse the contents of a double-linked list, or a part of it. The first code, which uses indexing, will definitely not work. At least, it will not work in time, because it’s impossible to get element by index in a double-linked list in O(1), only in O(N), so the whole algorithm will work in O(N^2). Errr...

But look: the second code can work for ANY pointer-like object. The only restriction is that that object can perform the operations described above: take value (unary *), comparison (<), and increment/decrement (++/--). Objects with these properties that are associated with containers are called iterators. Any STL container may be traversed by means of an iterator. Although not often needed for vector, it’s very important for other container types.

So, what do we have? An object with syntax very much like a pointer. The following operations are defined for iterators:
  • get value of an iterator, int x = *it;
  • increment and decrement iterators it1++, it2--;
  • compare iterators by '!=' and by '<'
  • add an immediate to iterator it += 20; <=> shift 20 elements forward
  • get the distance between iterators, int n = it2-it1;
But instead of pointers, iterators provide much greater functionality. Not only can they operate on any container, they may also perform, for example, range checking and profiling of container usage.

And the main advantage of iterators, of course, is that they greatly increase the reuse of code: your own algorithms, based on iterators, will work on a wide range of containers, and your own containers, which provide iterators, may be passed to a wide range of standard functions.

Not all types of iterators provide all the potential functionality. In fact, there are so-called "normal iterators" and "random access iterators". Simply put, normal iterators may be compared with ‘==’ and ‘!=’, and they may also be incremented and decremented. They may not be subtracted and we can not add a value to the normal iterator. Basically, it’s impossible to implement the described operations in O(1) for all container types. In spite of this, the function that reverses array should look like this:
 
 
 template < typename T >   void  reverse_array(T  * first, T  * last) 
      
if(first != last) 
           
while(true
                swap(
*first, *last); 
                first
++
                
if(first == last) 
                     
break
                }
 
                last
--
                
if(first == last) 
                     
break
                }
 
           }
 
      }
 
 }
 

The main difference between this code and the previous one is that we don’t use the "<" comparison on iterators, just the "==" one. Again, don’t panic if you are surprised by the function prototype: template is just a way to declare a function, which works on any appropriate parameter types. This function should work perfectly on pointers to any object types and with all normal iterators.

Let's return to the STL. STL algorithms always use two iterators, called "begin" and "end." The end iterator is pointing not to the last object, however, but to the first invalid object, or the object directly following the last one. It’s often very convenient.

Each STL container has member functions begin() and end() that return the begin and end iterators for that container.

Based on these principles, c.begin() == c.end() if and only if c is empty, and c.end() – c.begin() will always be equal to c.size(). (The last sentence is valid in cases when iterators can be subtracted, i.e. begin() and end() return random access iterators, which is not true for all kinds of containers. See the prior example of the double-linked list.)

The STL-compliant reverse function should be written as follows:
 
 
 template < typename T >   void  reverse_array_stl_compliant(T  * begin, T  * end) 
      
// We should at first decrement 'end' 
      
// But only for non-empty range 
      if(begin != end) 
      

           end
--
           
if(begin != end) 
                
while(true
                     swap(
*begin, *end); 
                     begin
++
                     If(begin 
== end) 
                          
break
                     }
 
                     end
--
                     
if(begin == end) 
                          
break
                     }
 
                }
 
           }
 
      }
 
 }
 

Note that this function does the same thing as the standard function std::reverse(T begin, T end) that can be found in algorithms module (#include <algorithm>).

In addition, any object with enough functionality can be passed as an iterator to STL algorithms and functions. That is where the power of templates comes in! See the following examples:
 
 
 vector < int >  v; 
 
//  ... 
 vector < int >  v2(v); 
 vector
< int >  v3(v.begin(), v.end());  //  v3 equals to v2 

 
int  data[]  =   235711131719232931 }
 vector
< int >  primes(data, data + ( sizeof (data)  /   sizeof (data[ 0 ]))); 

The last line performs a construction of vector from an ordinal C array. The term 'data' without index is treated as a pointer to the beginning of the array. The term 'data + N' points to N-th element, so, when N if the size of array, 'data + N' points to first element not in array, so 'data + length of data' can be treated as end iterator for array 'data'. The expression 'sizeof(data)/sizeof(data[0])' returns the size of the array data, but only in a few cases, so don’t use it anywhere except in such constructions. (C programmers will agree with me!)

Furthermore, we can even use the following constructions:
 
 
 vector < int >  v; 
 
//  ... 
 vector < int >  v2(v.begin(), v.begin()  +  (v.size() / 2 )); 

It creates the vector v2 that is equal to the first half of vector v.

Here is an example of reverse() function:
 
 
  int  data[ 10 =   135791113151719 }
 reverse(data
+ 2 , data + 6 );  //  the range { 5, 7, 9, 11 } is now { 11, 9, 7, 5 }; 

Each container also has the rbegin()/rend() functions, which return reverse iterators. Reverse iterators are used to traverse the container in backward order. Thus:
 
 
 vector < int >  v; 
 vector
< int >  v2(v.rbegin() + (v.size() / 2 ), v.rend()); 

will create v2 with first half of v, ordered back-to-front.

To create an iterator object, we must specify its type. The type of iterator can be constructed by a type of container by appending “::iterator”, “::const_iterator”, “::reverse_iterator” or “::const_reverse_iterator” to it. Thus, vector can be traversed in the following way:
 
 
 vector < int >  v; 

 
//  ... 

 
//  Traverse all container, from begin() to end() 
  for (vector < int > ::iterator it  =  v.begin(); it  !=  v.end(); it ++
      
*it++// Increment the value iterator is pointing to 
 }
 

I recommend you use '!=' instead of '<', and 'empty()' instead of 'size() != 0' -- for some container types, it’s just very inefficient to determine which of the iterators precedes another.

Now you know of STL algorithm reverse(). Many STL algorithms are declared in the same way: they get a pair of iterators – the beginning and end of a range – and return an iterator.

The find() algorithm looks for appropriate elements in an interval. If the element is found, the iterator pointing to the first occurrence of the element is returned. Otherwise, the return value equals the end of interval. See the code:
 
 
 vector < int >  v; 
 
for ( int  i  =   1 ; i  <   100 ; i ++
      v.push_back(i
*i); 
 }
 

 
if (find(v.begin(), v.end(),  49 !=  v.end()) 
      
// ... 
 }
 

To get the index of element found, one should subtract the beginning iterator from the result of find():
 
 
  int  i  =  (find(v.begin(), v.end(),  49 -  v.begin(); 
 
if (i  <  v.size()) 
      
// ... 
 }
 

Remember to #include <algorithm> in your source when using STL algorithms.

The min_element and max_element algorithms return an iterator to the respective element. To get the value of min/max element, like in find(), use *min_element(...) or *max_element(...), to get index in array subtract the begin iterator of a container or range:
  int  data[ 5 =   15243 }
 vector
< int >  X(data, data + 5 ); 
 
int  v1  =   * max_element(X.begin(), X.end());  //  Returns value of max element in vector 
  int  i1  =  min_element(X.begin(), X.end()) – X.begin;  //  Returns index of min element in vector 

 
int  v2  =   * max_element(data, data + 5 );  //  Returns value of max element in array 
  int  i3  =  min_element(data, data + 5 ) – data;  //  Returns index of min element in array

Now you may see that the useful macros would be:
 
 
  #define  all(c) c.begin(), c.end() 

Don’t put the whole right-hand side of these macros into parentheses -- that would be wrong!

Another good algorithm is sort(). It's very easy to use. Consider the following examples:
 vector < int >  X; 

 
//  ... 

 sort(X.begin(), X.end()); 
//  Sort array in ascending order 
 sort(all(X));  //  Sort array in ascending order, use our #define 
 sort(X.rbegin(), X.rend());  // Sort array in descending order using with reverse iterators 

Compiling STL Programs
One thing worth pointing out here is STL error messages. As the STL is distributed in sources, and it becomes necessary for compilers to build efficient executables, one of STL's habits is unreadable error messages.

For example, if you pass a vector<int> as a const reference parameter (as you should do) to some function:
 
 
  void  f( const  vector < int >&  v) 
      
for
           vector
<int>::iterator it = v.begin(); // hm... where’s the error?.. 
           
// ... 
      
// ... 
 }
 

The error here is that you are trying to create the non-const iterator from a const object with the begin() member function (though identifying that error can be harder than actually correcting it). The right code looks like this:
 
 
  void  f( const  vector < int >&  v) 
      
int r = 0
      
// Traverse the vector using const_iterator 
      for(vector<int>::const_iterator it = v.begin(); it != v.end(); it++
           r 
+= (*it)*(*it); 
      }
 
      
return r; 
 }
 

In spite of this, let me tell about very important feature of GNU C++ called ' typeof'. This operator is replaced to the type of an expression during the compilation. Consider the following example:
 
 
  typeof (a + b) x  =  (a + b); 

This will create the variable x of type matching the type of (a+b) expression. Beware that typeof(v.size()) is unsigned for any STL container type. But the most important application of typeof for TopCoder is traversing a container. Consider the following macros:
 
 
  #define  tr(container, it)  
 
for ( typeof (container.begin()) it  =  container.begin(); it  !=  container.end(); it ++ )

By using these macros we can traverse every kind of container, not only vector. This will produce const_iterator for const object and normal iterator for non-const object, and you will never get an error here.
 
 
  void  f( const  vector < int >&  v) 
      
int r = 0
      tr(v, it) 

           r 
+= (*it)*(*it); 
      }
 
      
return r; 
 }
 

Note: I did not put additional parentheses on the #define line in order to improve its readability. See this article below for more correct #define statements that you can experiment with in practice rooms.

Traversing macros is not really necessary for vectors, but it’s very convenient for more complex data types, where indexing is not supported and iterators are the only way to access data. We will speak about this later in this article.

Data manipulation in vector
One can insert an element to vector by using the insert() function:
 
 
 vector < int >  v; 
 
//  ... 
 v.insert( 1 42 );  //  Insert value 42 after the first 

All elements from second (index 1) to the last will be shifted right one element to leave a place for a new element. If you are planning to add many elements, it's not good to do many shifts – you're better off calling insert() one time. So, insert() has an interval form:
 vector < int >  v; 
 vector
< int >  v2; 

 
//  .. 

 
//  Shift all elements from second to last to the appropriate number of elements. 
 
//  Then copy the contents of v2 into v. 
 v.insert( 1 , all(v2)); 

Vector also has a member function erase, which has two forms. Guess what they are:
 
 
 erase(iterator); 
 erase(begin iterator, end iterator); 

At first case, single element of vector is deleted. At second case, the interval, specified by two iterators, is erased from vector.

The insert/erase technique is common, but not identical for all STL containers. 

See next: Power up C++ with STL: Part II (string, set, map)
width="728" scrolling="no" height="90" frameborder="0" align="middle" src="http://download1.csdn.net/down3/20070601/01184120111.htm" marginheight="0" marginwidth="0">
[编辑本段]Turbo C2.0    介绍      Turbo C2.0不仅是一个快捷、高效的编译程序,同时还有一个易学、易用的集成开发环境。使用Turbo C2.0无需独立地编辑、编译和连接程序,就能建立并运行C语言程序。因为这些功能都组合在Turbo 2.0的集成开发环境内,并且可以通过一个简单的主屏幕使用这些功能。    基本配置要求   Turbo C 2.0可运行于IBM-PC系列微机,包括XT,AT及IBM 兼容机。此时要求DOS2.0或更高版本支持,并至少需要448K的RAM,可在任何彩、单色80列监视器上运行。支持数学协处理器芯片,也可进行浮点仿真,这将加快程序的执行。 [编辑本段]Turbo C 2.0的主要文件的简单介绍   INSTALL.EXE 安装程序文件   TC.EXE 集成编译   TCINST.EXE 集成开发环境的配置设置程序   TCHELP.TCH 帮助文件   THELP.COM 读取TCHELP.TCH的驻留程序README 关于Turbo C的信息文件   TCCONFIG.EXE 配置文件转换程序MAKE.EXE   项目管理工具TCC.EXE   命令行编译TLINK.EXE   Turbo C系列连接器TLIB.EXE   Turbo C系列库管理工具C0?.OBJ 不   同模式启动代码C?.LIB   不同模式运行库GRAPHICS.LIB   图形库EMU.LIB   8087仿真库FP87.LIB 8087库   *.H Turbo C头文件   *.BGI 不同显示器图形驱动程序   *.C Turbo C例行程序(源文件)   其中:上面的?分别为:T Tiny(微型模式)S Small(小模式)C Compact(紧凑模式)M Medium(中型模式)L Large(大模式)H Huge(巨大模式)    Turbo C++ 3.0   “Turbo C++ 3.0”软件是Borland公司在1992年推出的强大的——C语言程序设计与C++面向对象程序设计 的集成开发工具。它只需要修改一个设置选项,就能够在同一个IDE集成开发环境下设计和编译以标准 C 和 C++ 语法设计的程序文件。 [编辑本段]C 语言   C语言起始于1968年发表的CPL语言,它的许多重要思想都来自于Martin Richards在1969年研制的BCPL语言,以及以BCPL语言为基础的与Ken Thompson在1970年研制的B语言。Ken Thompson用B语言写了第一个UNIX操作系统。M.M.Ritchie1972年在B语言的基础上研制了C语言,并用C语言写成了第一个在PDP-11计算机上研制的UNIX操作系统。1977年出现了独立于极其的C语言编译文本《看移植C语言编译程序》,从而大大简化了把C语言编译程序移植到新环境中所做的工作,这本身也就使UNIX的日益广泛使用,C语言也迅速得到推广。   1983年美国国家标准化协会(ANSI)根据C语言问世以来的各种版本,对C语言的发展和扩充制定了新的标准,成为ANSI C。1987年ANSI又公布了新标准————87ANSI C。   目前在微型计算机上使用的有Microsoft C、Quick C、Turbo C等多种版本。这些不同的C语言版本,基本部分是相同的,但是在有关规定上有略有差异。   C 语言发展如此迅速, 而且成为最受欢迎的语言之一, 主要因为它具有强大的功能。许多著名的系统软件, 如DBASE Ⅲ PLUS、DBASE Ⅳ 都是由C 语言编写的。用C 语言加上一些汇编语言子程序, 就更能显示C 语言的优势了,象PC- DOS ,WORDSTAR等就是用这种方法编写的。归纳起来C 语言具有下列特点:   1. C是中级语言   它把高级语言的基本结构和语句与低级语言的实用性结合起来。C 语言可以象汇编语言一样对位、字节和地址进行操作, 而这三者是计算机最基本的工作单元。   2. C是结构式语言   结构式语言的显著特点是代码及数据的分隔化, 即程序的各个部分除了必要的信息交流外彼此独立。这种结构化方式可使程序层次清晰, 便于使用、维护以及调试。C 语言是以函数形式提供给用户的, 这些函数可方便的调用, 并具有多种循环、条件语句控制程序流向, 从而使程序完全结构化。   3. C语言功能齐全   C 语言具有各种各样的数据类型, 并引入了指针概念, 可使程序效率更高。另外C 语言也具有强大的图形功能, 支持多种显示器和驱动器。而且计算功能、逻辑判断功能也比较强大, 可以实现决策目的。   4. C语言适用范围大   C 语言还有一个突出的优点就是适合于多种操作系统, 如DOS、UNIX,也适用于多种机型。   C语言的优点很多,但是也存在一些缺点,如运算优先级太多,运算能力方面不像其它高级语言那样强,语法定义不严格等。但是这些都不能阻止C语言成为一门广受欢迎的计算机编程语言
Turbo C2.0 介绍   Turbo C2.0不仅是一个快捷、高效的编译程序,同时还有一个易学、易用的集成开发环境。使用Turbo C2.0无需独立地编辑、编译和连接程序,就能建立并运行C语言程序。因为这些功能都组合在Turbo 2.0的集成开发环境内,并且可以通过一个简单的主屏幕使用这些功能。 基本配置要求   Turbo C 2.0可运行于IBM-PC系列微机,包括XT,AT及IBM 兼容机。此时要求DOS2.0或更高版本支持,并至少需要448K的RAM,可在任何彩、单色80列监视器上运行。支持数学协处理器芯片,也可进行浮点仿真,这将加快程序的执行。 主要文件的简单介绍   INSTALL.EXE 安装程序文件   TC.EXE 集成编译   TCINST.EXE 集成开发环境的配置设置程序   TCHELP.TCH 帮助文件   THELP.COM 读取TCHELP.TCH的驻留程序README 关于Turbo C的信息文件   TCCONFIG.EXE 配置文件转换程序MAKE.EXE   项目管理工具TCC.EXE   命令行编译TLINK.EXE   Turbo C系列连接器TLIB.EXE   Turbo C系列库管理工具C0?.OBJ 不   同模式启动代码C?.LIB   不同模式运行库GRAPHICS.LIB   图形库EMU.LIB   8087仿真库FP87.LIB 8087库   *.H Turbo C头文件   *.BGI 不同显示器图形驱动程序   *.C Turbo C例行程序(源文件)   其中:上面的?分别为:T Tiny(微型模式)S Small(小模式)C Compact(紧凑模式)M Medium(中型模式)L Large(大模式)H Huge(巨大模式)
### 回答1: 在 TURBO C 2.0 中,您可以使用 biosdisk() 函数来格式化软盘。 下面是一段代码示例: ``` #include <stdio.h> #include <dos.h> int main() { // 定义扇区的大小 unsigned int sector_size[3] = {512, 512, 1024}; // 定义填充字节 unsigned char fill_char[3] = {0xF6, 0xF6, 0x48}; // 格式化磁道 700 的前三个扇区 for (int i = 0; i < 3; i++) { int result = biosdisk(2, 0, 70, 0, i + 1, 1, sector_size[i], fill_char[i]); if (result != 0) { printf("格式化扇区 %d 失败,返回码: %d\n", i + 1, result); } } return 0; } ``` 请注意,在使用 biosdisk() 函数之前,需要包含 dos.h 头文件。 希望这能帮到您! ### 回答2使用TURBO C 2.0语言,可以通过以下代码格式化软盘的70磁道0面,并分别格式化3个扇区,大小分别为512字节、512字节和1024字节。其中,前两个扇区使用F6填充,第三个扇区使用48填充。 ```c #include<stdlib.h> #include<stdio.h> #include<dos.h> void formatFloppyDisk(){ union REGS regs; regs.h.ah = 0x0;// To format a floppy disk, we set AH=0 regs.h.dl = 0;// Drive number (0=A, 1=B, etc.) regs.x.cx = 0;// Track number to format regs.h.dh = 0;// Head number regs.h.al = 0;// Sector size (0=default, 1=512 bytes, 2=1024 bytes, 3=2048 bytes etc.) int FILL_BYTE = 0;// The byte value to fill the sectors with during formatting int NUM_SECTORS = 3;// Number of sectors to format // To format 70th track 0th head regs.x.ax = 0x1301; // 0x13 = Reset disk system, 01H = Reset only specified drive int86(0x13, &regs, &regs); // BIOS interrupt to reset disk system for (int i=0; i<NUM_SECTORS; i++){ regs.x.ax = 0x3101; // 0x31 = Write Format, 01H = Format only current track regs.x.bx = 0x0001; // 0x00 = Drive A:, 01H = Head 1, 0 = Generate ID Field depending on the disk in the drive 1 = Keep the ID Field all zeros regs.x.cx = 0x0170; // Track number=70(0-79 range) regs.h.dh = 0x00; // Head number=0 or 1 regs.h.al = 0x02; // Control byte=always zero regs.x.dx = i+1; // Sector number starting from 1 regs.x.si = 0x0000; // segment and offset of read/write buffer regs.x.di = 0x0000; // segment and offset of result if(i == 2){ FILL_BYTE = 0x48; // Fill the third sector with 48 regs.x.ax = 0x3102; // 0x31 = Write Format, 02H = Format sequential tracks immediately following the one being formatted }else{ FILL_BYTE = 0xF6; // Fill the first two sectors with F6 } regs.h.ah = FILL_BYTE; // Fill the sector with specified byte int86(0x13, &regs, &regs); // BIOS interrupt to format the specified sector } } int main(){ formatFloppyDisk(); return 0; } ``` 上述代码使用了INT 0x13,即BIOS中断服务例程,来执行软盘格式化操作。通过设置寄存器的不同参数,可以指定要格式化的磁道、面、扇区大小和填充字节。在这个例子中,我们格式化了软盘70磁道0面的3个扇区,前两个扇区使用F6填充,第三个扇区使用48填充。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值