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]:意外"buffer overflows"是C程序中很常见的问题,尤其是在处理字符串的时候。这是新手 C 程序员最常犯的错误之一。请参阅下面的示例。许多病毒利用此类错误来制作一个特殊的字符串来强制进行非常小心控制的溢出,从而导致函数 return 地址(也存储在堆栈中)被覆盖。当函数 returns 而不是 returning 到调用函数时,它 returns 到攻击者精心选择的某个地址。这允许攻击者执行她选择的一些代码。当我说很大一部分病毒以这种方式工作时,我认为我并没有夸大其词。所以,要小心并密切注意你的内存管理!!!有关详细信息,请参阅 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保留的内存位置。