繁体   English   中英

C:声明一个指针然后作为数组使用,内存分配

[英]C: Declaring a pointer and then use it as an array, memory allocation

在讨论指针和数组时,解释通常会说明初始化数组然后初始化指向同一内存位置的指针确实使您能够以与第一个数组相同的方式使用该指针:

int myIntArray[3] = {5, 6, 7};
int* ptr = myIntArray; // ptr[2] = myIntArray[2] = 7

我知道这是因为堆栈上的内存分配是通过第一次初始化int myIntArray[3] 但是如果创建一个指针,它不会为数组分配潜在的内存。

因此,我的问题是,创建一个指针并将其直接用作数组是否安全,或者是否可以覆盖其他使用的内存等?

int* ptr;
*ptr = 5;
*(ptr+1) = 6;
*(ptr+2) = 7;

我的猜测是,如果地址(ptr+2)将包含一些先前分配的内存,例如较早的初始化变量,则计算机会将ptr(ptr+1)重新分配到某个地方,因为(ptr+2)未被使用前。

不,你的例子不安全。

您的ptr变量未显式初始化。

如果ptr是局部变量,它将被初始化为未定义(即随机)值。 因此, *ptr = 5会将值 5 写入随机内存位置。 这将导致内存损坏或(更有可能)分段错误。 请参阅以下示例:

#include <stdio.h>

int main(void)
{
    int *ptr;    /* Local variables are not intitialized and contain a random value */
    printf("ptr contains address %p\n", ptr);   /* Prints a random value */
    ptr[0] = 123;  /* BAD: On macOS causes memory corruption */
    return 0;
}

另请参阅下面的注释 [1]。

如果ptr是一个全局变量,它将被初始化为零。 这个*ptr = 5会将值 5 写入虚拟地址 0,这很可能会导致分段错误。

#include <stdio.h>

int *ptr;  /* Global variables are initialized to zero */

int main(void)
{
    printf("ptr contains address %p\n", ptr);  /* Prints 0x0 */
    ptr[0] = 123;  /* BAD: On macOS causes "Segmentation fault: 11" */
    return 0;
}

静态变量也是如此:

#include <stdio.h>

int main(void)
{
    static int *ptr;    /* Static variable are initialized to zero */
    printf("ptr contains address %p\n", ptr);   /* Prints 0x0 */
    ptr[0] = 123;  /* BAD: On macOS causes "Segmentation fault: 11" */
    return 0;
}

如果要使用不指向数组的指针,则应显式分配内存,例如通过调用malloc

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *ptr;    /* Local variables are not intitialized and contain a random value */

/* Explicitly allocate enough memory to store one int value */
    ptr = malloc(sizeof(int));
    if (!ptr) {            
        fprintf(stderr, "Out of memory");
        exit(1);
    }
    printf("ptr contains address %p\n", ptr);   /* Prints a random value "on the heap" */

    *ptr = 111;  /* OK */
    printf("*ptr contains value %d\n", *ptr);   /* OK, 111 */
    printf("ptr[0] contains value %d\n", ptr[0]);   /* OK, 111 */

    ptr[0] = 222;  /* OK */
    printf("*ptr contains value %d\n", *ptr);   /* OK, 222 */
    printf("ptr[0] contains value %d\n", ptr[0]);   /* OK, 222 */

    /* Must explicitly free memory after you are done with it, otherwise you have a memory leak */
    free(ptr);

    return 0;
}

如果您使指针指向 int,则使用*pp[0] (本质上是相同的)读取或写入解除引用的指针是安全的,但p[1]并不安全:

#include <stdio.h>

int main(void)
{
    int foo = 111;
    int bar = 222;
    int *ptr = &bar;

    printf("Address of ptr %p\n", &ptr);   /* An address on the stack */
                                        /* Some random value */
                                        /* Note: this is the address OF the ptr variable, */
                                        /*       and not the address stored IN ptr */

    printf("Address of bar %p\n", &bar);   /* An address on the stack */
                                        /* Exact difference with ptr depends on compiler etc. */
                                        /* On macOS: 12 bytes after address of ptr */

    printf("Address of foo %p\n", &foo);   /* An address on the stack */
                                        /* Exact difference with bar depends on compiler etc. */
                                        /* On macOS: 4 bytes after address of bar */

    printf("foo contains value %d\n", foo);   /* 111 */
    printf("bar contains value %d\n", bar);   /* 222 */
    printf("ptr contains address %p\n", ptr);   /* The same as address of bar above */

    ptr[0] = 333;  /* Perfectly safe, changes the value of bar */

    printf("bar contains value %d\n", bar);  /* 333 */

    ptr[1] = 444;  /* BAD technically the behavior is undefined */
                /* But most likely will change the value of foo */
                /* Because most likely foo is stored after bar on the stack */

    printf("foo contains value %d\n", foo);  /* On macOS: 444 */

    return 0;
}

注意 [1]:意外“缓冲区溢出”是 C 程序中非常常见的问题,尤其是在处理字符串时。 这是新手 C 程序员最常见的错误之一。 请参阅下面的示例。 许多病毒利用这样的错误来制作一个特殊的字符串来强制非常小心地控制溢出,从而导致函数返回地址(也存储在堆栈中)被覆盖。 当函数返回时,它不是返回调用函数,而是返回到攻击者精心选择的某个地址。 这允许攻击者执行她选择的一些代码。 当我说很大一部分病毒以这种方式工作时,我想我并没有夸大其词。 所以,要小心并非常注意你的内存管理!!! 有关更多详细信息,请参阅https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/BufferOverflows.html

#include <stdio.h>
#include <string.h>

int main(void)
{
    int foo = 111;
    char str[5];

    printf("foo contains value %d\n", foo);   /* 111 */

    strcpy(str, "12345678");  /* BAD: Overflow! Writing 9 bytes into a 5 byte string */
                            /* (9 not 8, because of the terminating 0 byte ) */
                            /* Modern operating systems / compilers / processors catch this */
                            /* On my macOS, I get "Abort trap 6" */

    /* Technically, behavior is undefined */
    /* In practice, foo is overwritten */

    printf("foo contains value %d\n", foo);   /* With older operating systems / compilers / */
                                            /* processors this would show a changed value */



    return 0;
}

当您声明一个局部变量时,它会使用堆栈中的随机值进行初始化。 所以当你说int *x ; int*值本质上是随机的。 在这种状态下使用指针的问题是你不知道它指向哪里。 它是有效的记忆吗? 它是否指向您不想覆盖的内存?

如果您希望它可靠地工作,您必须在使用它之前用某个值初始化一个指针。 这可以是堆栈上的数组或 malloc 保留的内存位置。

暂无
暂无

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

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