const char *在分配给char *后被修改

时间:2014-08-23 09:03:07

标签: c++

int FunctionName(const char *pValueName, const char *pValueData, long iMaxValueSize)
{
  char *pDataToStore = const_cast<char *>(pValueData);
  int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);
...
...
}

在上面的代码片段中,ProcessData()函数修改它作为参数接收的char *。现在,即使在将pValueData分配到pDataToStore之后,在执行ProcessData()之后,pValueData的值也与pDataToStore相同。

我的目标是保持pValueData的完整值,该值作为const char *

传递

4 个答案:

答案 0 :(得分:2)

  

我的目标是保持传递的pValueData的完整值   const char *

这是不可能的。通过const传递意味着它不能被修改,除非它最初不是常数。

示例:

char *ptr1 = new char[100]; // not const
char *ptr2 = new char[100]; // not const
int i = FunctionName(ptr1, ptr2, 123);

在这种情况下,您可以在技术上保留const_cast。但是为了什么?只需更改您的函数参数即可char *

int FunctionName(char *pValueName, char *pValueData, long iMaxValueSize)
{
  int iActualSiz = ProcessData(pValueData, iMaxValueSize);
  // ...
}

但是,您很可能希望能够传递常量字符串。例如字符串文字:

int i = FunctionName("name", "data", 123);

字符串文字是不可修改的,因此需要您的函数取char const *。稍后尝试修改它们会导致未定义的行为。


如您所见,错误在于通用架构和代码逻辑。 您想要修改某些内容,同时又不想允许修改它。

问题是:pDataToStore完成ProcessData后会发生什么? FunctionName的来电者是否需要了解修改?或者它只是FunctionName的内部业务?

如果它只是FunctionName的内部业务,那么您可以保持其签名不变,并ProcessData修改传递数据的副本。这是一个简化(不是异常安全,没有错误检查)示例:

int FunctionName(const char *pValueName, const char *pValueData, long iMaxValueSize)
{
   char *copy = new char[strlen(pValueData) + 1];
   strcpy(copy, pValueData):
   int iActualSiz = ProcessData(copy, iMaxValueSize);

   // ...

   delete[] copy;
}

好消息是,您现在可以通过隐藏所有低级指针业务来大规模改进FunctionName的接口。事实上,当C ++标准类可以为你完成所有工作时,为什么要使用这么多指针呢?

int FunctionName(std::string const &valueName, std::string const &valueData, long maxValueSize)
{
   std::vector<char> copy(valueData.begin(), valueData.end());
   int actualSize = ProcessData(&copy[0], maxValueSize);

   // ...
   // no more delete[] needed here
}

std::vector<char>会自动分配足够的内存来保存valueData的副本,然后执行复制。即使抛出异常,它也会在不再需要时完全自动释放内存。并且&copy[0](在C ++ 11中可以写为copy.data())保证产生指向内部使用数据的指针,因此低级C函数可以修改向量。元件。

(我也有机会删除微软风格的匈牙利表示法。这是90年代失败的实验,你甚至错误地使用它,假设一个前导i应该表示int。)


最重要的是:

如果您需要在代码中使用const_cast 进行编译,那么其他地方至少会有一个const 缺失< / em>或一个太多 A const_cast总是弥补另一段代码中的错误。它始终是一种解决方法,从来没有预先设计好的解决方案。

答案 1 :(得分:0)

我已经通过创建堆内存解决了这个问题。

char *pDataToStore = new char[iMaxValueSize];
memcpy(pDataToStore, pValueData, iMaxValueSize*sizeof(char));
int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);
...
....
delete []pDataToStore;

答案 2 :(得分:0)

您必须在 const限定类型 const限定对象 之间做出改变。

7.1.6.1:cv-qualifiers 部分中的标准状态:( cv = const或volatile)

  

指向cv限定类型的指针或引用实际上不需要指向   或者引用一个符合cv标准的对象,但它被视为它;一个   const限定访问路径即使不能用于修改对象   引用的对象是非const对象,可以修改   通过其他一些访问途径。

如果你的指针指向一个非const对象,那么cast away将使你能够修改objet,但正如有人告诉你的那样,你正在向你的函数用户撒谎。

你的指针指向一个真正的const对象(即在const保护的内存中),编译器将编译你的代码,但你可能有一个段错误,典型的未定义行为。

这是一个例子,使用“普通字符串文字(...)具有类型”n const char“数组的事实,其中n是字符串的大小(...)”(见标准,第2.14.5节):

char *my_realconst = "This is a real constant string";    // pointer does not claim that it points to const object 

(*my_realconst)++;  // Try to increment the first letter, will compile but will not run properly !! 

因此,如果您的函数ProcessData()是只读取数据但遗忘了参数列表中的const的遗留代码,那么您的抛弃将起作用。但是,如果您的功能正在改变数据,它可能会起作用,也可能会失败,具体取决于创建的数据的创建方式!

因此,如果你不是100%确定效果会是什么,请尽量避免使用const!更好地克隆您的对象,创建临时对象并复制内容。

答案 3 :(得分:0)

我建议你一个小模板来轻松处理这类问题:

template <typename T>
class Buffer { 
    size_t sz;      // size
    T* addr;        // pointed
public: 
    Buffer(const T*source, size_t l) : sz(l), addr(new T[l]) { std::copy(source, source + l, addr);  }  // allocate and copy
    ~Buffer() { delete[]addr; }   // destroy memory 
    operator T* () { return addr; }  // convert to pointer
};

您可以按原样使用现有代码:

Buffer<char> pDataToStore(pValueData, iMaxValueSize);  // create the automatic buffer
int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);  // automatic use of pointer to buffer
cout << "modified copy: " << pDataToStore << endl;
cout << "original:      " << pValueData << endl;

一旦pDataToStore不再在范围内,缓冲区将自动释放。

如果您对wchar_t缓冲区或其他任何内容有类似问题,它也可以正常工作。

关于抛弃const的邪恶的解释,请参阅我的其他答案