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

标签 c arrays pointers 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;
}

关于C:声明一个指针然后将其用作数组,内存分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59184633/

相关文章:

c - 当我在 C 中更改目录时出现段错误

C 对称流密码

c - 为什么灵活数组成员必须位于结构的末尾,但具有灵活数组的结构则不然?

c - C语言中文件指针指向什么?

c - C中的数组是通过指针使用的吗?

C++ 对象实例在被销毁后保留在列表中

c - 如何知道数组中有多少个模式段?

arrays - 将对象添加到 [String : [[String]]] dictionary

java - AWT-EventQueue-0"java.lang.ArrayIndexOutOfBoundsException

java - 我怎样才能对 main 有多个定义?