如果存储字符串的char数组小于字符串,C ++真的存储字符串?

时间:2012-07-12 15:31:11

标签: c++ arrays string char strlen

我正在从“C ++ Premiere”一书中测试一个关于C ++中字符串的例子。

const int size = 9;
char name1[size];
char name2[size] = "C++owboy";   // 8 characters here

cout << "Howdy! I'm " << name2 << "! What's your name?" << endl;

cin >> name1;  // I input "Qwertyuiop" - 11 chars. It is more than the size of name1 array;

// now I do cout
cout << "Well, your name has " << strlen(name1) << " letters";  // "Your name has 11 letters".
cout << " and is stored in an array of " << size(name1) << " bytes"; // ...stored in an array of 9 bytes.

如何将11个字符存储在一个数组中,仅用于8个字符+'\ 0'char?它在编译时变宽了吗?或者字符串存储在其他地方?

另外,我不能这样做:

const int size = 9;
char name2[size] = "C++owboy_12345";   // assign 14 characters to 9 chars array

但可以做我上面写的:

cin >> name1;   // any length string into an array of smaller size

这里的诀窍是什么?我使用NetBeans和Cygwin g ++编译器。

4 个答案:

答案 0 :(得分:9)

将更多条目写入数组而不是数组大小允许调用未定义行为。计算机可能会将数据存储在任何位置,或者根本不存储它。

通常,数据存储在内存中接下来发生的任何事情中。这可能是另一个变量,一个指令流,甚至是你椅子下方炸弹的控制寄存器。

简单地说:你已编写了一个缓冲区溢出错误。不要那样做。

<小时/> 只是为了好玩未定义的行为是C ++标准不评论的行为。它可以是任何东西,因为标准没有对它施加任何限制。

在一个特定情况下,该行为将我的银行余额从10美元增加到18亿美元:http://ideone.com/35FQW

你能看出为什么那个程序可能会这样吗?

答案 1 :(得分:5)

name1在内存中被赋予一个地址。如果你写80个字节,它将从该位置开始在内存中写入超过80个字节。如果存在一个存储在name1的地址+20的变量,那么它将通过向name1写入80个字节来覆盖其数据。这就是C / C ++中的工作方式,这些被称为缓冲区溢出,可用于破解程序。

答案 2 :(得分:3)

这里没有技巧:)你正在缓冲区之外写入内存,这是一个未定义的bahaviour

答案 3 :(得分:3)

这是典型的缓冲区溢出。这就是为什么你总是应该检查输入的大小,如果你把它放在缓冲区。以下是发生的事情:

在C ++(和C)中,数组名称只是指向数组第一个元素的指针。编译器知道数组的大小,并将进行一些编译时检查。但是,在运行时,它只会将其视为char *。

当您执行cin >> name1时,您将char *传递给cincin不知道分配的空间有多大 - 所有它都是指向某个内存的指针。因此,它假设您分配了足够的空间,写入所有内容,并超过了数组的末尾。这是一张图片:

Bytes   1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
Before  |-----name1 array-------|  |--- other data-|
After   Q  w  e  r  t  y  u  i  o  p  \0 |-er data-|

如您所见,您已覆盖在阵列之后存储的其他数据。有时这个其他数据只是垃圾,但有时它很重要,可能意味着一个棘手的错误。更不用说,这是一个安全漏洞,因为攻击者可以用用户输入覆盖程序内存。

关于大小的混淆是因为strlen将计算字节,直到找到'\0'(空终止符),这意味着它找到10个字符。另一方面,size(name1)使用编译器提供的数组的实际大小。

由于这些问题,每当您看到一个以数组作为参数的C函数时,它也会占用数组大小​​。否则无法分辨出它有多大。为了避免这些问题,使用像std :: string这样的C ++对象要好得多。