构造函数对 const C 字符串数组的帮助

Posted

技术标签:

【中文标题】构造函数对 const C 字符串数组的帮助【英文标题】:Constructor help with const C-string array 【发布时间】:2010-09-30 05:18:18 【问题描述】:

我有一个简单的类名:

   class Name
 private:
    char nameStr[30];
  public:
    Name(const char * = "");
    char * getName() const;
    void print() const;
;

我有两个问题,这对我来说有点困难。构造函数应该采用 const char * 的参数并将其复制到 nameStr 数组中。但是,这是我尝试过但不起作用的方法:

Name::Name(const char * setName)

    &nameStr = setName;

它给了我错误“赋值中的左值无效”。如果我删除 nameStr 前面的引用,它仍然会出错,说“将 'const char*' 分配给 'char[30]' 时类型不兼容”。不太清楚我错过了什么。

另一个问题是访问器方法:

char * Name::getName() const

    return  &nameStr[0];

我有同样的问题,它错误“从 'const char*' 到 char*' 的无效转换”,我无法终生弄清楚如何让它返回一个指向 const char 的指针。 . 我一直在寻找有用的。在 const 上找到了大量信息,但不太清楚如何将其应用于我的情况。任何帮助将不胜感激!

【问题讨论】:

相关,你应该问问自己为什么使用 char[30] 而不是 std::string。如果没有出现重要原因,则其他问题自动解决。 我也强烈推荐使用 std::string 除非有充分的理由不这样做。处理 std::string 对象会容易得多。 嘿,感谢关于 std::string 的提示。它是作业的一部分,因此必须使用 C 风格的字符串。我应该在问题中注明。 【参考方案1】:

线

char nameStr[ 30 ];

相对于(例如)

char * nameStr;

建议您要保留整个字符串的“副本”,而不仅仅是指针。所以,

&nameStr = setName;

没有意义。您可以使用类似的东西(但可能不完全一样)

strncpy( nameStr, setName, 30 );

然而,关于第二个问题,为什么要让getName() 方法返回char *?我看到 Name 类正在封装一个 char 数组,但是如果你暴露了 char 指针,你就破坏了你自己的封装。如果您真的需要,您可以通过return nameStr; 返回一个const char *,但最好避免这种情况。

【讨论】:

memcpy 对于这样的事情有点不安全,如果你真的想使用它,memcpy(nameStr, setName, 30*sizeof(char));会更合适。 @KSchimdt:完全同意。我只是继续并专注于这个问题,没有太大变化。实际上,我可能会使用enum N = 30 ; char nameStr[ N ];,或者甚至使用std::string(如果有)。 (在工作中,我没有std :-9) sizeof(char) == 1 根据定义。 这更像是一种风格/一致性建议 :) 是的,我应该确保不暴露 char 数组。感觉就像我应该每隔一个单词输入 const 就可以了。我将返回类型更改为“const char *”,这就是我想要完成的。谢谢!【参考方案2】:

我建议你使用 std::string,但首先你需要知道指针是如何工作的。

我们来解决你的第一个问题:&nameStr = setName;

这里,nameStr 是一个字符数组,用于存储您的姓名。 &nameStr 指的是这个数组的地址。您不能设置任何类型的变量的地址,这是非法的。例如,&a = &b(其中 a 和 b 是整数)同样非法。接下来你尝试了nameStr = setName;,它说来自const char* to char[30] 的转换不兼容。您要做的是将一个指针分配给另一个指针。这实际上并没有复制数据。要复制数据,您需要手动执行此操作,例如:

for (int n=0;(n>0?setName[n-1]!=0:true);n++)  nameStr[n] = setName[n]; 
// The above won't work (or might crash) if the ternary operator evaluates both sides. I don't think it does, but correct me if I'm wrong.

请注意,与 setName[n-1] 而不是 setName[n] 的比较是为了复制 NULL 字符。

对于您的下一个问题,cannot convert const char* to char*。获取变量的地址总是会产生一个常量指针,因为您不允许更改地址。另请注意,&array[0]array 基本相同,因为您先取消引用,然后重新引用它,从而使该过程变得不必要。您需要将您的 getName() 函数声明为返回 const char* 并在其中返回 nameStr

关于数组的一句话:数组和其他数据类型一样,只是它们包含多个数据类型,而不是一个。引用数组时,其类型是指向其成员类型的指针。例如,整数数组将被视为int*。指针指向它的第一个元素。这意味着在数组上使用 [] 将取消引用指向其任何元素的指针。

祝 C++ 好运!

【讨论】:

太棒了,帮助很大,谢谢!我刚刚开始学习 C++,并且对指针有所了解,但还没有完全掌握。感谢您提供有关数组和被尊重的提示。 哦,我多么希望我可以使用 std::string,但这是作业的一部分,我有一些限制。虽然我认为这是接受挑战的好方法。 第一次运行循环时,您将在 setName 范围之外进行索引,这不是一个好主意...(当 n=0 变为 setName[-1] 时,setName[n-1]) “取消引用变量总是会产生一个常量指针”这不准确。他得到一个 const 指针的原因是因为他在一个类中声明了一个静态大小的数组。指针必须始终指向该类中的数据(30 个字符在该类中并影响其大小。)但话又说回来,也许您的意思是“获取地址”而不是“取消引用”。并 +1 以获得详细的答案。 是的,我做到了……小错误。我会改正错误。哦,很高兴你发现这很有帮助。【参考方案3】:

当您在类或结构中声明 char nameStr[30] 之类的内容时,它会在类的内存中创建 30 个连续字符。 nameStr 的值是指向类中该确切位置的常量指针,因此您无法更改它。你必须使用类似的东西

strncpy(nanemStr, setName, 30).

这会复制字符串的内容并添加一个空终止符。它限制为 30 个字符,因此您不会溢出缓冲区并破坏整个宇宙。

【讨论】:

毁灭宇宙?缓冲区溢出在最坏的情况下会导致运行时错误。 请注意,如果要复制的字符串长度至少为 30 个字符,则 strncpy 不会以空值结尾。如果 OP 使用的是 Visual Studio,他可以使用 strcpy_s,如果不是,则可以轻松地将 strcpy 与他自己的 strcpy_s 包装起来。 @Alex:超出数组范围的写入是未定义的行为,这意味着 任何事情 都可能发生(如果计算机能够以某种方式做到这一点,则包括对宇宙的破坏) . 那太糟糕了...不过我认为我的电脑做不到。 从程序的角度考虑。未处理的异常就像炸死一样。 (如果调试器得到你,它会让你陷入困境!)【参考方案4】:

如果您愿意使用 std::string,您可以执行以下操作:

#include <string>

class Name
 private:
    std::string nameStr;
  public:
    Name(std::string = "");
    std::string getName() const;
    void print() const;
;

Name::Name(std::string setName)

    nameStr = setName;


std::string Name::getName() const

    return nameStr;

【讨论】:

setter 应该采用 std::string 引用,因此它不会创建不必要的副本。 谢谢,但这是作业的一部分,我们使用的是 C 风格的字符串。我多么希望我可以使用 std::string。 @ross,好的。我只是想无论如何我都会把这个建议放在那里。 @KSchmidt,说得好。【参考方案5】:

为了澄清错误消息的含义,语句&amp;nameStr = setName; 字面意思是“将nameStr 的地址设置为存储在变量setName 中的值”。你永远不能改变变量地址的值;这是在定义变量时分配的常量。因此,&amp;nameStr 正如编译器所说,是一个无效的左值。

【讨论】:

【参考方案6】:

第一个问题 -

However, this is what I have tried and is not working:

Name::Name(const char * setName)

   &nameStr = setName;


It gives me the error "invalid lvalue in assignment".

因为&amp;nameStr给你nameStr的地址,你不能给它赋值,所以不能在任何赋值语句中用作左值。

If I remove the reference in front of nameStr, it still errors, saying 
"incompatible types in assignment of 'const char*' to 'char[30]'"

因为nameStr 是一个字符数组,而 C++ 中的数组名称是指向其第一个元素的不可变指针。这意味着您无法更改它指向的位置。

因此,也许你应该尝试这样的事情 -

strcpy(nameStr, setName);

第二个问题 -

char * Name::getName() const

    return  &nameStr[0];

同样的道理——&amp;nameStr[0] 仍然等同于写简单的nameStr。 尝试更改您的函数签名,例如 -

const char * Name::getName() const

【讨论】:

以上是关于构造函数对 const C 字符串数组的帮助的主要内容,如果未能解决你的问题,请参考以下文章

字符串类中的字符串(const char *)构造函数内存泄漏

将 Swift 字符串数组转换为 const char * const *

在类构造函数内初始化 Dev C 中的字符串数组

处理产生奇怪结果的字节数组的字符串构造函数[重复]

字符函数和字符串函数

在 C# String 构造函数 String(Char*) 中,为啥构造函数不期望指向字符数组的指针?