Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
199 views
in Technique[技术] by (71.8m points)

c++ - Swift String from C String has correct length but incorrect contents

I'm having a similar issue to this fellow, but the problem is not the same cause as in that answer.

I'm getting a const char * from a C function. This function is actually implemented in C++ and it returns a struct by value which contains the return value of std::string c_str() which is a C pointer to the string contents. This string lives in static memory. Since the pointer is copied into the return, I would expect the data to be fine.

According to Xcode's debugger, the string I get back is actually the correct length as I'd expect (see countAndFlags below with correct character count of 7), but the contents are garbage. This is in a UITableView method, and what's very strange is that the garbage problem disappears if I use the reloadData() TableView method (which I've wired to a button) which then has valid content for the name variable below. However this subsequent invocation of reloadData() does not alter any of the data used elsewhere in the table, only this problematic string garbage goes away.

enter image description here

Here is the debugger on first pass, showing garbage but correct length:

enter image description here

And here is the next pass when the text is correct:

enter image description here

I'm at a loss to understand this behaviour. The struct s contains others properties that are not corrupted when they are returned, but this char * is garbage on first pass only, with correct length nonetheless.

I'd appreciate any pointers (no pun intended) on what is happening here.

Finally, here is the C++ code for the function returning the C string:

scene getScene(sceneID s){
  static std::map<sceneID, std::vector<nodeID> > nodeArrays;
  auto ss = globalObjects.scenes.at(s);
  auto sceneroots = ss.roots;
  if (nodeArrays.count(s)>0) nodeArrays.at(s).clear();
  else confirmInsertion(nodeArrays.emplace(s, std::vector<nodeID>{}));
  auto v = nodeArrays.at(s);
  v.reserve(sceneroots.size());
  for (const auto&i:sceneroots) v.push_back(i);
  scene sr;
  sr.name=ss.name.c_str();
  sr.roots=vec2array(v);
  return sr;
}

If I log sr.name it is always correct.

Here is scene:

  typedef struct {
    const char* name;
    arrayID roots;
  } scene;
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One issue you should observe is this within the C++ function:

Given that this is your scene definition:

typedef struct {
    const char* name;
    arrayID roots;
  } scene;

Inside the getScene C++ function, you're doing this:

auto ss = globalObjects.scenes.at(s);

Then you do this later:

  scene sr;
  sr.name = ss.name.c_str();
  //...
  return sr;

The issue is that since ss is a local variable, the ss.name.c_str() pointer assigned to sr.name will not be valid when the function returns. Accessing this pointer leads to undefined behavior.

One fix for the problem is to copy the contents of the string to a buffer that is sent by the client program, or copy the contents to a buffer you know is available by the client and won't be invalidated. Since I do not know Swift, you will have to negotiate how to accomplish this.


Another solution would be to make sure you are accessing the same std::string, not a copy of the std::string:

auto& ss = globalObjects.scenes.at(s);

Now a reference to the scene is returned. But then you have the issue of making sure that the string within the scene is not mutated at all when you actually refer to the returned c_str() value later on in your application. A C++ programmer would not hold onto the return value of c_str() for an extended time, since this can lead to hard-to-diagnose bugs.


The bottom line is that always be suspicious of returning a character pointer as a means of returning string data. The usual way that an API returns string data is to have the client supply a buffer and the API fills the buffer with the string data, or less likely, the API allocates memory and returns a pointer to the allocated memory (which leads to complications as the API has to free the memory by some means to avoid memory leaks).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...