传递参数并赋予它们值会导致运行时错误

时间:2012-02-01 06:14:16

标签: c++ segmentation-fault runtime-error

所以我有以下代码:

void emptyString(char* token, int size) {
    for (int i=0; i < size; i++) token[i] = '\0';
}

    void setDefaults(char uloginName[], char *home_directory, char *password, char *shell, char *gecos) {
        emptyString(home_directory, sizeof(home_directory) - 1);
        strcat(home_directory, "home/home/");
        strcat(home_directory, uloginName);
    }

    int main(){
        char buffer[256];
        AccountInfo *tempAccount;
        UserDB users;
        char uloginName[9] = "Smith";
        char homeDirectory[33];
        int userID;
        int groupID;
        char password[17];
        char shell[17];
        char gecos[65];
        int i, j, k;
        char flag[3];
        char IDString[6];
        char command[11];
        char blank = ' ';

    setDefaults(uloginName, homeDirectory, password, shell, gecos);
    return 0;
    }

当我在Visual Studio中运行此代码时,我会弹出一个窗口

  

运行时检查失败#2 - 围绕变量'users'堆栈   损坏

这是由setDefaults()方法中的代码行引起的。我希望有人帮助我纠正代码。我的目的是将home_directory的值设置为“ home / home / userloginname ”,其中userloginname也是传递的变量之一。

编辑:我刚刚发现我的代码中的问题是strcat()方法。如果我使用strcpy()作为第一个,它可以正常工作,但是一旦我想添加更多,它会向我显示一个弹出窗口,说明变量users已损坏。以下是userDB类的代码:

class UserDB
{
private:
    AccountInfo* _accounts[200]; // store up to 200 accounts
    unsigned int _size; // number of account stored
    unsigned int _nextUid; // next user id to be assigned
    unsigned int _defaultGid; // default group id
    // other private methods necessary for this class
public:
    // constructors(empty), destructor
    void adduser( AccountInfo* newUser);
    void    showUsers();
    void    showPasswd();
    void    finger(char* userLoginName);class
    int size(); // return the number of accounts stored (_size)
    // and other public methods (mutators/setters, accessors/getters)
};

现在,代码就像在这里一样。也许我需要制作构造函数或析构函数或诸如此类的东西,但作为一个新手,无法知道究竟是什么导致了错误并需要修复。

编辑:所以很长一段时间后我发现strcat()是导致错误的原因,但是我需要做strcat声称要做的事情。这是我的完整代码http://ideone.com/A5iWh的副本,以便有兴趣的人可以让我知道要改变什么,因为我不知道还能做什么。感谢。

4 个答案:

答案 0 :(得分:1)

emptyString(home_directory, sizeof(home_directory) - 1);

你这里有问题:

sizeof(home_directory)

此语句为您指定指针home_directory的大小,而不是数组 如果你想在函数内部使用数组的大小,那么你必须将它作为函数参数显式传递。

此外,您的变量未初始化,导致未定义的行为 具体来说,在显示的代码段中uloginName 您需要初始化所有变量,以免它们在整个项目中引起问题。

答案 1 :(得分:1)

值得注意的问题:

// in getNextToken
while ((buffer[i] != delimeter) && (i < 256) && (j < tokenSize))
    token[j++] = buffer[i++];

除了长度检查之外,没有检查您是否已到达buffer[]内容的末尾。如果cin.getline(buffer)读取“用户名p mypasswd”怎么办?然后:

  • 在第150行,复制到command填满command但从不在结尾处放置终止空值。也许这不是问题......
  • 在第153行,读入uloginName也不会插入终止空字符。
  • 在第170行,读入password读取16个字节。如果密码足够短,也许你会幸运地使用null终止符。

  • 答案 2 :(得分:0)

    strcat(home_directory, uloginName);
                           ^^^^^^^^^^ uninitialized
    

    原因是char uloginName[9];未在main()内初始化,而setDefaults(...)正在传递给uloginName,因此 保证 nul终止字符串。

    缓冲区可能超出范围,代码进入未定义的行为

    更新:从您posted in the link的代码中,它似乎一团糟。您没有像我之前所说的那样初始化字符数组。此外,我没有看到任何范围检查,因此缓冲区可能很容易超载。

    例如,9的大小只有kstd::string的变量值很可能超过{{1}}。这会导致堆栈溢出,并且会破坏在其附近声明的相邻变量的值。如果您对绑定检查不满意,请尝试使用{{1}}而不是裸阵列。

    答案 3 :(得分:0)

    您的错误消息表示&#34;堆栈溢出。&#34;你的程序堆栈中有一些固定大小的缓冲区,而且你写入的内容比缓冲区可以容纳的多。

    无论何时在C或C ++中声明局部变量,都会在运行函数的堆栈中为其分配一些存储空间。让我举个例子。

    #include <stdio.h>
    
    int main()
    {
        short above=0;
        char buffer[1];
        short below=0;
    
        strcpy(buffer, "Hello,");
        strcat(buffer, " world!");
    
        printf("%d %d %s", above, below, buffer);
    }
    

    所以我宣布buffer有足够的空间来存储1个字节。我尝试将"Hello, world!\0"的14个字节放入其中。我运行这个程序后会得到什么?

    27762 0 Hello, world!
    

    现在,我设置了以上&#39;显式为0。它是怎么变成27762的?好吧,这里有一个实际发生的例子:

        Stack:
    |        ....         |                              |   ...  |
    | misc platform stuff |                              | d!\0...|
    |---------------------|                              |--------|
    | storage for 'above' | 2 bytes = sizeof(short)      |   rl   |
    |---------------------|                              |--------|
    | padding/cookie      | system dependent             |ello, wo|
    |---------------------|                              |--------|
    | storage for 'buffer'| 1 byte = sizeof(buffer)  ->  |   H    | 
    |---------------------|                              |--------|
    | padding/cookie      | system dependent             |  ? ? ? |
    |---------------------|                              |--------|
    | storage for 'below' | 2 bytes = sizeof(short)      |  0  0  |
    |      ...            |
    

    当我写入buffer时,第一个字节落在它应该是的位置。然后接下来的几个字节开始写入&#39; padding / cookie&#39;区域。最终该区域溢出,我们继续覆盖下一个更高的东西,&#39;以上&#39;。

    快速检查向我们显示27762是我的机器在世界中间解释字符rl的方式!&#34;作为short

    现在,像这样的错误是堆栈缓冲区溢出,或者#34;堆栈溢出&#34;。恶意用户可以利用堆栈溢出的某些实例来控制您的程序,并使其完全不同于它的设计目的。利用堆栈溢出是一种经过验证的真正的黑客技术。

    以至于编译器编写者和操作系统设计者已经采取了“深度防御”的策略。防止堆栈溢出被利用。理想情况下,如果某个用户设法覆盖堆栈的坏部分,工具链会尝试做唯一合理的事情:立即崩溃。

    在这种特殊情况下,编译器所做的是在存储“缓冲区”后立即插入一个特殊的难以猜测的值。称为堆栈cookie。&#39;当它离开main时,它会检查&#39;堆栈Cookie&#39;没有改变。如果有的话,一些顽皮的东西(可能是堆栈溢出)已经发生了,所以程序会立即崩溃并显示你看到的对话框。

    我们如何修复堆栈溢出?代码防守。 从不致电strcpystrcatsprintf或他们的各个朋友。不使用固定大小的缓冲区(例如char homeDirectory[33];),而是使用std::string homeDirectory;等动态工具来管理字符串。如果使用得当,就不可能以覆盖意外内存的方式溢出std::string