简体   繁体   English

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

[英]const char* getting modified after assigning to char*

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

In the upper code snippet ProcessData() function modifies the char*, which it receives as parameter. 在上面的代码中,ProcessData()函数修改了char *,它作为参数接收。 Now even after assigning pValueData into pDataToStore, after ProcessData() get executed, value of pValueData is being same as pDataToStore. 现在,即使在将pValueData分配给pDataToStore之后,在执行ProcessData()之后,pValueData的值也将与pDataToStore相同。

My aim is to keep intact value of pValueData which is being passed as const char* 我的目的是保持pValueData的完整值,该值作为const char *传递

My aim is to keep intact value of pValueData which is being passed as const char* 我的目的是保持pValueData的完整值,该值作为const char *传递

That's impossible. 这不可能。 Passing via const means it cannot be modified, except when it was originally not constant. 通过const传递意味着它不能被修改,除非它最初不是恒定的。

Example: 例:

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

In this case, you could technically keep the const_cast . 在这种情况下,您可以从技术上保留const_cast But what for? 但是为什么呢? Just change your function parameters to take char * : 只需将函数参数更改为char *

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

However, you most likely want to be able to pass constant strings. 但是,您最有可能希望能够传递常量字符串。 For example string literals: 例如字符串文字:

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

String literals are unmodifiable and thus require your function to take char const * . 字符串文字是不可修改的,因此要求您的函数采用char const * A later attempt to modify them causes undefined behaviour. 以后尝试修改它们会导致未定义的行为。


As you can see, the error is in the general architecture and code logic. 如您所见,该错误发生在常规体系结构和代码逻辑中。 You want to modify something and at the same time you do not want to allow to modify it. 您想要修改某些内容,但同时又不想允许对其进行修改。

The question is: What happens with your pDataToStore when ProcessData is done with it? 问题是:使用ProcessData完成pDataToStore会发生什么? Does the caller of FunctionName need to be aware of the modifications? FunctionName的调用者是否需要知道所做的修改? Or is it just internal business of FunctionName ? 还是FunctionName内部业务?

If it's just internal business of FunctionName , then you can keep its signature intact and have ProcessData modify a copy of the passed data. 如果这只是FunctionName的内部业务,则可以保持其签名不变,并让ProcessData修改传递的数据的副本。 Here is a simplified (not exception-safe, no error checks) example: 这是一个简化的示例(不是异常安全的,没有错误检查):

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;
}

The nice thing is that you can now massively improve the interface of FunctionName by hiding all the low-level pointer business. 令人高兴的是,您现在可以通过隐藏所有低级指针业务来大大改进FunctionName的接口。 In fact, why use so many pointers at all when C++ standard classes can do all the work for you? 实际上,当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
}

The std::vector<char> automatically allocates enough memory to hold a copy of valueData , and performs the copy. std::vector<char>自动分配足够的内存来保存valueData的副本,并执行该副本。 It fully automatically frees the memory when it is no longer needed, even if exceptions are thrown. 即使不再抛出异常,它也会在不再需要时自动释放内存。 And &copy[0] (which in C++11 can be written as copy.data() ) is guaranteed to yield a pointer to the internally used data, so that low-level C functions can modify the vector's elements. 并且&copy[0] (在C ++ 11中可以写为copy.data() )保证产生一个指向内部使用的数据的指针,以便低级C函数可以修改向量的元素。

(I've also taken the chance to remove the Microsoft-style Hungarian Notation. It's a failed experiment from the 90s, and you've even used it incorrectly, supposing that a leading i is supposed to indicate an int .) (我还借此机会删除了Microsoft风格的匈牙利表示法。这是90年代的一次失败的实验,您甚至错误地使用了它,并假设前导i应该表示一个int 。)


The bottom line is really: 最重要的是:

If you need a const_cast anywhere in your code to make it compile, then somewhere else there is at least either one const missing or one too much . 如果在代码中的任何地方都需要const_cast进行编译,则在其他地方至少要缺少一个const太多 A const_cast always makes up for a mistake in another piece of code. const_cast总是可以弥补另一段代码中的错误。 It is always a workaround and never a solution designed up front. 这始终是一种解决方法,绝不是预先设计的解决方案。

Well I have solved the issue by creating the heap memory. 好了,我已经通过创建堆内存解决了这个问题。

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

You have to make a difference between a const qualified type and a const qualified object . 您必须在const限定类型const限定对象之间进行区分。

The standard states in section 7.1.6.1: cv-qualifiers : (cv = const or volatile) 7.1.6.1节中的标准状态:cv-qualifiers :(cv = const或volatile)

A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; 指向cv限定类型的指针或引用实际上不必指向或引用cv限定对象,但是将其视为指向或引用。 a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. 即使引用的对象是非const对象,也不能使用const限定的访问路径来修改对象,并且可以通过其他一些访问路径对其进行修改。

If your pointer points to a non const object, the casting away will enable you to modifiy the objet, but as someone told, you are lying to the user of your function. 如果您的指针指向一个非const对象,则抛弃将使您能够修改对象,但是正如有人告诉您的那样,您对函数的用户说谎。

It your pointer points to a real const object (ie in const protected memory), the compiler will compile your code, but you might have a segmentation fault, typical for undefined behaviour. 如果您的指针指向一个真正的const对象(即在const受保护的内存中),则编译器将编译您的代码,但是您可能会遇到分段错误,这通常是针对未定义行为的。

Here an example, using the fact that "Ordinary string literal (...) has type “array of n const char”, where n is the size of the string (...)" (see standard, section 2.14.5): 这是一个使用“普通字符串文字(...)具有类型为“ 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 !! 

So if your function ProcessData() is legacy code that is only reading the data but has forgotten to mention a const in the parameter list, your cast-away will work. 因此,如果您的函数ProcessData()是仅读取数据但忘记在参数列表中提及const的旧版代码,则您的转换将起作用。 If your function is however altering the data, it might work or it might fail, depending how the data poitned to was created ! 但是,如果您的函数正在更改数据,则它可能起作用或失败,具体取决于创建数据的方式!

So try to avoid casting const away if you are not 100% sure of what the effects will be ! 因此,如果您不确定100%会产生什么影响,请尝试避免抛弃const! Better clone your object the hard way creating a temporary object and copying the content. 更好的克隆对象的方法是创建临时对象并复制内容。

I propose you a small template to handle these kind of issues easily: 我建议您使用一个小模板轻松处理此类问题:

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
};

You may use your existing code almost as is: 您几乎可以按原样使用现有代码:

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;

The buffer will be automatically released once pDataToStore is no longer in scope. 一旦pDataToStore不在范围内,缓冲区将自动释放。

If you have similar issues with wchar_t buffers or anything else, it will work as well. 如果您对wchar_t缓冲区或其他任何东西有类似的问题,它也将正常工作。

For explanations on the evil of casting away const, see my other answer 有关放弃const的弊端的解释,请参阅我的其他答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM