C++

strcpy和strcpy_s函数

最近有同学在编写作业时,用了字符串拷贝库函数strcpy,被编译器提示不安全,要求用strcpy_s代替。

一个典型的自定义strcpy函数代码可能如下:


char * mystrcpy(char *des , const char *src)
{
    char *t = des;
    while(*des++ = *src++)
        ;
    return t;
}

这个代码的问题在于如果des指向的空间不足以存放字符串src的内容,就可能会导致数据冲突或程序崩溃。

在高版本的vs当中,微软推出了更安全的strcpy_s函数。其函数声明为:

三个参数版本
errno_t strcpy_s(
    char *strDestination,
    size_t numberOfElements,
    const char *strSource
);
两个参数版本
template <size_t size>
errno_t strcpy_s(
    char (&strDestination)[size],
   const char *strSource
); // C++ only

其中三个参数的版本可以为c/c++通用,需要程序员主动指明可接受赋值的空间大小,这样避免只传递指针而无法控制复制长度的问题,通用性较强;而两个参数的版本使用了模板和对数组的引用技术,只能对c++使用,而且第一个参数要求必须是字符数组(不能是字符指针),否则无法运行。

不过这两个函数不是c++标准库函数,在linux环境下需要进行移植或者用strncpy函数(该函数也可以指定拷贝的数量)代替。

含有类、动态分配内存、插入和提取运算符重载、文件读写的程序示例

程序说明

  1. 在类当中定义提取和插入运算符重载,并对参数分别进行检测,如果是输入或输出对象则进行额外处理。
  2. 在写文件时需要写入字符串的长度,但输出不需要。读文件时需要读取字符串的长度,但输入不需要。
  3. 为了简化步骤,输入时控制了字符串的长度,否则可以用链表等不定长的容器来获取输入。
  4. 出于简化问题的角度,本程序把保存数据用的文件名固定了,因此如果创建需要多个对象(包括复制构造函数和赋值运算符等)时可能会出现问题(会导致数据的覆盖等)。
#include <iostream>
#include <fstream>
using namespace std;

class rect;
istream& operator >>(istream & is , rect & r);
ostream& operator <<(ostream & os , const rect & r);

class rect
{
    int left , top , right , bottom;
    char *info;
public:
    rect()
    {
        ifstream infile("rect.txt");
        if(infile)
        {
            infile >> *this;
            infile.close();
        }
        else
            cin >> *this;
    }
    ~rect()
    {
        ofstream outfile("rect.txt");
        outfile << *this;
        outfile.close();
        delete []info;
    }
    friend istream& operator >>(istream & is , rect & r)
    {
        int len;
        if(&is == &cin)
        {
            char c[20];
            cout << "请输入矩形的四个坐标:";
            cin >> r.left >> r.top >> r.right >> r.bottom;
            cin.get();
            cout << "请输入矩形的描述(20字符以内):";
            cin.getline(c , 20);
            len = strlen(c);
            r.info = new char[len + 1];
            strcpy(r.info , c);
        }
        else
        {
            is >> r.left >> r.top >> r.right >> r.bottom >> len;
            r.info = new char[len + 1];
            is.get();
            is.getline(r.info , len + 1);
        }
        return is;
    }
    friend ostream& operator <<(ostream & os , rect & r)
    {
        os << r.left << ' ' << r.top << ' ' << r.right << ' ' << r.bottom << endl;
        if(&os != &cout)
            os << strlen(r.info) << endl;
        return os << r.info << endl;
    }
};
int main()
{
    rect r;
    cout << r;
    return 0;
}