C++进阶版,有些没懂等个大佬

  • 用户定义的类
  • 类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法
  • 函数在一个类中被称为类的成员

在这里插入图片描述

img``

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
    return length * breadth * height;
}
 
void Box::set( double len, double bre, double hei)
{
    length = len;
    breadth = bre;
    height = hei;
}
int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box3;        // 声明 Box3,类型为 Box
   double volume = 0.0;     // 用于存储体积
 
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;
   // box 3 详述
   Box3.set(16.0, 8.0, 12.0); 
   volume = Box3.get(); 
   cout << "Box3 的体积:" << volume <<endl;
   return 0;
}

私有成员和受保护成员不饿能使用直接成员访问运算符(.)来直接访问

类成员函数

类成员函数是类的一个成员,可以操作类的任意对象,可以访问对象中的所有成员

  1. 直接在类内

    class Box
    {
       public:
          double length;         // 长度
          double breadth;        // 宽度
          double height;         // 高度
          double getVolume(void);// 返回体积
        {
             return length * breadth * height;
          }
    };
    
  2. 在类的外部

    double Box::getVolume(void)
    {
        return length * breadth * height;
    }
    Box  box1;
    box1.length=10;
    box1.getVolume();
    

    在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.

访问修饰符(public、private、protected)

构造函数&析构函数

类的构造函数是一种特殊的函数,在创建一个新的对象时调用。

class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 这是构造函数
 
   private:
      double length;
};
Line::Line(void)//可以带参
{
    cout << "Object is being created" << endl;
}
/*以下两种都一样
Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}
Line::Line( double len)
{
    length = len;
    cout << "Object is being created, length = " << len << endl;
}
*/
......//setLength   getLength
int main( )
{
   Line line;//创建了一个新的对象,调用Line()
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。

	  Line();   // 这是构造函数声明
      ~Line();  // 这是析构函数声明,在删除时调用

C++拷贝构造函数

  • 通过使用另一个同类型的对象来初始化新创建的对象。
  • 复制对象把它作为参数传递给函数。
  • 复制对象,并从函数返回这个对象。
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数
 
   private:
      int *ptr;
};
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值  obj 是一个对象引用,该对象是用于初始化另一个对象的。
}
int main( )
{
   Line line(10);//先Line(int len)再Line( const Line &obj);又因为结束两次所以      ~Line();调用两次
   display(line);
   return 0;
}

C++友元函数

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员,友元函数并不是成员函数。使用关键字friend

#include <iostream>
using namespace std;
class Box
{
   double width;
public:
   friend void printWidth( Box box );//友元函数
   void setWidth( double wid );
}; 
// 成员函数定义
void Box::setWidth( double wid ){ width = wid;}
// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
   /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
   cout << "Width of box : " << box.width <<endl;
} 
// 程序的主函数
int main( )
{
   Box box; 
   // 使用成员函数设置宽度
   box.setWidth(10.0);   
   // 使用友元函数输出宽度
   printWidth( box ); 
   return 0;
}
Width of box : 10

C++内联函数

对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。

内联函数的目的是解决程序中函数调用的效率问题,都是1-10行的小函数

#include <iostream>
using namespace std;

inline int Max(int x, int y)//内联函数,使用inline关键字,
{
   return (x > y)? x : y;
}
// 程序的主函数
int main( )
{
   cout << "Max (20,10): " << Max(20,10) << endl;
   cout << "Max (0,200): " << Max(0,200) << endl;
   cout << "Max (100,1010): " << Max(100,1010) << endl;
   return 0;
}

C++中的this指针

访问自己的地址,成员函数有this。友元函数没有this

#include <iostream> 
using namespace std;
 
class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      int compare(Box box)//比较的是位置
      {
         return this->Volume() > box.Volume();
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};
int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   if(Box1.compare(Box2))      //两个Constructor called.
   {
      cout << "Box2 is smaller than Box1" <<endl;
   }
   else
   {
      cout << "Box2 is equal to or larger than Box1" <<endl;
   }
   return 0;
}
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

C++中指向类的指针(还没太理解,等个大佬)

指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。

#include <iostream>
using namespace std;

class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};
int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   Box *ptrBox;                // Declare pointer to a class.指针
   // 保存第一个对象的地址
   ptrBox = &Box1;
   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;
   // 保存第二个对象的地址
   ptrBox = &Box2;
   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;  
   return 0;
}

C++类的静态成员

img

在创建第一个对象时,所有的静态数据都会被初始化为零。不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化

#include <iostream> 
using namespace std;
 
class Box//Box类
{
   public:
      static int objectCount;//静态类
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
int main(void)
{ cout << "Inital Stage Count: " << Box::getCount() << endl;
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1,调用1次
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2,调用2次
   // 输出对象的总数
   cout << "Total objects: " << Box::objectCount << endl; 
   return 0;
}
Inital Stage Count: 0
Constructor called.
Constructor called.
Total objects: 2

静态成员函数与普通成员函数的区别:

  • 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
  • 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。

继承

img

// 基类
class Animal {
     //eat() 函数
     //sleep() 函数
};
//派生类
class Dog : public Animal {
     //bark() 函数,Dog可以调用Animal中的函数
};
//可以从多个基类继承数据和函数。

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

继承类

/**/
0class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>//多继承
};
class Rectangle: public Shape, public PaintCost
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
};

重载运算符和重载函数

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

调用一个重载函数重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策

  • 函数重载

同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。

class printData
{
   public:
    /*重载 名字相同,形参不同*/
      void print(int i) {
        cout << "整数为: " << i << endl;
      }
 
      void print(double  f) {
        cout << "浮点数为: " << f << endl;
      }
 
      void print(char c[]) {
        cout << "字符串为: " << c << endl;
      }
};
    • 运算符重载 需要一个关键字
Box operator+(const Box&, const Box&);
// 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }

多态

当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

(2条消息) C++多态的用法详解_c++多态的使用方法_卖寂寞的小男孩的博客-CSDN博客

子继承了父亲的方法,调用时根据不同的类来判断是用的子还父

#include<iostream>
#include<string>
using namespace std;
class Person
{
public: 
	virtual void BuyTicket()
	{
		cout << "全价买票" << endl;
	}
};
class Student :public Person//学生这个类继承了人,
{
public:
	virtual void BuyTicket()//重载这个方法
	{
		cout << "半价买票" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person p1;//人这个类
	Student p2;//学生这个类
	Func(p1);
	Func(p2);
}

多态的构成满足两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的虚函数的派生类必须完成了对基类虚函数的重写。

数据抽象

外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。

数据抽象有两个重要的优势:

  • 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
  • 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。
class Adder{
   public:
      // 构造函数
      Adder(int i = 0)
      {
        total = i;
      }
      // 对外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 对外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 对外隐藏的数据
      int total;
};
int main( )
{
   Adder a;//定义类型
   
   a.addNum(10);//调用方法,通过接口
   a.addNum(20);
   a.addNum(30);
 
   cout << "Total " << a.getTotal() <<endl;//对外接口
   return 0;
}

数据封装

  • **程序语句(代码):**这是程序中执行动作的部分,它们被称为函数。
  • **程序数据:**数据是程序的信息,会受到程序函数的影响。

把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据隐藏

----**数据封装**是一种把数据和操作数据的函数捆绑在一起的机制,**数据抽象**是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。----

class Adder{
   public:
      // 构造函数
      Adder(int i = 0)
      {
        total = i;
      }
      // 对外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 对外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 对外隐藏的数据外部无法访问
      int total;
};

接口(抽象类)

C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。

**如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。**纯虚函数是通过在声明中使用 “= 0” 来指定的,

#include <iostream> 
using namespace std;
 
// 基类
class Shape 
{
public:
   // 提供接口框架的纯虚函数
   virtual int getArea() = 0;
   void setWidth(int w)
   {
      width = w;
   }
   void setHeight(int h)
   {
      height = h;
   }
protected:
   int width;
   int height;
};
 
// 派生类
class Rectangle: public Shape
{
public:
   int getArea()//继承Shape,使用他的方法,并重写
   { 
      return (width * height); 
   }
};
class Triangle: public Shape
{
public:
   int getArea()//继承Shape,使用他的方法,并重写
   { 
      return (width * height)/2; 
   }
};
 
int main(void)
{
   Rectangle Rect;//根据类型来判断调用那个
   Triangle  Tri;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
   // 输出对象的面积
   cout << "Total Rectangle area: " << Rect.getArea() << endl;
 
   Tri.setWidth(5);
   Tri.setHeight(7);
   // 输出对象的面积
   cout << "Total Triangle area: " << Tri.getArea() << endl; 
 
   return 0;
}

文件和流

数据类型描述
ofstream该数据类型表示输出文件流,用于创建文件并向文件写入信息。
ifstream该数据类型表示输入文件流,用于从文件读取信息。
fstream该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

C++ 文件和流 | 菜鸟教程 (runoob.com)

异常处理

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
  • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
  • 在不同的环境下,会有不同的异常

抛出异常

double division(int a, int b)
{
   if( b == 0 )//除数为0的情况
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}

捕获异常

try
{
   // 保护代码
}catch( ExceptionName e )
{
  // 处理 ExceptionName 异常的代码
}

C++ 异常的层次结构

异常描述
std::exception该异常是所有标准 C++ 异常的父类。
std::bad_alloc该异常可以通过 new 抛出。
std::bad_cast该异常可以通过 dynamic_cast 抛出。
std::bad_typeid该异常可以通过 typeid 抛出。
std::bad_exception这在处理 C++ 程序中无法预期的异常时非常有用。
std::logic_error理论上可以通过读取代码来检测到的异常。
std::domain_error当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument当使用了无效的参数时,会抛出该异常。
std::length_error当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator
std::runtime_error理论上不可以通过读取代码来检测到的异常。
std::overflow_error当发生数学上溢时,会抛出该异常。
std::range_error当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error当发生数学下溢时,会抛出该异常。

动态内存

  • **栈:**在函数内部声明的所有变量都将占用栈内存。
  • **堆:**这是程序中未使用的内存,在程序运行时可用于动态分配内存。
new data-type;//动态分配内存
double* pvalue  = NULL; // 初始化为 null 的指针
pvalue  = new double;   // 为变量请求内存,分配之前需要判断是否有空间

new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。

分配之后用delete删除

数组的动态内存分配

char* pvalue  = NULL;   // 初始化为 null 的指针
pvalue  = new char[20]; // 为变量请求内存
delete [] pvalue;        // 删除 pvalue 所指向的数组
多维数组
/*二维用一个for*/
int ***array;
// 假定数组第一维为 m, 第二维为 n, 第三维为h
// 动态分配空间
array = new int **[m];
for( int i=0; i<m; i++ )
{
    array[i] = new int *[n];
    for( int j=0; j<n; j++ )
    {
        array[i][j] = new int [h];
    }
}

命名空间

解决同名问题

#include <iostream>
using namespace std;
 
// 第一个命名空间
namespace first_space{//调用时first_space::func();
   void func(){
      cout << "Inside first_space" << endl;
   }
}
// 第二个命名空间
namespace second_space{//调用时 second_space::func(); 
   void func(){
      cout << "Inside second_space" << endl;
   }
}
/*
using namespace first_space;
int main ()
{
   // 调用第一个命名空间中的函数
   func();   
   return 0;
}
*/

还可以嵌套,类似于if else中套if else

模板

template <typename T>
inline T const& Max (T const& a, T const& b) 
{ 
    return a < b ? b:a; 
} 
//T是代表一种类型,可以是int/double/string等
cout << "Max(i, j): " << Max(i, j) << endl; //ij可以是各种相同形式的值

预处理器

  1. #define预处理

    #define macro-name replacement-text 
    
  2. #define定义参数

  3. 条件编译,类似于if 结构

#ifdef NULL
   #define NULL 0
#endif
  1. 描述
    LINE这会在程序编译时包含当前行号。
    FILE这会在程序编译时包含当前文件名。
    DATE这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。
    TIME这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。

信号处理

操作系统传给进程的中断,会提前终止一个程序。在C++的中

信号描述
SIGABRT程序的异常终止,如调用 abort
SIGFPE错误的算术运算,比如除以零或导致溢出的操作。
SIGILL检测非法指令。
SIGINT程序终止(interrupt)信号。
SIGSEGV非法访问内存。
SIGTERM发送到程序的终止请求。

signal()函数

捕获突发事件.

signal(registered signal, signal handler)

第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。

raise()函数

生成信号

int raise (signal sig);//sig 是要发送的信号的编号

多线程

(2条消息) 一文详解C++多线程_非晚非晚的博客-CSDN博客_c++多线程

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。

基于线程和基于进程

  • 基于进程的多任务处理是程序的并发执行。
  • 基于线程的多任务处理是同一程序的片段的并发执行。
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) //创建4种
/*终止线程*/
#include <pthread.h>
/*在线程完成工作后无需继续存在时被调用。*/
pthread_exit (status)
参数描述
thread指向线程标识符指针。
attr一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine线程运行函数起始地址,一旦线程被创建就会执行。
arg运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。
#include <iostream>
// 必须的头文件
#include <pthread.h>
 
using namespace std;
 
#define NUM_THREADS 5
 
// 线程的运行函数
void* say_hello(void* args)
{
    cout << "Hello Runoob!" << endl;
    return 0;
}
 
int main()
{
    // 定义线程的 id 变量,多个变量使用数组
    pthread_t tids[NUM_THREADS];
    for(int i = 0; i < NUM_THREADS; ++i)
    {
        //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
        int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
        if (ret != 0)
        {
           cout << "pthread_create error: error_code=" << ret << endl;
        }
    }
    //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
    pthread_exit(NULL);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV4I0GRW-1676386410534)(C:\Users\阿娆\AppData\Roaming\Typora\typora-user-images\image-20230214205725828.png)]

#include <iostream>
#include <cstdlib>
#include <pthread.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
void *PrintHello(void *threadid)
{  
   // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
   int tid = *((int*)threadid);
   cout << "Hello Runoob! 线程 ID, " << tid << endl;
   pthread_exit(NULL);
}
 
int main ()
{
   pthread_t threads[NUM_THREADS];
   int indexes[NUM_THREADS];// 用数组来保存i的值
   int rc;
   int i;
   for( i=0; i < NUM_THREADS; i++ ){      
      cout << "main() : 创建线程, " << i << endl;
      indexes[i] = i; //先保存i的值
      // 传入的时候必须强制转换为void* 类型,即无类型指针        
      rc = pthread_create(&threads[i], NULL, 
                          PrintHello, (void *)&(indexes[i]));
      if (rc){
         cout << "Error:无法创建线程," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGLQjXaS-1676386410534)(D:\1ar\c笔记\c++\linux多线程2.png)]

向线程传递参数

std::thread

Web编程

(2条消息) WEB之CGI----CGI详解(原理,配置及访问)_锥子A的博客-CSDN博客_cgi web

什么是 CGI?

  • 公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。
  • CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下:
  • 公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准。
  • 目前的版本是 CGI/1.1,CGI/1.2 版本正在推进中。

Web 浏览

为了更好地了解 CGI 的概念,让我们点击一个超链接,浏览一个特定的网页或 URL,看看会发生什么。

  • 您的浏览器联系上 HTTP Web 服务器,并请求 URL,即文件名。
  • Web 服务器将解析 URL,并查找文件名。如果找到请求的文件,Web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。
  • Web 浏览器从 Web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。

然而,以这种方式搭建起来的 HTTP 服务器,不管何时请求目录中的某个文件,HTTP 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。

公共网关接口(CGI),是使得应用程序(称为 CGI 程序或 CGI 脚本)能够与 Web 服务器以及客户端进行交互的标准协议。这些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等进行编写。

CGI 架构图

下图演示了 CGI 的架构:

CGI 架构

Web 服务器配置

在您进行 CGI 编程之前,请确保您的 Web 服务器支持 CGI,并已配置成可以处理 CGI 程序。所有由 HTTP 服务器执行的 CGI 程序,都必须在预配置的目录中。该目录称为 CGI 目录,按照惯例命名为 /var/www/cgi-bin。虽然 CGI 文件是 C++ 可执行文件,但是按照惯例它的扩展名是 .cgi

默认情况下,Apache Web 服务器会配置在 /var/www/cgi-bin 中运行 CGI 程序。如果您想指定其他目录来运行 CGI 脚本,您可以在 httpd.conf 文件中修改以下部分:

<Directory "/var/www/cgi-bin">
   AllowOverride None
   Options ExecCGI
   Order allow,deny
   Allow from all
</Directory>
 
<Directory "/var/www/cgi-bin">
Options All
</Directory>

HTTP 头信息

Content-type:text/html\r\n\r\n 是 HTTP 头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP 头信息的形式如下:

HTTP 字段名称: 字段内容
 
例如
Content-type: text/html\r\n\r\n

还有一些其他的重要的 HTTP 头信息,这些在您的 CGI 编程中都会经常被用到。

头信息描述
Content-type:MIME 字符串,定义返回的文件格式。例如 Content-type:text/html。
Expires: Date信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 Jan 1998 12:00:00 GMT。
Location: URL这个 URL 是指应该返回的 URL,而不是请求的 URL。你可以使用它来重定向一个请求到任意的文件。
Last-modified: Date资源的最后修改日期。
Content-length: N要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。
Set-Cookie: String通过 string 设置 cookie。

CGI 环境变量

所有的 CGI 程序都可以访问下列的环境变量。这些变量在编写 CGI 程序时扮演了非常重要的角色。

变量名描述
CONTENT_TYPE内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。
CONTENT_LENGTH查询的信息长度。只对 POST 请求可用。
HTTP_COOKIE以键 & 值对的形式返回设置的 cookies。
HTTP_USER_AGENT用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。
PATH_INFOCGI 脚本的路径。
QUERY_STRING通过 GET 方法发送请求时的 URL 编码信息,包含 URL 中问号后面的参数。
REMOTE_ADDR发出请求的远程主机的 IP 地址。这在日志记录和认证时是非常有用的。
REMOTE_HOST发出请求的主机的完全限定名称。如果此信息不可用,则可以用 REMOTE_ADDR 来获取 IP 地址。
REQUEST_METHOD用于发出请求的方法。最常见的方法是 GET 和 POST。
SCRIPT_FILENAMECGI 脚本的完整路径。
SCRIPT_NAMECGI 脚本的名称。
SERVER_NAME服务器的主机名或 IP 地址。
SERVER_SOFTWARE服务器上运行的软件的名称和版本。

CGI库安装

$ tar xzf cgicc-X.X.X.tar.gz 
$ cd cgicc-X.X.X/ 
$ ./configure --prefix=/usr 
$ make
$ make install

**注意:**libcgicc.so 和 libcgicc.a 库会被安装到/usr/lib目录下,需执行拷贝命令:

$ sudo cp /usr/lib/libcgicc.* /usr/lib64/

才能使 CGI 程序自动找到 libcgicc.so 动态链接库。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值